feat:更改页面
This commit is contained in:
1 parent
4e804a17cd
commit
8995bf89d6
174 files changed
+11342
-220
No files matched your search
@@ -61,3 +61,10 @@ gen_code
|
||||
|
||||
### node ###
|
||||
node_modules
|
||||
|
||||
### AI 辅助工具目录 ###
|
||||
.v2c/
|
||||
.video_agent/
|
||||
.mimosa/
|
||||
.cloudbase/
|
||||
.zcode/
|
||||
@@ -0,0 +1,44 @@
|
||||
-- 订单识别证据图片表(迁移自 hq-platform 20260823_add_hard_order_evidence_image.sql)。
|
||||
-- 本表独立于 hard_order,退款、撤销和订单逻辑删除均不自动清理证据文件或证据记录。
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_order_evidence_image`
|
||||
(
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '证据记录自增主键',
|
||||
`order_id` bigint(20) NOT NULL COMMENT '订单主键,来源hard_order.id',
|
||||
`android_order_code` varchar(128) NOT NULL COMMENT '终端本地稳定订单号快照',
|
||||
`evidence_type` varchar(32) NOT NULL COMMENT '证据类型,FACE_RECOGNITION或MENU_RECOGNITION',
|
||||
`evidence_role` varchar(32) NOT NULL COMMENT '图片角色,FACE_REFERENCE、FACE_LIVE或MENU_LIVE',
|
||||
`sequence_no` int(11) NOT NULL DEFAULT 0 COMMENT '同类型同角色图片序号,从0开始',
|
||||
`user_id` bigint(20) DEFAULT NULL COMMENT '识别用户主键快照',
|
||||
`face_sample_id` varchar(64) DEFAULT NULL COMMENT '虹软人脸样本编号快照',
|
||||
`reference_image_url` varchar(512) DEFAULT NULL COMMENT '识别时后台参考人脸来源地址快照,不作为展示地址',
|
||||
`similarity` decimal(8, 6) DEFAULT NULL COMMENT '识别相似度快照,取值0至1',
|
||||
`similarity_threshold` decimal(8, 6) DEFAULT NULL COMMENT '终端生效的相似度阈值快照,取值0至1',
|
||||
`hard_id` bigint(20) NOT NULL COMMENT '产生证据的设备主键',
|
||||
`captured_at` datetime DEFAULT NULL COMMENT '终端实际采集时间',
|
||||
`sys_file_id` bigint(20) DEFAULT NULL COMMENT '文件中心sys_file记录主键',
|
||||
`storage_file_name` varchar(255) DEFAULT NULL COMMENT '文件中心对象名',
|
||||
`image_url` varchar(512) DEFAULT NULL COMMENT '订单证据图片访问地址',
|
||||
`image_hash` char(64) DEFAULT NULL COMMENT '实际文件SHA-256哈希',
|
||||
`image_size` bigint(20) DEFAULT NULL COMMENT '实际文件大小,单位字节',
|
||||
`image_width` int(11) DEFAULT NULL COMMENT '实际图片宽度,单位像素',
|
||||
`image_height` int(11) DEFAULT NULL COMMENT '实际图片高度,单位像素',
|
||||
`status` varchar(32) NOT NULL COMMENT '证据状态,PENDING、AVAILABLE或UNAVAILABLE',
|
||||
`failure_reason` varchar(500) DEFAULT NULL COMMENT '永久缺失或复制失败摘要,不保存终端完整路径',
|
||||
`app_version` varchar(64) DEFAULT NULL COMMENT '产生证据的终端应用版本',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`del_flag` char(1) DEFAULT '0' COMMENT '逻辑删除标记,0有效,1删除',
|
||||
`tenant_id` bigint(20) NOT NULL COMMENT '租户主键',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_hard_order_evidence_position`
|
||||
(`tenant_id`, `order_id`, `evidence_type`, `evidence_role`, `sequence_no`, `del_flag`),
|
||||
KEY `idx_hard_order_evidence_android_order`
|
||||
(`tenant_id`, `hard_id`, `android_order_code`, `del_flag`),
|
||||
KEY `idx_hard_order_evidence_status`
|
||||
(`tenant_id`, `status`, `update_time`, `del_flag`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci
|
||||
COMMENT = '硬件订单识别证据图片';
|
||||
@@ -0,0 +1,23 @@
|
||||
-- 硬件订单新增独立核销时间(迁移自 hq-platform 20260823_add_hard_order_verification_time.sql)。
|
||||
-- 历史二维码核销订单只能使用最后更新时间近似回填;外卖等非核销完成状态不参与回填。
|
||||
-- 新核销订单由业务事务写入准确时间,后续订单更新不再改变该值。
|
||||
|
||||
SET @verification_time_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_order'
|
||||
AND `column_name` = 'verification_time') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_order` ADD COLUMN `verification_time` datetime DEFAULT NULL COMMENT ''订单实际核销完成时间,后续订单更新不得覆盖'' AFTER `refund_status`, ADD KEY `idx_hard_order_verification_time` (`tenant_id`, `verification_time`, `del_flag`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE verification_time_stmt FROM @verification_time_ddl;
|
||||
EXECUTE verification_time_stmt;
|
||||
DEALLOCATE PREPARE verification_time_stmt;
|
||||
|
||||
UPDATE `pjcloudx_hardware`.`hard_order`
|
||||
SET `verification_time` = `update_time`
|
||||
WHERE ((`order_status` = '20' AND `order_type` IN ('1', '2', '3', '4'))
|
||||
OR `pay_type` IN ('5', '6', '50'))
|
||||
AND `verification_time` IS NULL
|
||||
AND `update_time` IS NOT NULL;
|
||||
|
||||
SET @verification_time_ddl = NULL;
|
||||
@@ -0,0 +1,206 @@
|
||||
-- 尔印独立打印终端、终端一对一绑定及持久化打印任务(迁移自 hq-platform 20260904_add_er_yin_printer.sql,仅保留 DDL)。
|
||||
-- 字典(hard_type 类型12、hard_print_type、hard_print_status)与按钮权限请按 db/20260908_权限菜单字典待添加.txt 手动添加。
|
||||
-- 每个结构变更独立判断,可在兼容数据库自动提交部分DDL后安全重跑整个脚本。
|
||||
|
||||
ALTER TABLE `pjcloudx_hardware`.`hard_invoice`
|
||||
MODIFY COLUMN `hard_id` bigint(20) NULL COMMENT '来源业务或核销终端id',
|
||||
MODIFY COLUMN `pic` varchar(512) NULL COMMENT '后端生成的打印PNG地址';
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'printer_hard_id') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `printer_hard_id` bigint(20) NULL COMMENT ''目标尔印打印终端id'' AFTER `hard_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'print_type') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `print_type` char(2) NOT NULL DEFAULT ''1'' COMMENT ''打印类型,1发票、2兑换券'' AFTER `order_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'print_no') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `print_no` int(11) NOT NULL DEFAULT 1 COMMENT ''同订单同类型打印序号'' AFTER `print_type`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'retry_version') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `retry_version` int(11) NOT NULL DEFAULT 0 COMMENT ''人工失败重试版本'' AFTER `print_no`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'attempt_count') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `attempt_count` int(11) NOT NULL DEFAULT 0 COMMENT ''终端USB累计尝试次数'' AFTER `retry_version`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'failure_stage') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `failure_stage` varchar(32) NULL COMMENT ''BINDING、IMAGE_GENERATION、MQTT_DISPATCH或TERMINAL_PRINT'' AFTER `status`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'last_error') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `last_error` varchar(2000) NULL COMMENT ''最近失败摘要'' AFTER `failure_stage`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'printed_at') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `printed_at` datetime NULL COMMENT ''终端确认完整USB数据和切纸命令写入成功时间'' AFTER `last_error`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `column_name` = 'reprint_reason') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD COLUMN `reprint_reason` varchar(500) NULL COMMENT ''人工重打或再次核销原因'' AFTER `printed_at`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
-- 唯一索引尚未建立才回填历史发票,避免迁移完成后重跑时覆盖新产生的兑换券任务。
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`statistics`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `index_name` = 'uk_hard_invoice_order_type_no') = 0,
|
||||
'UPDATE `pjcloudx_hardware`.`hard_invoice` invoice
|
||||
JOIN (
|
||||
SELECT ranked.`id`, ranked.`print_no`
|
||||
FROM (
|
||||
SELECT `id`, ROW_NUMBER() OVER (
|
||||
PARTITION BY `tenant_id`, `order_id`
|
||||
ORDER BY `create_time`, `id`
|
||||
) AS `print_no`
|
||||
FROM `pjcloudx_hardware`.`hard_invoice`
|
||||
) ranked
|
||||
) ranked_rows ON ranked_rows.`id` = invoice.`id`
|
||||
SET invoice.`print_type` = ''1'',
|
||||
invoice.`print_no` = ranked_rows.`print_no`,
|
||||
invoice.`retry_version` = 0,
|
||||
invoice.`attempt_count` = 0',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`statistics`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `index_name` = 'uk_hard_invoice_order_type_no') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD UNIQUE KEY `uk_hard_invoice_order_type_no` (`tenant_id`, `order_id`, `print_type`, `print_no`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`statistics`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `index_name` = 'idx_hard_invoice_printer_status') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD KEY `idx_hard_invoice_printer_status` (`tenant_id`, `printer_hard_id`, `status`, `update_time`, `del_flag`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
SET @er_yin_ddl = IF(
|
||||
(SELECT COUNT(*) FROM `information_schema`.`statistics`
|
||||
WHERE `table_schema` = 'pjcloudx_hardware' AND `table_name` = 'hard_invoice'
|
||||
AND `index_name` = 'idx_hard_invoice_source_status') = 0,
|
||||
'ALTER TABLE `pjcloudx_hardware`.`hard_invoice` ADD KEY `idx_hard_invoice_source_status` (`tenant_id`, `hard_id`, `status`, `del_flag`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE er_yin_stmt FROM @er_yin_ddl;
|
||||
EXECUTE er_yin_stmt;
|
||||
DEALLOCATE PREPARE er_yin_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_printer_binding`
|
||||
(
|
||||
`id` bigint(20) NOT NULL COMMENT '绑定关系主键',
|
||||
`source_hard_id` bigint(20) NOT NULL COMMENT 'SUPERMARKET或CANTEEN来源业务终端id',
|
||||
`printer_hard_id` bigint(20) NOT NULL COMMENT 'EY_PRINT目标打印终端id',
|
||||
`merchant_id` bigint(20) NOT NULL COMMENT '双方共同所属商户id快照',
|
||||
`dept_id` bigint(20) NOT NULL COMMENT '双方共同所属商户部门id快照',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_hard_printer_binding_source` (`tenant_id`, `source_hard_id`),
|
||||
UNIQUE KEY `uk_hard_printer_binding_printer` (`tenant_id`, `printer_hard_id`),
|
||||
KEY `idx_hard_printer_binding_merchant` (`tenant_id`, `merchant_id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci
|
||||
COMMENT = '业务终端与尔印打印终端一对一绑定';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_printer_config`
|
||||
(
|
||||
`id` bigint(20) NOT NULL COMMENT '配置主键',
|
||||
`printer_hard_id` bigint(20) NOT NULL COMMENT 'EY_PRINT打印终端id',
|
||||
`printer_model` varchar(128) DEFAULT NULL COMMENT '打印机型号',
|
||||
`usb_vendor_id` int(11) DEFAULT NULL COMMENT 'USB Vendor ID十进制值',
|
||||
`usb_product_id` int(11) DEFAULT NULL COMMENT 'USB Product ID十进制值',
|
||||
`paper_width` int(11) NOT NULL DEFAULT 80 COMMENT '纸宽,单位mm,首期固定80',
|
||||
`auto_cut` char(1) NOT NULL DEFAULT '1' COMMENT '自动切刀开关,1开启、0关闭',
|
||||
`image_mode` varchar(16) NOT NULL DEFAULT 'GS_V0' COMMENT '图片指令模式,GS_V0或ESC_STAR',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_hard_printer_config_printer` (`tenant_id`, `printer_hard_id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci
|
||||
COMMENT = '尔印打印终端USB与ESC/POS配置';
|
||||
|
||||
SET @er_yin_ddl = NULL;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- 终端日志文件表(hq-platform 无独立建表脚本,本 DDL 依据 HardTerminalLogFileEntity 与查询场景编写)。
|
||||
-- 记录 Android 终端上传到文件中心的日志文件索引,同一文件重复上传时累加 upload_count 并更新地址。
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_terminal_log_file`
|
||||
(
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`hard_id` bigint NOT NULL COMMENT '设备ID(hard_manager.id)',
|
||||
`hard_name` varchar(128) DEFAULT NULL COMMENT '上传时设备名称快照',
|
||||
`unique_code` varchar(128) DEFAULT NULL COMMENT '上传时设备唯一编号快照',
|
||||
`hard_type` char(10) DEFAULT NULL COMMENT '上传时设备类型快照',
|
||||
`merchant_id` bigint DEFAULT NULL COMMENT '上传时商户ID快照',
|
||||
`dept_id` bigint DEFAULT NULL COMMENT '上传时部门ID快照',
|
||||
`log_date` date NOT NULL COMMENT '日志所属日期',
|
||||
`relative_path` varchar(512) NOT NULL COMMENT '终端日志根目录下的相对路径',
|
||||
`original_name` varchar(255) DEFAULT NULL COMMENT '原日志文件名',
|
||||
`file_size` bigint DEFAULT NULL COMMENT '文件大小(字节)',
|
||||
`file_hash` char(32) DEFAULT NULL COMMENT '文件内容哈希(当前使用MD5)',
|
||||
`file_modified_time` datetime DEFAULT NULL COMMENT '终端本地文件最后修改时间',
|
||||
`sys_file_id` bigint DEFAULT NULL COMMENT '文件中心sys_file记录ID',
|
||||
`storage_file_name` varchar(255) DEFAULT NULL COMMENT 'OSS对象文件名',
|
||||
`storage_url` varchar(512) DEFAULT NULL COMMENT '文件访问相对地址',
|
||||
`app_version` varchar(64) DEFAULT NULL COMMENT '上传时应用版本',
|
||||
`sanitize_version` varchar(64) DEFAULT NULL COMMENT '日志脱敏规则版本',
|
||||
`upload_time` datetime DEFAULT NULL COMMENT '最近成功上传时间',
|
||||
`upload_count` int NOT NULL DEFAULT 1 COMMENT '成功上传次数',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
|
||||
`tenant_id` bigint NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_hard_terminal_log_position` (`tenant_id`, `hard_id`, `relative_path`, `del_flag`),
|
||||
KEY `idx_hard_terminal_log_date` (`tenant_id`, `hard_id`, `log_date`, `del_flag`),
|
||||
KEY `idx_hard_terminal_log_hash` (`tenant_id`, `file_hash`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='终端日志文件';
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 终端心跳状态表(迁移自 hq-platform 20260730_add_hard_terminal_status.sql)。
|
||||
-- 食堂、超市 Android 终端心跳状态。在线状态由服务端按最后心跳时间实时计算,不冗余落库。
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_terminal_status` (
|
||||
`hard_id` bigint NOT NULL COMMENT '设备ID,对应hard_manager.id',
|
||||
`last_online_time` datetime NOT NULL COMMENT '服务端最近收到心跳的时间',
|
||||
`heartbeat_scene` varchar(32) NOT NULL COMMENT '最近心跳场景(ONLINE,AUTO_UPDATE)',
|
||||
`disconnect_reason` varchar(128) DEFAULT NULL COMMENT '计划内断开原因,目前用于标记自动更新重启',
|
||||
`apk_version_name` varchar(64) DEFAULT NULL COMMENT '终端上报的APK展示版本号',
|
||||
`apk_version_code` bigint DEFAULT NULL COMMENT '终端上报的APK内部版本码',
|
||||
`system_uptime_seconds` bigint DEFAULT NULL COMMENT 'Android系统本次启动后运行秒数',
|
||||
`app_uptime_seconds` bigint DEFAULT NULL COMMENT '终端App本次进程运行秒数',
|
||||
`network_type` varchar(32) DEFAULT NULL COMMENT '最近心跳使用的网络类型',
|
||||
`available_storage_bytes` bigint DEFAULT NULL COMMENT '应用存储卷最近可用空间,单位字节',
|
||||
`root_status` varchar(16) DEFAULT NULL COMMENT 'Root状态(unknown,available,unavailable)',
|
||||
`device_owner` tinyint(1) DEFAULT NULL COMMENT '当前应用是否为Device Owner',
|
||||
`kiosk_active` tinyint(1) DEFAULT NULL COMMENT '最近心跳时是否处于Kiosk锁定任务模式',
|
||||
`device_status_json` json DEFAULT NULL COMMENT '打印机、电子秤、读卡器、副屏等可选硬件状态快照',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime NOT NULL COMMENT '修改时间',
|
||||
`tenant_id` bigint NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`hard_id`),
|
||||
KEY `idx_hard_terminal_status_tenant_online` (`tenant_id`, `last_online_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='食堂超市终端心跳状态';
|
||||
@@ -0,0 +1,30 @@
|
||||
-- 消费机日志表(迁移自 hq-platform 800pjcloudx_hardware.sql 末段)。
|
||||
-- 记录消费机终端 HTTP 调用日志(req/res/error/耗时),unique_code 唯一。
|
||||
CREATE TABLE IF NOT EXISTS `pjcloudx_hardware`.`hard_xfj_log`
|
||||
(
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`unique_code` varchar(64) NOT NULL COMMENT '设备唯一编号',
|
||||
`hard_type` varchar(32) NOT NULL COMMENT '设备类型',
|
||||
`title` varchar(64) NOT NULL COMMENT '接口标题',
|
||||
`headers` varchar(2048) COMMENT 'headers',
|
||||
`res` varchar(2048) COMMENT 'res',
|
||||
`req` varchar(2048) COMMENT 'req',
|
||||
`error` varchar(2048) COMMENT 'error',
|
||||
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
|
||||
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||
`cost` int DEFAULT NULL COMMENT '耗时(毫秒)',
|
||||
`create_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`del_flag` char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_unique_code` (`unique_code`),
|
||||
KEY `idx_hard_type` (`hard_type`),
|
||||
KEY `idx_title` (`title`),
|
||||
KEY `idx_start_time` (`start_time`),
|
||||
KEY `idx_end_time` (`end_time`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='消费机日志';
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 为钱包流水补充财务统计标签和业务备注;历史数据保持为空。
|
||||
-- tags 保存 wallet_transaction_tag 字典值,多个值使用英文逗号分隔;remark 前后端统一限制为 256 个字符。
|
||||
ALTER TABLE wallet_transaction
|
||||
ADD COLUMN tags varchar(512) DEFAULT NULL COMMENT '财务统计标签,多个wallet_transaction_tag字典值使用英文逗号分隔' AFTER balance;
|
||||
|
||||
ALTER TABLE wallet_transaction
|
||||
ADD COLUMN remark varchar(256) DEFAULT NULL COMMENT '流水备注,最多256个字符' AFTER tags;
|
||||
@@ -0,0 +1,158 @@
|
||||
================================================================
|
||||
hard 模块迁移(2026-09-08)—— 权限/菜单/字典 手动添加清单
|
||||
================================================================
|
||||
说明:以下内容需要在 pjcloudx 主库(sys_menu / sys_dict / sys_dict_item)手动添加,
|
||||
可以直接在「系统管理-菜单管理 / 字典管理」界面配置,也可以执行文末附带的 SQL(幂等,可重复执行)。
|
||||
每项都标注了用途,若目标环境已存在同名项请跳过。
|
||||
|
||||
----------------------------------------------------------------
|
||||
一、按钮权限(sys_menu,menu_type='2')
|
||||
----------------------------------------------------------------
|
||||
挂载位置建议:挂在对应页面的父菜单下(与 hard_hardManager_view 同级)。
|
||||
|
||||
1. hard_hardTerminalLogFile_view 终端日志-查看 (终端日志页面 + 设备详情页日志区)
|
||||
2. hard_hardTerminalLogFile_preview 终端日志-预览
|
||||
3. hard_hardTerminalLogFile_down 终端日志-下载
|
||||
4. hard_hardTerminalLogFile_pull 终端日志-拉取
|
||||
5. hard_hardManager_printer_bind 设备管理-绑定打印机 (设备详情页打印配置区)
|
||||
6. hard_hardManager_export 设备管理-导出 (设备列表导出按钮,若已有则跳过)
|
||||
7. hard_hardInvoice_voucher_reprint 发票凭证-重打兑换券(打印任务弹窗重打,若不需要可不加)
|
||||
|
||||
----------------------------------------------------------------
|
||||
二、页面菜单(sys_menu,menu_type='1',动态路由)
|
||||
----------------------------------------------------------------
|
||||
1. 终端日志(机器日志)
|
||||
路径 path:/hard/hard-terminal-log-file/index
|
||||
权限标识:hard_hardTerminalLogFile_view
|
||||
2. 消费机日志
|
||||
路径 path:/hard/xfj/xfj-log/index
|
||||
权限标识:(查看权限可留空或自定 hard_xfjLog_view,页面本身无按钮权限)
|
||||
|
||||
说明:
|
||||
- 设备详情页 /hard/hard-manager/details 已由前端静态路由注册(isHide),无需配菜单。
|
||||
- 售后订单详情 order-refund 沿用目标库已有菜单,无需改动。
|
||||
|
||||
----------------------------------------------------------------
|
||||
三、字典(sys_dict + sys_dict_item)
|
||||
----------------------------------------------------------------
|
||||
1. hard_type(设备类型,已有字典,补充缺失项):
|
||||
- 9 云版消费机
|
||||
- 12 尔印打印机器
|
||||
(若已有 10/11 云音响等项则不动)
|
||||
2. 新增 hard_print_type(打印凭证类型):
|
||||
- 1 发票
|
||||
- 2 兑换券
|
||||
3. 新增 hard_print_status(打印任务状态):
|
||||
- 0 待生成
|
||||
- 1 待打印
|
||||
- 2 已打印
|
||||
- 3 打印失败
|
||||
- 4 已取消
|
||||
4. 可选:hard_appt_reserve_mode(预约模式,售后详情页预约信息列显示用;
|
||||
目标库若无此字典该列显示空,不影响其他功能)
|
||||
|
||||
----------------------------------------------------------------
|
||||
四、附:可直接执行的幂等 SQL(按需使用,执行前请备份)
|
||||
----------------------------------------------------------------
|
||||
-- 1. hard_type 补类型 9/12(每个已配置 hard_type 的租户幂等插入)
|
||||
SET @dict_item_id_base = (SELECT GREATEST(COALESCE(MAX(`id`), 0), 2026090800000000000) FROM `pjcloudx`.`sys_dict_item`);
|
||||
INSERT INTO `pjcloudx`.`sys_dict_item`
|
||||
(`id`, `dict_id`, `item_value`, `label`, `dict_type`, `description`, `sort_order`, `create_by`, `create_time`, `remarks`, `del_flag`, `tenant_id`)
|
||||
SELECT @dict_item_id_base + ROW_NUMBER() OVER (ORDER BY d.`tenant_id`, item.`item_value`),
|
||||
d.`id`, item.`item_value`, item.`label`, 'hard_type', item.`label`, CAST(item.`item_value` AS SIGNED),
|
||||
'system', NOW(), item.`label`, '0', d.`tenant_id`
|
||||
FROM `pjcloudx`.`sys_dict` d
|
||||
JOIN (
|
||||
SELECT '9' AS `item_value`, '云版消费机' AS `label`
|
||||
UNION ALL SELECT '12', '尔印打印机器'
|
||||
) item
|
||||
WHERE d.`dict_type` = 'hard_type' AND d.`del_flag` = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `pjcloudx`.`sys_dict_item` i
|
||||
WHERE i.`tenant_id` = d.`tenant_id` AND i.`dict_type` = 'hard_type'
|
||||
AND i.`item_value` = item.`item_value` AND i.`del_flag` = '0'
|
||||
);
|
||||
SET @dict_item_id_base = NULL;
|
||||
|
||||
-- 2. hard_print_type / hard_print_status 字典(按租户幂等创建)
|
||||
SET @dict_id_base = (SELECT GREATEST(COALESCE(MAX(`id`), 0), 2026090800000000000) FROM `pjcloudx`.`sys_dict`);
|
||||
INSERT INTO `pjcloudx`.`sys_dict`
|
||||
(`id`, `dict_type`, `description`, `create_by`, `create_time`, `remarks`, `system_flag`, `del_flag`, `tenant_id`)
|
||||
SELECT @dict_id_base + ROW_NUMBER() OVER (ORDER BY tenant.`tenant_id`, t.`dict_type`),
|
||||
t.`dict_type`, t.`description`, 'system', NOW(), t.`description`, '1', '0', tenant.`tenant_id`
|
||||
FROM (SELECT DISTINCT `tenant_id` FROM `pjcloudx`.`sys_dict` WHERE `del_flag` = '0') tenant
|
||||
CROSS JOIN (
|
||||
SELECT 'hard_print_type' AS `dict_type`, '打印凭证类型' AS `description`
|
||||
UNION ALL SELECT 'hard_print_status', '打印任务状态'
|
||||
) t
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `pjcloudx`.`sys_dict` d
|
||||
WHERE d.`tenant_id` = tenant.`tenant_id` AND d.`dict_type` = t.`dict_type` AND d.`del_flag` = '0'
|
||||
);
|
||||
SET @dict_id_base = NULL;
|
||||
|
||||
SET @dict_item_id_base = (SELECT GREATEST(COALESCE(MAX(`id`), 0), 2026090800000000000) FROM `pjcloudx`.`sys_dict_item`);
|
||||
INSERT INTO `pjcloudx`.`sys_dict_item`
|
||||
(`id`, `dict_id`, `item_value`, `label`, `dict_type`, `description`, `sort_order`, `create_by`, `create_time`, `del_flag`, `tenant_id`)
|
||||
SELECT @dict_item_id_base + ROW_NUMBER() OVER (ORDER BY d.`tenant_id`, d.`dict_type`, item.`sort_order`, item.`item_value`),
|
||||
d.`id`, item.`item_value`, item.`label`, d.`dict_type`, item.`label`, item.`sort_order`,
|
||||
'system', NOW(), '0', d.`tenant_id`
|
||||
FROM `pjcloudx`.`sys_dict` d
|
||||
JOIN (
|
||||
SELECT 'hard_print_type' AS `dict_type`, '1' AS `item_value`, '发票' AS `label`, 1 AS `sort_order`
|
||||
UNION ALL SELECT 'hard_print_type', '2', '兑换券', 2
|
||||
UNION ALL SELECT 'hard_print_status', '0', '待生成', 0
|
||||
UNION ALL SELECT 'hard_print_status', '1', '待打印', 1
|
||||
UNION ALL SELECT 'hard_print_status', '2', '已打印', 2
|
||||
UNION ALL SELECT 'hard_print_status', '3', '打印失败', 3
|
||||
UNION ALL SELECT 'hard_print_status', '4', '已取消', 4
|
||||
) item ON item.`dict_type` = d.`dict_type`
|
||||
WHERE d.`del_flag` = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `pjcloudx`.`sys_dict_item` i
|
||||
WHERE i.`tenant_id` = d.`tenant_id` AND i.`dict_type` = d.`dict_type`
|
||||
AND i.`item_value` = item.`item_value` AND i.`del_flag` = '0'
|
||||
);
|
||||
SET @dict_item_id_base = NULL;
|
||||
|
||||
-- 3. 按钮权限(挂到已有父权限所在菜单下,menu_type='2')
|
||||
-- 先在界面确认父菜单,或使用下面按 permission 定位父菜单的幂等插入:
|
||||
SET @menu_id_base = (SELECT GREATEST(COALESCE(MAX(`menu_id`), 0), 2026090800000000000) FROM `pjcloudx`.`sys_menu`);
|
||||
INSERT INTO `pjcloudx`.`sys_menu`
|
||||
(`menu_id`, `name`, `permission`, `parent_id`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `del_flag`, `tenant_id`)
|
||||
SELECT @menu_id_base + ROW_NUMBER() OVER (ORDER BY p.`tenant_id`, p.`menu_id`, item.`permission`),
|
||||
item.`name`, item.`permission`, p.`menu_id`, '1', 20, '0', '0', '2', 'system', NOW(), '0', p.`tenant_id`
|
||||
FROM `pjcloudx`.`sys_menu` p
|
||||
JOIN (
|
||||
SELECT 'hard_hardTerminalLogFile_view' AS `permission`, '终端日志查看' AS `name`, 'hard_hardManager_view' AS `parent_permission`
|
||||
UNION ALL SELECT 'hard_hardTerminalLogFile_preview', '终端日志预览', 'hard_hardManager_view'
|
||||
UNION ALL SELECT 'hard_hardTerminalLogFile_down', '终端日志下载', 'hard_hardManager_view'
|
||||
UNION ALL SELECT 'hard_hardTerminalLogFile_pull', '终端日志拉取', 'hard_hardManager_view'
|
||||
UNION ALL SELECT 'hard_hardManager_printer_bind', '绑定打印机', 'hard_hardManager_view'
|
||||
UNION ALL SELECT 'hard_hardManager_export', '导出设备', 'hard_hardManager_view'
|
||||
UNION ALL SELECT 'hard_hardInvoice_voucher_reprint', '重打兑换券', 'hard_hardInvoice_view'
|
||||
) item ON item.`parent_permission` = p.`permission`
|
||||
WHERE p.`del_flag` = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `pjcloudx`.`sys_menu` e
|
||||
WHERE e.`tenant_id` = p.`tenant_id` AND e.`permission` = item.`permission` AND e.`del_flag` = '0'
|
||||
);
|
||||
SET @menu_id_base = NULL;
|
||||
|
||||
-- 4. 页面菜单(终端日志 / 消费机日志)—— parent_id 请替换为实际的「设备管理/硬件」目录菜单ID
|
||||
-- (建议直接在菜单管理界面添加,下面仅作字段参考)
|
||||
-- INSERT INTO `pjcloudx`.`sys_menu`
|
||||
-- (`menu_id`,`name`,`permission`,`path`,`parent_id`,`icon`,`visible`,`sort_order`,`keep_alive`,`embedded`,`menu_type`,`create_by`,`create_time`,`del_flag`,`tenant_id`)
|
||||
-- VALUES
|
||||
-- (自定义ID, '终端日志', 'hard_hardTerminalLogFile_view', '/hard/hard-terminal-log-file/index', <父菜单ID>, NULL, '1', 100, '0', NULL, '1', 'admin', NOW(), '0', 1),
|
||||
-- (自定义ID, '消费机日志', NULL, '/hard/xfj/xfj-log/index', <父菜单ID>, NULL, '1', 101, '0', NULL, '1', 'admin', NOW(), '0', 1);
|
||||
|
||||
----------------------------------------------------------------
|
||||
五、数据库迁移 SQL 执行顺序(pj_service_cloud/db/ 下本次新增)
|
||||
----------------------------------------------------------------
|
||||
1. 20260908_add_hard_terminal_status.sql
|
||||
2. 20260908_add_hard_terminal_log_file.sql
|
||||
3. 20260908_add_hard_order_evidence_image.sql
|
||||
4. 20260908_add_hard_printer.sql (含 hard_invoice 打印任务化,注意先备份)
|
||||
5. 20260908_add_hard_xfj_log.sql
|
||||
6. 20260908_add_hard_order_verification_time.sql
|
||||
+151
-15
@@ -177,22 +177,83 @@ CREATE TABLE hard_group
|
||||
DROP TABLE IF EXISTS `hard_invoice`;
|
||||
CREATE TABLE hard_invoice
|
||||
(
|
||||
id bigint NOT NULL COMMENT '编号',
|
||||
dept_id bigint NOT NULL COMMENT '部门id',
|
||||
hard_id bigint NOT NULL COMMENT '设备id',
|
||||
order_id bigint NOT NULL COMMENT '硬件订单id',
|
||||
status char(10) NOT NULL COMMENT '状态',
|
||||
pic varchar(256) NOT NULL COMMENT '发票图片',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id)
|
||||
id bigint NOT NULL COMMENT '编号',
|
||||
dept_id bigint NOT NULL COMMENT '部门id',
|
||||
hard_id bigint DEFAULT NULL COMMENT '来源业务或核销终端id',
|
||||
printer_hard_id bigint DEFAULT NULL COMMENT '目标尔印打印终端id',
|
||||
order_id bigint NOT NULL COMMENT '硬件订单id',
|
||||
print_type char(2) NOT NULL DEFAULT '1' COMMENT '打印类型,1发票、2兑换券',
|
||||
print_no int NOT NULL DEFAULT 1 COMMENT '同订单同类型打印序号',
|
||||
retry_version int NOT NULL DEFAULT 0 COMMENT '人工失败重试版本',
|
||||
attempt_count int NOT NULL DEFAULT 0 COMMENT '终端USB累计尝试次数',
|
||||
status char(10) NOT NULL DEFAULT '0' COMMENT '状态,0待生成、1待打印、2已打印、3打印失败、4已取消',
|
||||
failure_stage varchar(32) DEFAULT NULL COMMENT '失败阶段',
|
||||
last_error varchar(2000) DEFAULT NULL COMMENT '最近失败摘要',
|
||||
printed_at datetime DEFAULT NULL COMMENT '终端确认写入成功时间',
|
||||
reprint_reason varchar(500) DEFAULT NULL COMMENT '人工重打或再次核销原因',
|
||||
pic varchar(512) DEFAULT NULL COMMENT '后端生成的打印PNG地址',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_hard_invoice_order_type_no (tenant_id, order_id, print_type, print_no),
|
||||
KEY idx_hard_invoice_printer_status (tenant_id, printer_hard_id, status, update_time, del_flag),
|
||||
KEY idx_hard_invoice_source_status (tenant_id, hard_id, status, del_flag)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='发票记录';
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='打印任务与凭证记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_printer_binding
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `hard_printer_binding`;
|
||||
CREATE TABLE hard_printer_binding
|
||||
(
|
||||
id bigint NOT NULL COMMENT '绑定关系主键',
|
||||
source_hard_id bigint NOT NULL COMMENT 'SUPERMARKET或CANTEEN来源业务终端id',
|
||||
printer_hard_id bigint NOT NULL COMMENT 'EY_PRINT目标打印终端id',
|
||||
merchant_id bigint NOT NULL COMMENT '双方共同所属商户id快照',
|
||||
dept_id bigint NOT NULL COMMENT '双方共同所属商户部门id快照',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
tenant_id bigint NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_hard_printer_binding_source (tenant_id, source_hard_id),
|
||||
UNIQUE KEY uk_hard_printer_binding_printer (tenant_id, printer_hard_id),
|
||||
KEY idx_hard_printer_binding_merchant (tenant_id, merchant_id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='业务终端与尔印打印终端一对一绑定';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_printer_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `hard_printer_config`;
|
||||
CREATE TABLE hard_printer_config
|
||||
(
|
||||
id bigint NOT NULL COMMENT '配置主键',
|
||||
printer_hard_id bigint NOT NULL COMMENT 'EY_PRINT打印终端id',
|
||||
printer_model varchar(128) DEFAULT NULL COMMENT '打印机型号',
|
||||
usb_vendor_id int DEFAULT NULL COMMENT 'USB Vendor ID十进制值',
|
||||
usb_product_id int DEFAULT NULL COMMENT 'USB Product ID十进制值',
|
||||
paper_width int NOT NULL DEFAULT 80 COMMENT '纸宽,单位mm,首期固定80',
|
||||
auto_cut char(1) NOT NULL DEFAULT '1' COMMENT '自动切刀开关,1开启、0关闭',
|
||||
image_mode varchar(16) NOT NULL DEFAULT 'GS_V0' COMMENT '图片指令模式,GS_V0或ESC_STAR',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
tenant_id bigint NOT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_hard_printer_config_printer (tenant_id, printer_hard_id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='尔印打印终端USB与ESC/POS配置';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_car
|
||||
@@ -311,6 +372,50 @@ CREATE TABLE hard_order
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE = InnoDB COMMENT ='订单表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_order_evidence_image
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `hard_order_evidence_image`;
|
||||
CREATE TABLE hard_order_evidence_image
|
||||
(
|
||||
id bigint NOT NULL AUTO_INCREMENT COMMENT '证据记录自增主键',
|
||||
order_id bigint NOT NULL COMMENT '订单主键,来源hard_order.id',
|
||||
android_order_code varchar(128) NOT NULL COMMENT '终端本地稳定订单号快照',
|
||||
evidence_type varchar(32) NOT NULL COMMENT '证据类型,FACE_RECOGNITION或MENU_RECOGNITION',
|
||||
evidence_role varchar(32) NOT NULL COMMENT '图片角色,FACE_REFERENCE、FACE_LIVE或MENU_LIVE',
|
||||
sequence_no int NOT NULL DEFAULT 0 COMMENT '同类型同角色图片序号,从0开始',
|
||||
user_id bigint DEFAULT NULL COMMENT '识别用户主键快照',
|
||||
face_sample_id varchar(64) DEFAULT NULL COMMENT '虹软人脸样本编号快照',
|
||||
reference_image_url varchar(512) DEFAULT NULL COMMENT '识别时后台参考人脸来源地址快照,不作为展示地址',
|
||||
similarity decimal(8, 6) DEFAULT NULL COMMENT '识别相似度快照,取值0至1',
|
||||
similarity_threshold decimal(8, 6) DEFAULT NULL COMMENT '终端生效的相似度阈值快照,取值0至1',
|
||||
hard_id bigint NOT NULL COMMENT '产生证据的设备主键',
|
||||
captured_at datetime DEFAULT NULL COMMENT '终端实际采集时间',
|
||||
sys_file_id bigint DEFAULT NULL COMMENT '文件中心sys_file记录主键',
|
||||
storage_file_name varchar(255) DEFAULT NULL COMMENT '文件中心对象名',
|
||||
image_url varchar(512) DEFAULT NULL COMMENT '订单证据图片访问地址',
|
||||
image_hash char(64) DEFAULT NULL COMMENT '实际文件SHA-256哈希',
|
||||
image_size bigint DEFAULT NULL COMMENT '实际文件大小,单位字节',
|
||||
image_width int DEFAULT NULL COMMENT '实际图片宽度,单位像素',
|
||||
image_height int DEFAULT NULL COMMENT '实际图片高度,单位像素',
|
||||
status varchar(32) NOT NULL COMMENT '证据状态,PENDING、AVAILABLE或UNAVAILABLE',
|
||||
failure_reason varchar(500) DEFAULT NULL COMMENT '永久缺失或复制失败摘要,不保存终端完整路径',
|
||||
app_version varchar(64) DEFAULT NULL COMMENT '产生证据的终端应用版本',
|
||||
create_by varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
del_flag char(1) DEFAULT '0' COMMENT '逻辑删除标记,0有效,1删除',
|
||||
tenant_id bigint NOT NULL COMMENT '租户主键',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_hard_order_evidence_position
|
||||
(tenant_id, order_id, evidence_type, evidence_role, sequence_no, del_flag),
|
||||
KEY idx_hard_order_evidence_android_order
|
||||
(tenant_id, hard_id, android_order_code, del_flag),
|
||||
KEY idx_hard_order_evidence_status
|
||||
(tenant_id, status, update_time, del_flag)
|
||||
) ENGINE = InnoDB COMMENT ='硬件订单识别证据图片';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_after_sale
|
||||
-- ----------------------------
|
||||
@@ -599,4 +704,35 @@ CREATE TABLE hard_third_mi_order
|
||||
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE = InnoDB COMMENT ='硬件商品免密订单';
|
||||
) ENGINE = InnoDB COMMENT ='硬件商品免密订单';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_xfj_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `hard_xfj_log`;
|
||||
CREATE TABLE hard_xfj_log
|
||||
(
|
||||
id bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
unique_code varchar(64) NOT NULL COMMENT '设备唯一编号',
|
||||
hard_type varchar(32) NOT NULL COMMENT '设备类型',
|
||||
title varchar(64) NOT NULL COMMENT '接口标题',
|
||||
headers varchar(2048) COMMENT 'headers',
|
||||
res varchar(2048) COMMENT 'res',
|
||||
req varchar(2048) COMMENT 'req',
|
||||
error varchar(2048) COMMENT 'error',
|
||||
start_time datetime DEFAULT NULL COMMENT '开始时间',
|
||||
end_time datetime DEFAULT NULL COMMENT '结束时间',
|
||||
cost int DEFAULT NULL COMMENT '耗时(毫秒)',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY idx_unique_code (unique_code),
|
||||
KEY idx_hard_type (hard_type),
|
||||
KEY idx_title (title),
|
||||
KEY idx_start_time (start_time),
|
||||
KEY idx_end_time (end_time)
|
||||
) ENGINE = InnoDB COMMENT ='消费机日志';
|
||||
@@ -374,6 +374,8 @@ CREATE TABLE wallet_transaction
|
||||
title varchar(128) NOT NULL COMMENT '流水标题',
|
||||
price int NOT NULL COMMENT '交易金额, 单位分',
|
||||
balance int NOT NULL COMMENT '余额, 单位分',
|
||||
tags varchar(512) DEFAULT NULL COMMENT '财务统计标签,多个wallet_transaction_tag字典值使用英文逗号分隔',
|
||||
remark varchar(256) DEFAULT NULL COMMENT '流水备注,最多256个字符',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
-- 线上慢 SQL 索引优化(OceanBase MySQL 模式)
|
||||
-- 执行前请确认同名索引不存在;建议低峰期逐条执行并观察建索引耗时。
|
||||
-- 本文件只包含索引 DDL,不包含任何数据修改。
|
||||
|
||||
-- P0:订单列表按租户、删除标记、创建时间倒序分页。
|
||||
CREATE INDEX idx_hard_order_tenant_del_ct_uid
|
||||
ON pjcloudx_hardware.hard_order (tenant_id, del_flag, create_time, user_id);
|
||||
|
||||
-- P0:订单号 + 订单类型 + 状态精确计数。
|
||||
CREATE INDEX idx_hard_order_tenant_del_order_lookup
|
||||
ON pjcloudx_hardware.hard_order (tenant_id, del_flag, order_no, order_type, order_status);
|
||||
|
||||
-- P0:设备侧订单号精确查询。
|
||||
CREATE INDEX idx_hard_order_tenant_del_android_code
|
||||
ON pjcloudx_hardware.hard_order (tenant_id, del_flag, android_order_code);
|
||||
|
||||
-- P0:每个订单取最新售后记录(GROUP BY order_id + MAX(id))。
|
||||
CREATE INDEX idx_hard_after_sale_tenant_order_id
|
||||
ON pjcloudx_hardware.hard_after_sale (tenant_id, order_id, id);
|
||||
|
||||
-- P0:钱包流水按业务类型、时间倒序分页;biz_type 为 TINYTEXT,使用前缀索引。
|
||||
CREATE INDEX idx_wallet_tx_tenant_del_ct_uid
|
||||
ON pjcloudx_wallet.wallet_transaction (tenant_id, del_flag, create_time, user_id);
|
||||
|
||||
-- P0:钱包流水按业务类型、时间倒序分页;biz_type 为 TINYTEXT,使用前缀索引。
|
||||
CREATE INDEX idx_wallet_tx_tenant_del_biz_ct_uid
|
||||
ON pjcloudx_wallet.wallet_transaction (tenant_id, del_flag, biz_type(8), create_time, user_id);
|
||||
|
||||
-- P0:流程历史按办理人查询并按结束时间倒序。
|
||||
CREATE INDEX idx_act_hi_actinst_assignee_end
|
||||
ON pjcloudx_flow.act_hi_actinst (ASSIGNEE_, END_TIME_);
|
||||
|
||||
-- P1:支付状态 + 支付时间统计。先观察 P0 上线后的实际收益,再决定是否执行。
|
||||
-- CREATE INDEX idx_hard_order_tenant_del_status_ptime_mch
|
||||
-- ON pjcloudx_hardware.hard_order (tenant_id, del_flag, order_status, pay_time, merchant_id);
|
||||
|
||||
-- P1:指定商户部门的支付统计。索引较宽,按接口频率决定是否执行。
|
||||
-- CREATE INDEX idx_hard_order_tenant_del_mdept_status_mt_ptime
|
||||
-- ON pjcloudx_hardware.hard_order
|
||||
-- (tenant_id, del_flag, merchant_dept_id, order_status, merchant_type, pay_time);
|
||||
|
||||
-- P1:按钱包所属部门定位钱包 ID;维表较小时收益有限。
|
||||
-- CREATE INDEX idx_wallet_manage_tenant_dept_id
|
||||
-- ON pjcloudx_wallet.wallet_manage (tenant_id, dept_id, id);
|
||||
@@ -0,0 +1,54 @@
|
||||
# 线上慢 SQL 索引优化分析(2026-08-26)
|
||||
|
||||
## 数据范围
|
||||
|
||||
- 来源:`/Users/dengzhangchen/Downloads/rm-1fyg92tzhbt23g9hw_slowlog_*.csv`
|
||||
- 原始记录:638 条;按完整请求字段去重后 608 条;63 个 SQL Hash。
|
||||
- 数据库分布:`pjcloudx_hardware` 451 条、`pjcloudx_wallet` 153 条、`pjcloudx_flow` 4 条。
|
||||
- 主要扫描表:`hard_order`、`hard_after_sale`、`wallet_transaction`、`act_hi_actinst`。
|
||||
- 测试连接:OceanBase CE 4.3.5.5,MySQL 兼容模式;读取 `SHOW CREATE TABLE`、`SHOW INDEX`、`EXPLAIN`,并在测试库临时创建/回滚候选索引;未在生产库执行 DDL。
|
||||
|
||||
## 结论
|
||||
|
||||
### 强推荐,先上线
|
||||
|
||||
| 优先级 | 数据库/表 | 建议索引 | 主要覆盖日志 | 依据 |
|
||||
|---|---|---|---|---|
|
||||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, create_time, user_id)` | 订单列表、分页 COUNT,Hash `270048891%7C1391`、`528608244%7C616` 等 | 现有索引的第三列是 `merchant_id` 或 `user_id`,无法同时支撑租户+删除标记后的时间倒序;临时索引验证后由扫描+TOP-N 变为反向范围扫描。 |
|
||||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, order_no, order_type, order_status)` | 订单号精确 COUNT,Hash `_1019382644%7C138` | 原执行计划只能在统计索引上按 `order_status` 扫描,再回表过滤订单号/类型;临时索引验证为三列等值范围扫描。 |
|
||||
| P0 | `pjcloudx_hardware.hard_order` | `(tenant_id, del_flag, android_order_code)` | 设备订单号查询,Hash `_986870313%7C628` | 原计划扫描约 98 万行后回表过滤 `android_order_code`;临时索引验证为等值范围扫描。 |
|
||||
| P0 | `pjcloudx_hardware.hard_after_sale` | `(tenant_id, order_id, id)` | 订单列表中的“每单最新售后”子查询,覆盖 233 条相关记录 | 原表只有主键,`GROUP BY order_id + MAX(id)` 为全表扫描;临时索引验证后为有序范围扫描并使用 Merge Group By。 |
|
||||
| P0 | `pjcloudx_wallet.wallet_transaction` | `(tenant_id, del_flag, create_time, user_id)` | 无业务类型条件的钱包流水首页,Hash `698590166%7C408` | 原计划全表扫描并排序;临时索引验证后为反向范围扫描,直接取前 10 行。 |
|
||||
| P0 | `pjcloudx_wallet.wallet_transaction` | `(tenant_id, del_flag, biz_type(8), create_time, user_id)` | 钱包流水按业务类型筛选列表/COUNT,Hash `2036241023%7C466`、`255738862%7C620` 等 | `biz_type` 是 `TINYTEXT`,必须使用前缀索引;临时索引验证后可按租户、删除标记、业务类型及时间范围扫描。 |
|
||||
| P0 | `pjcloudx_flow.act_hi_actinst` | `(ASSIGNEE_, END_TIME_)` | 流程历史待办查询,Hash `_2096433074%7C126` | 原计划全表扫描并排序;临时索引验证后为反向范围扫描,直接满足 `ORDER BY END_TIME_ DESC LIMIT 10`。 |
|
||||
|
||||
### 条件推荐,先观察资源与收益
|
||||
|
||||
| 优先级 | 建议索引 | 适用场景 | 说明 |
|
||||
|---|---|---|---|
|
||||
| P1 | `pjcloudx_hardware.hard_order (tenant_id, del_flag, order_status, pay_time, merchant_id)` | 按支付状态+支付时间汇总,Hash `_1655261703%7C470` 等 | 临时索引可将范围缩小到状态/时间,但仍需要回表和聚合;建议 P0 上线后按 `EXPLAIN` 和实际耗时决定。 |
|
||||
| P1 | `pjcloudx_hardware.hard_order (tenant_id, del_flag, merchant_dept_id, order_status, merchant_type, pay_time)` | 指定商户部门的支付统计,Hash `1634334540%7C358` 等 | 对指定部门过滤收益明显;宽索引写放大较大,先对高频租户/接口评估。 |
|
||||
| P1 | `pjcloudx_wallet.wallet_manage (tenant_id, dept_id, id)` | 钱包按部门聚合 | 仅用于小维表定位钱包 ID;表规模较小时收益有限,适合与钱包统计接口一起验证。 |
|
||||
|
||||
## 暂不建议只靠加索引解决
|
||||
|
||||
- 统计类 SQL 使用派生表、外层权限过滤和 `ORDER BY CASE`,索引只能减少底层扫描,不能消除派生表物化/聚合成本;建议后续改为先过滤再聚合,并移除 COUNT 查询中的无意义排序。
|
||||
- `hard_order` 订单详情列表反复 `LEFT JOIN` 最新售后子查询;P0 索引后仍建议将“最新售后 ID”改为窗口/预聚合或单独维护字段。
|
||||
- `UPDATE hard_door_cabinet ... WHERE id = ? AND del_flag = ? AND tenant_id = ?` 已命中主键 `id`,慢点更可能是锁等待、事务或网络;加索引不能解决,需结合 `SHOW PROCESSLIST`、锁等待和事务耗时排查。
|
||||
|
||||
## 上线步骤
|
||||
|
||||
1. 低峰期逐条执行 `01_add_indexes.sql`,每条 DDL 单独观察完成时间和租户延迟。
|
||||
2. 执行前确认同名索引不存在;OceanBase 建索引可能持续几十秒到数分钟,不要重复提交。
|
||||
3. DDL 完成后对代表性 SQL 执行 `EXPLAIN`,确认使用新索引且不出现 `TABLE FULL SCAN`。
|
||||
4. 观察 15–30 分钟慢 SQL、CPU、IO、事务提交延迟;若写入延迟明显,再评估 P1 索引。
|
||||
5. 统计接口仍慢时,优先改写 SQL,再考虑继续增加宽复合索引。
|
||||
|
||||
## 验证记录
|
||||
|
||||
临时索引均已在测试库创建并回滚,最终检查无 `tmp_%` 索引残留。验证结果:
|
||||
|
||||
- 订单时间分页:`TABLE RANGE SCAN ... Reverse`,不再全表扫描/大范围 TOP-N。
|
||||
- 钱包业务流水:`TABLE RANGE SCAN ... Reverse`,`biz_type(8)` 前缀索引可用。
|
||||
- 最新售后:`MERGE GROUP BY` + `TABLE RANGE SCAN`,不再全表扫描。
|
||||
- 流程历史:`TABLE RANGE SCAN ... Reverse`,不再全表扫描。
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bsd.pjcloud.canteen.vo.app;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "套餐组成项(App)")
|
||||
public class OnlineSpuComboItemVO {
|
||||
|
||||
@Schema(description = "组成菜品 SPU id")
|
||||
private Long childSpuId;
|
||||
|
||||
@Schema(description = "组成菜品名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "组成菜品封面")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "份数")
|
||||
private Integer quantity;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "组成菜品单价(分,仅展示)")
|
||||
private Integer price;
|
||||
}
|
||||
+2
@@ -169,6 +169,8 @@ public interface CacheConstants {
|
||||
|
||||
String HARDER_ORDER_NON_PAY = "hard:order:non_pay";
|
||||
|
||||
String HARD_XFJ_WHITE_LIST_LOCK = "hard:xfj:whitelist:lock";
|
||||
|
||||
/* ************************************* end 订单相关 end ************************************* */
|
||||
|
||||
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.bsd.pjcloud.hard.api.constant;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 打印任务失败阶段,帮助管理端区分配置问题、服务端故障和业务终端代理打印故障。
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HardPrintFailureStageEnum {
|
||||
|
||||
/** 来源业务终端缺失、未绑定打印机或绑定关系失效。 */
|
||||
BINDING("BINDING", "设备绑定"),
|
||||
|
||||
/** 服务端读取业务数据、绘制 PNG 或上传文件失败。 */
|
||||
IMAGE_GENERATION("IMAGE_GENERATION", "图片生成"),
|
||||
|
||||
/** 服务端向绑定的来源业务终端发布 MQTT 消息失败。 */
|
||||
MQTT_DISPATCH("MQTT_DISPATCH", "MQTT下发"),
|
||||
|
||||
/** 来源业务终端下载图片、连接 USB 或写入 ESC/POS 数据失败。 */
|
||||
TERMINAL_PRINT("TERMINAL_PRINT", "终端打印");
|
||||
|
||||
/** 持久化阶段编码。 */
|
||||
private final String stage;
|
||||
|
||||
/** 阶段中文说明。 */
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 按持久化编码解析失败阶段。
|
||||
*
|
||||
* @param stage 阶段编码
|
||||
* @return 匹配阶段;编码为空或未知时返回 {@code null}
|
||||
*/
|
||||
public static HardPrintFailureStageEnum valueOfByStage(String stage) {
|
||||
if (ObjectUtil.isEmpty(stage)) return null;
|
||||
return Arrays.stream(values()).filter(value -> ObjectUtil.equals(stage, value.stage)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+27
-4
@@ -4,23 +4,46 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.xml.stream.events.StartDocument;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 打印状态
|
||||
* 打印任务状态。
|
||||
*
|
||||
* <p>编码 {@code 1} 和 {@code 2} 分别兼容历史“已生成”和“已打印”记录。</p>
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum HardPrintStatusEnum {
|
||||
GENERATE("1", "已生成"),
|
||||
/** 任务已落库,等待后端生成并上传打印图片。 */
|
||||
PENDING_GENERATION("0", "待生成"),
|
||||
|
||||
/** 图片已生成并允许下发,历史枚举名保留为 GENERATE 以保持二进制兼容。 */
|
||||
GENERATE("1", "待打印"),
|
||||
|
||||
/** 来源业务终端已确认完整 USB 数据和切纸命令写入成功。 */
|
||||
PRINT("2", "已打印"),
|
||||
|
||||
/** 绑定、图片生成、MQTT 下发或业务终端代理打印阶段失败,可重试原任务。 */
|
||||
FAILED("3", "打印失败"),
|
||||
|
||||
/** 核销回退等业务动作取消了尚未打印的任务。 */
|
||||
CANCELLED("4", "已取消"),
|
||||
;
|
||||
|
||||
/** 持久化状态编码。 */
|
||||
private final String type;
|
||||
|
||||
/** 状态中文说明。 */
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 按持久化编码解析打印状态。
|
||||
*
|
||||
* @param type 状态编码
|
||||
* @return 匹配状态;编码为空或未知时返回 {@code null}
|
||||
*/
|
||||
public static HardPrintStatusEnum valueOfByType(String type) {
|
||||
if (ObjectUtil.isEmpty(type)) return null;
|
||||
return Arrays.stream(values()).filter(v -> ObjectUtil.equals(type, v.getType())).findFirst().get();
|
||||
return Arrays.stream(values()).filter(v -> ObjectUtil.equals(type, v.getType())).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.bsd.pjcloud.hard.api.constant;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 打印凭证类型,用于选择后端图片模板和管理端业务动作。
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HardPrintTypeEnum {
|
||||
|
||||
/** 支付订单的发票或消费小票。 */
|
||||
INVOICE("1", "发票"),
|
||||
|
||||
/** 预约订单核销成功后生成的纸质兑换凭证。 */
|
||||
REDEMPTION_VOUCHER("2", "兑换券");
|
||||
|
||||
/** 持久化类型编码。 */
|
||||
private final String type;
|
||||
|
||||
/** 类型中文说明。 */
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 按持久化编码解析打印类型。
|
||||
*
|
||||
* @param type 类型编码
|
||||
* @return 匹配类型;编码为空或未知时返回 {@code null}
|
||||
*/
|
||||
public static HardPrintTypeEnum valueOfByType(String type) {
|
||||
if (ObjectUtil.isEmpty(type)) return null;
|
||||
return Arrays.stream(values()).filter(value -> ObjectUtil.equals(type, value.type)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+14
@@ -23,8 +23,11 @@ public enum HardTypeEnum {
|
||||
OTHER("6", "其他支付方式(第三方接口对接)"),
|
||||
MERCHANT_ACT("7", "商户活动"),
|
||||
FARMER("8", "共富商城(助农)"),
|
||||
YUN_XFJ("9", "云版消费机"),
|
||||
YYX("10", "4G-云音响"),
|
||||
YYX_WIFI("11", "wifi-云音响"),
|
||||
/** 由绑定的超市或食堂 Android 终端代理驱动的无屏尔印 USB 打印机。 */
|
||||
EY_PRINT("12", "尔印打印机器"),
|
||||
;
|
||||
private final String type;
|
||||
private final String description;
|
||||
@@ -44,4 +47,15 @@ public enum HardTypeEnum {
|
||||
return Arrays.stream(values()).filter(v -> ObjectUtil.equals(type, v.getType())).findFirst().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定设备类型是否为尔印逻辑打印机资产。
|
||||
*
|
||||
* @param hardTypeEnum 待判断的设备类型
|
||||
* @return 类型为 {@link #EY_PRINT} 时返回 {@code true}
|
||||
*/
|
||||
public static boolean isPrintHard(HardTypeEnum hardTypeEnum) {
|
||||
if (ObjectUtil.isEmpty(hardTypeEnum)) return false;
|
||||
return List.of(EY_PRINT).contains(hardTypeEnum);
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.bsd.pjcloud.hard.api.constant;
|
||||
|
||||
/**
|
||||
* 终端心跳场景。
|
||||
*
|
||||
* <p>枚举名称是终端与服务端之间的固定接口编码,不得随展示文案调整。</p>
|
||||
*/
|
||||
public enum TerminalHeartbeatSceneEnum {
|
||||
|
||||
/**
|
||||
* 普通在线心跳;收到后清除之前的计划更新断开原因。
|
||||
*/
|
||||
ONLINE,
|
||||
|
||||
/**
|
||||
* 自动更新安装前心跳;用于在应用重启期间保留可解释的离线原因。
|
||||
*/
|
||||
AUTO_UPDATE
|
||||
}
|
||||
+1
@@ -15,6 +15,7 @@ public enum HardHourTypeEnum {
|
||||
MORNING("1", "早餐"),
|
||||
NOONING("2", "午餐"),
|
||||
NIGHT("3", "晚餐"),
|
||||
LATE_NIGHT("4", "夜间餐"),
|
||||
;
|
||||
private final String type;
|
||||
private final String description;
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.bsd.pjcloud.hard.api.constant.order;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 订单证据图片角色,编码用于唯一位置约束和跨端展示。
|
||||
*/
|
||||
@Getter
|
||||
public enum HardOrderEvidenceRoleEnum {
|
||||
|
||||
/**
|
||||
* 人脸库中被命中的后台参考人脸,由服务端从受信文件中心复制为订单快照。
|
||||
*/
|
||||
FACE_REFERENCE("FACE_REFERENCE", "后台参考人脸"),
|
||||
|
||||
/**
|
||||
* 终端实际通过比对的现场人脸裁剪图,由终端异步上传。
|
||||
*/
|
||||
FACE_LIVE("FACE_LIVE", "现场识别人脸"),
|
||||
|
||||
/**
|
||||
* 菜谱识别的现场餐盘图片预留角色,当前版本不创建该类证据。
|
||||
*/
|
||||
MENU_LIVE("MENU_LIVE", "餐盘现场图");
|
||||
|
||||
/**
|
||||
* 数据库和接口使用的稳定编码。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* PC 订单详情使用的中文名称。
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 构造订单证据图片角色。
|
||||
*
|
||||
* @param code 数据库与跨端协议编码
|
||||
* @param description 中文展示名称
|
||||
*/
|
||||
HardOrderEvidenceRoleEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按协议编码解析图片角色,未知编码返回 {@code null}。
|
||||
*
|
||||
* @param code 数据库或接口编码
|
||||
* @return 对应角色;未知时返回 {@code null}
|
||||
*/
|
||||
public static HardOrderEvidenceRoleEnum valueOfCode(String code) {
|
||||
return Arrays.stream(values()).filter(item -> item.code.equals(code)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.bsd.pjcloud.hard.api.constant.order;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 服务端订单证据状态,表达证据是否仍待补传、已经可查看或已永久缺失。
|
||||
*/
|
||||
@Getter
|
||||
public enum HardOrderEvidenceStatusEnum {
|
||||
|
||||
/**
|
||||
* 订单已记录识别元数据,但图片仍等待终端上传或服务端复制。
|
||||
*/
|
||||
PENDING("PENDING", "待补传"),
|
||||
|
||||
/**
|
||||
* 文件中心已经保存订单专属图片并可在订单详情中查看。
|
||||
*/
|
||||
AVAILABLE("AVAILABLE", "可查看"),
|
||||
|
||||
/**
|
||||
* 本地文件过期、损坏或参考图片变化,已确认无法继续补齐。
|
||||
*/
|
||||
UNAVAILABLE("UNAVAILABLE", "永久缺失");
|
||||
|
||||
/**
|
||||
* 数据库和接口使用的稳定编码。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* PC 订单详情使用的中文名称。
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 构造订单证据状态。
|
||||
*
|
||||
* @param code 数据库与跨端协议编码
|
||||
* @param description 中文展示名称
|
||||
*/
|
||||
HardOrderEvidenceStatusEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按协议编码解析证据状态,未知编码返回 {@code null}。
|
||||
*
|
||||
* @param code 数据库或接口编码
|
||||
* @return 对应状态;未知时返回 {@code null}
|
||||
*/
|
||||
public static HardOrderEvidenceStatusEnum valueOfCode(String code) {
|
||||
return Arrays.stream(values()).filter(item -> item.code.equals(code)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.bsd.pjcloud.hard.api.constant.order;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 订单证据业务类型,编码同时用于数据库、终端 SQLite 和跨端接口,禁止随展示文案调整。
|
||||
*/
|
||||
@Getter
|
||||
public enum HardOrderEvidenceTypeEnum {
|
||||
|
||||
/**
|
||||
* 人脸识别证据,当前版本保存后台参考人脸和现场识别人脸。
|
||||
*/
|
||||
FACE_RECOGNITION("FACE_RECOGNITION", "人脸识别"),
|
||||
|
||||
/**
|
||||
* 菜谱识别证据预留值,当前版本不执行菜谱识别或图片采集。
|
||||
*/
|
||||
MENU_RECOGNITION("MENU_RECOGNITION", "菜谱识别");
|
||||
|
||||
/**
|
||||
* 数据库和接口使用的稳定编码。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* PC 订单详情使用的中文名称。
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 构造订单证据业务类型。
|
||||
*
|
||||
* @param code 数据库与跨端协议编码
|
||||
* @param description 中文展示名称
|
||||
*/
|
||||
HardOrderEvidenceTypeEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按协议编码解析证据类型,未知编码返回 {@code null} 供调用方拒绝非法请求。
|
||||
*
|
||||
* @param code 数据库或接口编码
|
||||
* @return 对应类型;未知时返回 {@code null}
|
||||
*/
|
||||
public static HardOrderEvidenceTypeEnum valueOfCode(String code) {
|
||||
return Arrays.stream(values()).filter(item -> item.code.equals(code)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
+5
@@ -20,6 +20,11 @@ public enum HardOrderStatusEnum {
|
||||
REFUND("10", "退款"),
|
||||
CANCEL("20", "已核销"),
|
||||
UNFOLLOW("30", "已取消(智能柜)"),
|
||||
TAKE_OUT_AWAIT("100", "待接单(外卖)"),
|
||||
TAKE_OUT_ING("110", "配送中(外卖)"),
|
||||
SHOP_WAIT_PAY_SUCCESS("200", "待发货/已支付(商城购买)"),
|
||||
SHOP_WAIT_RECEIVE("210", "待收货(商城购买)"),
|
||||
SHOP_WAIT_EVALUATE("220", "订单完成/待评价(商城购买)"),
|
||||
;
|
||||
private final String type;
|
||||
private final String description;
|
||||
|
||||
+2
@@ -21,6 +21,8 @@ public enum HardOrderTypeEnum {
|
||||
LB("6", "宁波银行(美好生活)"),
|
||||
FARMER("7", "共富商城(助农)"),
|
||||
TOC_VISITOR("8", "toc访客购买(不记名用户)"),
|
||||
XFJ("9", "消费机支付"),
|
||||
TAKE_OUT("10", "外卖"),
|
||||
;
|
||||
private final String type;
|
||||
private final String description;
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.bsd.pjcloud.hard.api.constant.xfj;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.bsd.pjcloud.hard.api.constant.order.HardPayTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 硬件授权类型
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum HardXfjTypeEnum {
|
||||
|
||||
QRCODE_PAY("10", "钱包二维码支付"),
|
||||
WECHAT_QRCODE_PAY("11", "微信二维码支付"),
|
||||
ALI_QRCODE_PAY("12", "支付宝二维码支付"),
|
||||
CARD_PAY("20", "饭卡支付"),
|
||||
;
|
||||
private final String type;
|
||||
private final String description;
|
||||
|
||||
public static HardXfjTypeEnum valueOfByType(String type) {
|
||||
if (ObjectUtil.isEmpty(type)) return null;
|
||||
return Arrays.stream(values()).filter(v -> ObjectUtil.equals(type, v.getType())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static HardPayTypeEnum getHardPayType(HardXfjTypeEnum type) {
|
||||
return switch (type) {
|
||||
case QRCODE_PAY -> HardPayTypeEnum.WALLET_QR_CODE;
|
||||
case WECHAT_QRCODE_PAY -> HardPayTypeEnum.WECHANT_SELF_QR_CODE_PAY;
|
||||
case ALI_QRCODE_PAY -> HardPayTypeEnum.ALI_SELF_QR_CODE_PAY;
|
||||
case CARD_PAY -> HardPayTypeEnum.WALLET_CARD;
|
||||
};
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 管理端创建发票或兑换券打印任务的请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建打印任务请求")
|
||||
public class HardPrintRequestDto {
|
||||
|
||||
/** 待打印凭证所属订单 ID。 */
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
@Schema(description = "订单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 打印类型编码,1 发票、2 兑换券。 */
|
||||
@NotBlank(message = "打印类型不能为空")
|
||||
@Schema(description = "打印类型")
|
||||
private String printType;
|
||||
|
||||
/** 来源业务终端 ID;订单没有来源终端且同商户存在多个绑定时必填。 */
|
||||
@Schema(description = "来源业务终端ID")
|
||||
private Long sourceHardId;
|
||||
|
||||
/** 已打印任务再次创建下一序号时必填的重打原因。 */
|
||||
@Schema(description = "重打原因")
|
||||
private String reprintReason;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 食堂或超市终端代理无屏 EY_PRINT 提交的打印结果回执。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "业务终端代理打印结果回执")
|
||||
public class HardPrintResultDto {
|
||||
|
||||
/** hard_invoice 打印任务主键。 */
|
||||
@NotNull(message = "打印任务ID不能为空")
|
||||
@Schema(description = "打印任务ID")
|
||||
private Long taskId;
|
||||
|
||||
/** 任务所属订单 ID,用于阻断任务与订单错配。 */
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
@Schema(description = "订单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 来源业务终端实际处理的重试版本。 */
|
||||
@NotNull(message = "重试版本不能为空")
|
||||
@Min(value = 0, message = "重试版本不能小于0")
|
||||
@Schema(description = "重试版本")
|
||||
private Integer retryVersion;
|
||||
|
||||
/** 完整 USB 数据及切纸命令是否已经被接口接受。 */
|
||||
@NotNull(message = "打印结果不能为空")
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
/** 本版本在来源业务终端执行的 USB 尝试次数。 */
|
||||
@NotNull(message = "尝试次数不能为空")
|
||||
@Min(value = 1, message = "尝试次数不能小于1")
|
||||
@Schema(description = "终端尝试次数")
|
||||
private Integer attemptCount;
|
||||
|
||||
/** 失败时的可诊断错误摘要,不得包含凭证或密码。 */
|
||||
@Schema(description = "错误摘要")
|
||||
private String errorMessage;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 对既有打印任务执行失败重试或已打印重打的请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "打印任务重试或重打请求")
|
||||
public class HardPrintRetryDto {
|
||||
|
||||
/** 既有 hard_invoice 打印任务主键。 */
|
||||
@NotNull(message = "打印任务ID不能为空")
|
||||
@Schema(description = "打印任务ID")
|
||||
private Long historyId;
|
||||
|
||||
/**
|
||||
* 失败任务需要补录的来源业务终端 ID。
|
||||
*
|
||||
* <p>仅当原任务因缺少来源终端而失败时使用;服务端仍会校验该终端与订单同商户,
|
||||
* 并根据服务端绑定关系确定目标 EY_PRINT,客户端不能直接指定打印机。</p>
|
||||
*/
|
||||
@Schema(description = "补录的来源业务终端ID")
|
||||
private Long sourceHardId;
|
||||
|
||||
/** 已打印任务重打时必须填写的原因;失败任务原任务重试时可为空。 */
|
||||
@Schema(description = "重打原因")
|
||||
private String reprintReason;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 创建业务终端与无屏尔印 USB 打印机绑定关系的请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "USB 打印机绑定请求")
|
||||
public class HardPrinterBindingDto {
|
||||
|
||||
/** SUPERMARKET 或 CANTEEN 来源终端 ID。 */
|
||||
@NotNull(message = "来源终端不能为空")
|
||||
@Schema(description = "来源业务终端ID")
|
||||
private Long sourceHardId;
|
||||
|
||||
/** EY_PRINT 逻辑打印机资产 ID。 */
|
||||
@NotNull(message = "打印机不能为空")
|
||||
@Schema(description = "逻辑打印机资产ID")
|
||||
private Long printerHardId;
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 保存无屏尔印 USB 打印机参数的请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "USB 打印机配置请求")
|
||||
public class HardPrinterConfigDto {
|
||||
|
||||
/** EY_PRINT 逻辑打印机资产 ID。 */
|
||||
@NotNull(message = "打印机不能为空")
|
||||
@Schema(description = "逻辑打印机资产ID")
|
||||
private Long printerHardId;
|
||||
|
||||
/** 真机型号;未联调前允许为空。 */
|
||||
@Schema(description = "打印机型号")
|
||||
private String printerModel;
|
||||
|
||||
/** USB Vendor ID 十进制值;未联调前允许为空。 */
|
||||
@Schema(description = "USB VID")
|
||||
private Integer usbVendorId;
|
||||
|
||||
/** USB Product ID 十进制值;未联调前允许为空。 */
|
||||
@Schema(description = "USB PID")
|
||||
private Integer usbProductId;
|
||||
|
||||
/** 纸张宽度,首期只允许 80。 */
|
||||
@NotNull(message = "纸宽不能为空")
|
||||
@Schema(description = "纸宽,单位mm")
|
||||
private Integer paperWidth;
|
||||
|
||||
/** 自动切刀开关,1 开启、0 关闭。 */
|
||||
@NotBlank(message = "自动切刀开关不能为空")
|
||||
@Schema(description = "自动切刀开关")
|
||||
private String autoCut;
|
||||
|
||||
/** 图片指令模式,只允许 GS_V0 或 ESC_STAR。 */
|
||||
@NotBlank(message = "图片指令模式不能为空")
|
||||
@Schema(description = "图片指令模式")
|
||||
private String imageMode;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.bsd.pjcloud.hard.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 食堂或超市终端代理上报实际 USB 打印机信息的请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "业务终端代理上报的 USB 打印机信息")
|
||||
public class HardPrinterInfoReportDto {
|
||||
|
||||
/** USB 描述符中的厂商和产品名称组合,最长保存 128 个字符。 */
|
||||
@NotBlank(message = "打印机型号不能为空")
|
||||
@Size(max = 128, message = "打印机型号不能超过128个字符")
|
||||
@Schema(description = "实际打印机型号")
|
||||
private String printerModel;
|
||||
|
||||
/** USB Vendor ID 十进制值,取值范围为无符号 16 位整数。 */
|
||||
@NotNull(message = "USB VID不能为空")
|
||||
@Min(value = 0, message = "USB VID必须在0到65535之间")
|
||||
@Max(value = 65535, message = "USB VID必须在0到65535之间")
|
||||
@Schema(description = "USB VID")
|
||||
private Integer usbVendorId;
|
||||
|
||||
/** USB Product ID 十进制值,取值范围为无符号 16 位整数。 */
|
||||
@NotNull(message = "USB PID不能为空")
|
||||
@Min(value = 0, message = "USB PID必须在0到65535之间")
|
||||
@Max(value = 65535, message = "USB PID必须在0到65535之间")
|
||||
@Schema(description = "USB PID")
|
||||
private Integer usbProductId;
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.hard;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.constant.TerminalHeartbeatSceneEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 食堂、超市 Android 终端心跳请求。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端心跳请求")
|
||||
public class TerminalHeartbeatDto {
|
||||
|
||||
/**
|
||||
* APK 展示版本号,来源于终端当前安装包;取不到时允许为空。
|
||||
*/
|
||||
@Schema(description = "APK展示版本号")
|
||||
private String apkVersionName;
|
||||
|
||||
/**
|
||||
* APK 内部版本码,来源于终端当前安装包;取不到时允许为空。
|
||||
*/
|
||||
@Schema(description = "APK内部版本码")
|
||||
private Long apkVersionCode;
|
||||
|
||||
/**
|
||||
* 本次上报场景,使用 {@link TerminalHeartbeatSceneEnum} 的固定接口名称。
|
||||
*/
|
||||
@NotNull(message = "心跳场景不能为空")
|
||||
@Schema(description = "心跳场景", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private TerminalHeartbeatSceneEnum scene;
|
||||
|
||||
/**
|
||||
* Android 系统本次启动后的运行秒数,用于区分整机重启与应用进程重启。
|
||||
*/
|
||||
@Schema(description = "Android系统运行秒数")
|
||||
private Long systemUptimeSeconds;
|
||||
|
||||
/**
|
||||
* 当前应用进程运行秒数,用于识别应用崩溃或被系统重新拉起。
|
||||
*/
|
||||
@Schema(description = "应用进程运行秒数")
|
||||
private Long appUptimeSeconds;
|
||||
|
||||
/**
|
||||
* 最近上报时使用的网络类型,例如 ethernet、wifi、4g 或 offline。
|
||||
*/
|
||||
@Schema(description = "网络类型")
|
||||
private String networkType;
|
||||
|
||||
/**
|
||||
* 应用私有目录所在存储卷的可用空间,单位为字节。
|
||||
*/
|
||||
@Schema(description = "可用存储空间,单位字节")
|
||||
private Long availableStorageBytes;
|
||||
|
||||
/**
|
||||
* Root 最近检测状态,持久化值固定为 unknown、available 或 unavailable。
|
||||
*/
|
||||
@Pattern(regexp = "unknown|available|unavailable", message = "Root状态不正确")
|
||||
@Schema(description = "Root状态")
|
||||
private String rootStatus;
|
||||
|
||||
/**
|
||||
* 当前应用是否已被 Android 配置为 Device Owner;无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "是否为Device Owner")
|
||||
private Boolean deviceOwner;
|
||||
|
||||
/**
|
||||
* 最近上报时是否处于 Kiosk 锁定任务模式;无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "是否处于Kiosk模式")
|
||||
private Boolean kioskActive;
|
||||
|
||||
/**
|
||||
* 可选外围硬件状态,以稳定功能编码映射布尔值,例如 printer、steelyard、cardReader、subScreen。
|
||||
*/
|
||||
@Schema(description = "可选外围硬件状态")
|
||||
private Map<String, Boolean> deviceStatus;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 终端日志管理端查询条件。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端日志文件查询条件")
|
||||
public class HardTerminalLogFileQueryDto {
|
||||
|
||||
@Schema(description = "设备ID")
|
||||
private Long hardId;
|
||||
|
||||
@Schema(description = "设备名称")
|
||||
private String hardName;
|
||||
|
||||
@Schema(description = "设备唯一编号")
|
||||
private String uniqueCode;
|
||||
|
||||
@Schema(description = "商户ID")
|
||||
private Long merchantId;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long deptId;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
@Schema(description = "日志日期开始")
|
||||
private LocalDate logDateStart;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
@Schema(description = "日志日期结束")
|
||||
private LocalDate logDateEnd;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@Schema(description = "上传时间开始")
|
||||
private LocalDateTime uploadTimeStart;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@Schema(description = "上传时间结束")
|
||||
private LocalDateTime uploadTimeEnd;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 终端日志上传预检参数。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端日志上传预检参数")
|
||||
public class HardTerminalLogPreflightDto {
|
||||
|
||||
@NotBlank(message = "日志相对路径不能为空")
|
||||
@Size(max = 255, message = "日志相对路径不能超过255个字符")
|
||||
@Schema(description = "终端日志根目录下的相对路径")
|
||||
private String relativePath;
|
||||
|
||||
@NotNull(message = "文件大小不能为空")
|
||||
@Min(value = 0, message = "文件大小不能小于0")
|
||||
@Schema(description = "脱敏文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@NotBlank(message = "文件哈希不能为空")
|
||||
@Pattern(regexp = "(?i)^[0-9a-f]{32}$", message = "文件哈希必须为32位MD5")
|
||||
@Schema(description = "脱敏文件MD5")
|
||||
private String fileHash;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 终端日志上传元数据。设备快照由服务端根据请求头中的设备ID填充。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端日志上传元数据")
|
||||
public class HardTerminalLogUploadDto {
|
||||
|
||||
@NotBlank(message = "日志相对路径不能为空")
|
||||
@Size(max = 255, message = "日志相对路径不能超过255个字符")
|
||||
@Schema(description = "终端日志根目录下的相对路径")
|
||||
private String relativePath;
|
||||
|
||||
@NotNull(message = "日志日期不能为空")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
@Schema(description = "日志所属日期")
|
||||
private LocalDate logDate;
|
||||
|
||||
@NotNull(message = "本地修改时间不能为空")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@Schema(description = "终端本地文件最后修改时间")
|
||||
private LocalDateTime fileModifiedTime;
|
||||
|
||||
@Size(max = 64, message = "应用版本不能超过64个字符")
|
||||
@Schema(description = "应用版本")
|
||||
private String appVersion;
|
||||
|
||||
@NotBlank(message = "脱敏规则版本不能为空")
|
||||
@Size(max = 32, message = "脱敏规则版本不能超过32个字符")
|
||||
@Schema(description = "脱敏规则版本")
|
||||
private String sanitizeVersion;
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.DecimalMax;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 订单创建和证据补传共用的识别元数据,不包含图片字节和服务端文件信息。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "订单识别证据元数据")
|
||||
public class HardOrderEvidenceMetadataDto {
|
||||
|
||||
/**
|
||||
* 证据业务类型,使用 HardOrderEvidenceTypeEnum 的稳定编码。
|
||||
*/
|
||||
@NotBlank(message = "证据类型不能为空")
|
||||
@Size(max = 32, message = "证据类型不能超过32个字符")
|
||||
@Schema(description = "证据类型")
|
||||
private String evidenceType;
|
||||
|
||||
/**
|
||||
* 图片角色,使用 HardOrderEvidenceRoleEnum 的稳定编码。
|
||||
*/
|
||||
@NotBlank(message = "证据图片角色不能为空")
|
||||
@Size(max = 32, message = "证据图片角色不能超过32个字符")
|
||||
@Schema(description = "证据图片角色")
|
||||
private String evidenceRole;
|
||||
|
||||
/**
|
||||
* 同类型同角色下的图片序号,从0开始;人脸证据固定为0,菜谱识别可递增。
|
||||
*/
|
||||
@NotNull(message = "证据图片序号不能为空")
|
||||
@Min(value = 0, message = "证据图片序号不能小于0")
|
||||
@Max(value = 99, message = "证据图片序号不能大于99")
|
||||
@Schema(description = "证据图片序号")
|
||||
private Integer sequenceNo;
|
||||
|
||||
/**
|
||||
* 识别用户主键;未识别到平台用户时为空,不能由终端使用其他用户替代。
|
||||
*/
|
||||
@Schema(description = "识别用户主键")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 虹软搜索样本编号;仅人脸识别角色使用,服务端据此校验后台参考人脸。
|
||||
*/
|
||||
@Size(max = 64, message = "人脸样本编号不能超过64个字符")
|
||||
@Schema(description = "人脸样本编号")
|
||||
private String faceSampleId;
|
||||
|
||||
/**
|
||||
* 识别时终端人脸库记录的参考图片地址,仅作为服务端复制参考图的线索,不直接作为可访问地址返回。
|
||||
*/
|
||||
@Size(max = 512, message = "参考人脸地址不能超过512个字符")
|
||||
@Schema(description = "参考人脸地址")
|
||||
private String referenceImageUrl;
|
||||
|
||||
/**
|
||||
* 识别时终端人脸快照对应的后台更新时间;旧终端允许为空,新终端用于识别同地址内容替换。
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]", timezone = "Asia/Shanghai")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@Schema(description = "参考人脸来源版本")
|
||||
private LocalDateTime referenceImageVersion;
|
||||
|
||||
/**
|
||||
* 识别时实际相似度,取值0到1;为空表示当前证据类型不使用相似度。
|
||||
*/
|
||||
@DecimalMin(value = "0", message = "相似度不能小于0")
|
||||
@DecimalMax(value = "1", message = "相似度不能大于1")
|
||||
@Schema(description = "识别相似度")
|
||||
private BigDecimal similarity;
|
||||
|
||||
/**
|
||||
* 识别时终端生效的相似度阈值快照,取值0到1。
|
||||
*/
|
||||
@DecimalMin(value = "0", message = "相似度阈值不能小于0")
|
||||
@DecimalMax(value = "1", message = "相似度阈值不能大于1")
|
||||
@Schema(description = "相似度阈值快照")
|
||||
private BigDecimal similarityThreshold;
|
||||
|
||||
/**
|
||||
* 终端实际完成识别的本地时间;为空时服务端仅保留订单创建时间。
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]", timezone = "Asia/Shanghai")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@Schema(description = "终端采集时间")
|
||||
private LocalDateTime capturedAt;
|
||||
|
||||
/**
|
||||
* 终端按原始证据文件计算的 SHA-256;图片尚未生成或永久缺失时允许为空。
|
||||
*/
|
||||
@Pattern(regexp = "(?i)^[0-9a-f]{64}$", message = "图片哈希必须为64位SHA-256")
|
||||
@Schema(description = "图片SHA-256")
|
||||
private String imageHash;
|
||||
|
||||
/**
|
||||
* 终端证据文件大小,单位字节;参考图无法读取或文件尚未生成时允许为空。
|
||||
*/
|
||||
@Min(value = 0, message = "图片大小不能小于0")
|
||||
@Schema(description = "图片大小,单位字节")
|
||||
private Long imageSize;
|
||||
|
||||
/**
|
||||
* 终端解码后的图片宽度,单位像素。
|
||||
*/
|
||||
@Min(value = 1, message = "图片宽度必须大于0")
|
||||
@Schema(description = "图片宽度,单位像素")
|
||||
private Integer imageWidth;
|
||||
|
||||
/**
|
||||
* 终端解码后的图片高度,单位像素。
|
||||
*/
|
||||
@Min(value = 1, message = "图片高度必须大于0")
|
||||
@Schema(description = "图片高度,单位像素")
|
||||
private Integer imageHeight;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 终端订单识别证据的订单定位参数。
|
||||
*
|
||||
* <p>消费机现场下单继续使用终端订单号对账;预约核销订单不是当前终端创建的,核销成功后改用
|
||||
* 服务端订单 ID 定位。两种方式都必须结合请求头中的设备 ID 校验订单归属。</p>
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单识别证据的订单定位参数")
|
||||
public abstract class HardOrderEvidenceOrderRefDto extends HardOrderEvidenceMetadataDto {
|
||||
|
||||
/**
|
||||
* 终端本地稳定订单号,用于消费机现场下单后对账;预约核销证据允许为空。
|
||||
*/
|
||||
@Size(max = 128, message = "终端订单号不能超过128个字符")
|
||||
@Schema(description = "终端订单号,与服务端订单ID二选一")
|
||||
private String androidOrderCode;
|
||||
|
||||
/**
|
||||
* 已存在的服务端订单主键,仅用于当前设备已经成功核销的预约订单;现场下单证据允许为空。
|
||||
*/
|
||||
@Positive(message = "服务端订单ID必须大于0")
|
||||
@Schema(description = "服务端订单ID,与终端订单号二选一")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 校验至少提供一种订单定位信息,防止无条件查询或写入订单证据。
|
||||
*
|
||||
* @return 提供终端订单号或服务端订单 ID 时返回 {@code true}
|
||||
*/
|
||||
@AssertTrue(message = "终端订单号和服务端订单ID不能同时为空")
|
||||
public boolean isOrderReferencePresent() {
|
||||
return StrUtil.isNotBlank(androidOrderCode) || orderId != null;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 终端提交订单证据元数据或永久缺失结果的请求。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单证据元数据提交请求")
|
||||
public class HardOrderEvidenceSubmitDto extends HardOrderEvidenceOrderRefDto {
|
||||
|
||||
/**
|
||||
* 终端请求写入的目标状态,仅允许 PENDING 或 UNAVAILABLE;现场图片上传成功由服务端改为 AVAILABLE。
|
||||
*/
|
||||
@NotBlank(message = "证据状态不能为空")
|
||||
@Size(max = 32, message = "证据状态不能超过32个字符")
|
||||
@Schema(description = "证据状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 永久缺失原因;状态为 UNAVAILABLE 时必填,禁止包含本地完整敏感路径。
|
||||
*/
|
||||
@Size(max = 500, message = "失败原因不能超过500个字符")
|
||||
@Schema(description = "永久缺失原因")
|
||||
private String failureReason;
|
||||
|
||||
/**
|
||||
* 产生识别证据的终端应用版本,用于问题追溯。
|
||||
*/
|
||||
@Size(max = 64, message = "应用版本不能超过64个字符")
|
||||
@Schema(description = "终端应用版本")
|
||||
private String appVersion;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 终端上传订单现场证据图片时随 multipart 一并提交的元数据。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单现场证据上传请求")
|
||||
public class HardOrderEvidenceUploadDto extends HardOrderEvidenceOrderRefDto {
|
||||
|
||||
/**
|
||||
* 产生识别证据的终端应用版本,用于问题追溯。
|
||||
*/
|
||||
@Size(max = 64, message = "应用版本不能超过64个字符")
|
||||
@Schema(description = "终端应用版本")
|
||||
private String appVersion;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order.xfj;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.constant.xfj.HardXfjTypeEnum;
|
||||
import com.bsd.pjcloud.thirdparty.constant.miniplatform.AppPlusPlatformTypeEnum;
|
||||
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class HardXfjFastCreateOrderDto {
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
private Long hardId;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 支付金额(分)
|
||||
*/
|
||||
private int price;
|
||||
|
||||
/**
|
||||
* 钱包类型
|
||||
*/
|
||||
private WalletTypeEnum walletType;
|
||||
|
||||
/**
|
||||
* 消费机支付类型
|
||||
*/
|
||||
private HardXfjTypeEnum xfjPayType;
|
||||
|
||||
/**
|
||||
* 终端支付类型
|
||||
*/
|
||||
private AppPlusPlatformTypeEnum platformType;
|
||||
|
||||
/**
|
||||
* 付款码支付code
|
||||
*/
|
||||
private String devicePayCode;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.entity.xfj.HardXfjLogEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class HardXfjLogDto extends HardXfjLogEntity {
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String hardName;
|
||||
|
||||
private LocalDateTime[] createTimeList;
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class XfjConsumTransactionsDto {
|
||||
|
||||
/**
|
||||
* 消费序号(年月日时分秒+2个字节序号(0~65535)),用于识别上传数据不重复
|
||||
*/
|
||||
private String Order;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 卡模式
|
||||
* 0:实体卡
|
||||
* 1:电子卡
|
||||
*/
|
||||
private Integer CardMode;
|
||||
|
||||
/**
|
||||
* 交易模式
|
||||
* 0: 刷卡扣费
|
||||
* 1: 现金充值
|
||||
* 2: 余额查询
|
||||
* 3: 钱包转账
|
||||
* 4: 现金退款
|
||||
*/
|
||||
private Integer Mode;
|
||||
|
||||
/**
|
||||
* 扣费类型
|
||||
* 0:手动
|
||||
* 1:菜单
|
||||
* 2:定值
|
||||
* 3:取餐
|
||||
* 4:计时
|
||||
* 5:计次
|
||||
*/
|
||||
private Integer PayType;
|
||||
|
||||
/**
|
||||
* 消费金额(Mode为2时,此字段传入值可能为0)
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 菜品消费列表
|
||||
*/
|
||||
List<Menus> Menus;
|
||||
|
||||
@Data
|
||||
static public class Menus {
|
||||
|
||||
/**
|
||||
* 菜品编号(与菜品查询接口菜品编号一致)
|
||||
*/
|
||||
private String MenuID;
|
||||
|
||||
/**
|
||||
* 菜品数量(支持小数)
|
||||
*/
|
||||
private String Count;
|
||||
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjMenusDto {
|
||||
|
||||
/**
|
||||
* 菜品编号(操作员在消费机键盘输入的数字)
|
||||
*/
|
||||
private String MenuNo;
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOffLinesDto {
|
||||
|
||||
/**
|
||||
* 终端设备ID
|
||||
*/
|
||||
private Integer DeviceNumber;
|
||||
|
||||
/**
|
||||
* 消费序号(年月日时分秒+2个字节序号),用于识别上传数据不重复
|
||||
*/
|
||||
private String Order;
|
||||
|
||||
/**
|
||||
* 扣费类型
|
||||
* 0:手动模式
|
||||
* 1:菜单模式
|
||||
* 2:定值模式
|
||||
*/
|
||||
private Integer PayType;
|
||||
|
||||
/**
|
||||
* 卡模式
|
||||
* 0:实体卡
|
||||
* 1:电子卡
|
||||
*/
|
||||
private Integer CardMode;
|
||||
|
||||
/**
|
||||
* 消费时间年月日时分秒
|
||||
*/
|
||||
private String Time;
|
||||
|
||||
/**
|
||||
* 卡号10进制(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 扣费金额
|
||||
*/
|
||||
private String Money;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOrderQueryDto {
|
||||
|
||||
/**
|
||||
* 消费序号(年月日时分秒+2个字节序号(0~65535)),用于识别上传数据不重复
|
||||
*/
|
||||
private String Order;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 卡模式
|
||||
* 0:实体卡
|
||||
* 1:电子卡
|
||||
*/
|
||||
private Integer CardMode;
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOrderRefundDto {
|
||||
|
||||
/**
|
||||
* 消费订单号(最大30字符)
|
||||
*/
|
||||
private String OrderSeralid;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private String Amount;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class XfjQRCodeTransactionDto {
|
||||
|
||||
/**
|
||||
* 交易模式
|
||||
* 0:扣费
|
||||
* 1:充值
|
||||
* 3:信息查询
|
||||
*/
|
||||
private String Mode;
|
||||
|
||||
/**
|
||||
* 扣费类型
|
||||
* 0:手动
|
||||
* 1:菜单
|
||||
* 2:定值
|
||||
* 3:取餐
|
||||
* 4:计时
|
||||
*/
|
||||
private Integer PayType;
|
||||
|
||||
/**
|
||||
* 消费金额(Mode为3时,此字段传入值可能为0)
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 扫描到的二维码字符串(最大长度为200)
|
||||
*/
|
||||
private String QR;
|
||||
|
||||
private List<MenuItem> Menus;
|
||||
|
||||
@Data
|
||||
public static class MenuItem {
|
||||
|
||||
/**
|
||||
* 菜品编号(与菜品查询接口菜品编号一致)
|
||||
*/
|
||||
private String MenuID;
|
||||
|
||||
/**
|
||||
* 菜品数量(支持小数)
|
||||
*/
|
||||
private String Count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjServerTimeDto {
|
||||
|
||||
/**
|
||||
* 设备中的总白名单数
|
||||
*/
|
||||
private Integer WLSum;
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjTransactionDto {
|
||||
|
||||
/**
|
||||
* 从最后一笔消费往前算的位置
|
||||
* 1:就是最后一笔
|
||||
* 2:就是倒数第二笔
|
||||
*/
|
||||
private Integer Number;
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjTransactionInquiryDto {
|
||||
|
||||
/**
|
||||
* 二维码交易订单号(该值来自于第六及第七接口返回的QROrder)
|
||||
*/
|
||||
private String QROrder;
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjWhiteListDto {
|
||||
|
||||
/**
|
||||
* 通讯序号
|
||||
*/
|
||||
private Integer CommID;
|
||||
|
||||
/**
|
||||
* 页数
|
||||
*/
|
||||
private Long Page;
|
||||
|
||||
/**
|
||||
* 页个数(一页最多有多少个名单)
|
||||
*/
|
||||
private Long PageNumber;
|
||||
|
||||
}
|
||||
+74
-20
@@ -8,7 +8,7 @@ import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 发票记录
|
||||
* 打印任务与凭证记录
|
||||
*
|
||||
* @author dzc
|
||||
* @date 2025-01-05 14:17:32
|
||||
@@ -20,7 +20,7 @@ import java.time.LocalDateTime;
|
||||
@TenantTable
|
||||
@TableName("hard_invoice")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "发票记录")
|
||||
@Schema(description = "打印任务与凭证记录")
|
||||
public class HardInvoiceEntity extends Model<HardInvoiceEntity> {
|
||||
|
||||
|
||||
@@ -37,55 +37,109 @@ public class HardInvoiceEntity extends Model<HardInvoiceEntity> {
|
||||
@Schema(description="部门id")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
@Schema(description="设备id")
|
||||
/**
|
||||
* 来源业务终端 ID;历史记录继续保留原设备语义,不改写为打印机 ID。
|
||||
*/
|
||||
@Schema(description="来源业务终端ID")
|
||||
private Long hardId;
|
||||
|
||||
/**
|
||||
* 任务路由关联的尔印逻辑打印机资产 ID;历史记录和绑定失败任务允许为空。
|
||||
*/
|
||||
@Schema(description="逻辑打印机资产ID")
|
||||
private Long printerHardId;
|
||||
|
||||
/**
|
||||
* 硬件订单id
|
||||
*/
|
||||
@Schema(description="硬件订单id")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description="状态")
|
||||
/**
|
||||
* 打印任务状态,编码来自 HardPrintStatusEnum。
|
||||
*/
|
||||
@Schema(description="打印任务状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 发票图片
|
||||
*/
|
||||
@Schema(description="发票图片")
|
||||
/**
|
||||
* 打印凭证类型,1 发票、2 兑换券;历史空值按发票兼容。
|
||||
*/
|
||||
@Schema(description="打印凭证类型")
|
||||
private String printType;
|
||||
|
||||
/**
|
||||
* 同一订单同一凭证类型的打印序号;重试同一任务时保持不变。
|
||||
*/
|
||||
@Schema(description="打印序号")
|
||||
private Integer printNo;
|
||||
|
||||
/**
|
||||
* 人工重试版本;来源业务终端用任务 ID 和该版本共同判断是否允许再次打印。
|
||||
*/
|
||||
@Schema(description="重试版本")
|
||||
private Integer retryVersion;
|
||||
|
||||
/**
|
||||
* 来源业务终端代理上报的 USB 累计尝试次数。
|
||||
*/
|
||||
@Schema(description="终端尝试次数")
|
||||
private Integer attemptCount;
|
||||
|
||||
/**
|
||||
* 最近失败阶段,编码来自 HardPrintFailureStageEnum。
|
||||
*/
|
||||
@Schema(description="失败阶段")
|
||||
private String failureStage;
|
||||
|
||||
/**
|
||||
* 最近失败摘要,不保存 MQTT 密码、访问令牌等敏感信息。
|
||||
*/
|
||||
@Schema(description="最近失败原因")
|
||||
private String lastError;
|
||||
|
||||
/**
|
||||
* 来源业务终端确认完整 USB 数据和切纸命令写入成功的时间。
|
||||
*/
|
||||
@Schema(description="打印成功时间")
|
||||
private LocalDateTime printedAt;
|
||||
|
||||
/**
|
||||
* 人工重打原因,自动首次打印或失败重试允许为空。
|
||||
*/
|
||||
@Schema(description="重打原因")
|
||||
private String reprintReason;
|
||||
|
||||
/**
|
||||
* 后端生成并上传的打印 PNG 地址;待生成和生成失败时允许为空。
|
||||
*/
|
||||
@Schema(description="打印图片")
|
||||
private String pic;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description="修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description="修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@@ -93,7 +147,7 @@ public class HardInvoiceEntity extends Model<HardInvoiceEntity> {
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="删除标记")
|
||||
private String delFlag;
|
||||
|
||||
@@ -102,4 +156,4 @@ public class HardInvoiceEntity extends Model<HardInvoiceEntity> {
|
||||
*/
|
||||
@Schema(description="租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.bsd.pjcloud.hard.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 业务 Android 终端与无屏尔印 USB 打印机资产的一对一绑定关系。
|
||||
*
|
||||
* <p>记录使用物理删除,数据库分别对来源和目标设备设置唯一键,确保任何时刻都不能出现多重绑定。</p>
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TenantTable
|
||||
@TableName("hard_printer_binding")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "业务终端与 USB 打印机绑定关系")
|
||||
public class HardPrinterBindingEntity extends Model<HardPrinterBindingEntity> {
|
||||
|
||||
/** 绑定关系主键。 */
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "绑定关系主键")
|
||||
private Long id;
|
||||
|
||||
/** 来源业务终端 ID,只允许 SUPERMARKET 或 CANTEEN。 */
|
||||
@Schema(description = "来源业务终端ID")
|
||||
private Long sourceHardId;
|
||||
|
||||
/** 逻辑打印机资产 ID,只允许 EY_PRINT;设备本身不登录、不上报心跳。 */
|
||||
@Schema(description = "逻辑打印机资产ID")
|
||||
private Long printerHardId;
|
||||
|
||||
/** 双方共同所属商户 ID 快照。 */
|
||||
@Schema(description = "商户ID")
|
||||
private Long merchantId;
|
||||
|
||||
/** 双方共同所属商户部门 ID 快照。 */
|
||||
@Schema(description = "商户部门ID")
|
||||
private Long deptId;
|
||||
|
||||
/** 创建人。 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/** 创建时间。 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 修改人。 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/** 修改时间。 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 租户 ID。 */
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.bsd.pjcloud.hard.api.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 无屏尔印 USB 打印机资产的 USB 和 ESC/POS 参数配置。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TenantTable
|
||||
@TableName("hard_printer_config")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "尔印 USB 打印机配置")
|
||||
public class HardPrinterConfigEntity extends Model<HardPrinterConfigEntity> {
|
||||
|
||||
/** 配置主键。 */
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "配置主键")
|
||||
private Long id;
|
||||
|
||||
/** 配置所属 EY_PRINT 逻辑打印机资产 ID。 */
|
||||
@Schema(description = "逻辑打印机资产ID")
|
||||
private Long printerHardId;
|
||||
|
||||
/** 真机联调后填写的打印机型号。 */
|
||||
@Schema(description = "打印机型号")
|
||||
private String printerModel;
|
||||
|
||||
/** USB Vendor ID,使用十进制持久化。 */
|
||||
@Schema(description = "USB VID")
|
||||
private Integer usbVendorId;
|
||||
|
||||
/** USB Product ID,使用十进制持久化。 */
|
||||
@Schema(description = "USB PID")
|
||||
private Integer usbProductId;
|
||||
|
||||
/** 纸张宽度,首期固定为 80mm。 */
|
||||
@Schema(description = "纸宽,单位mm")
|
||||
private Integer paperWidth;
|
||||
|
||||
/** 是否在完整数据写入后发送自动切纸命令,1 开启、0 关闭。 */
|
||||
@Schema(description = "自动切刀开关")
|
||||
private String autoCut;
|
||||
|
||||
/** 图片指令模式,GS_V0 为默认值,ESC_STAR 为兼容回退。 */
|
||||
@Schema(description = "图片指令模式")
|
||||
private String imageMode;
|
||||
|
||||
/** 创建人。 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/** 创建时间。 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 修改人。 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/** 修改时间。 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 租户 ID。 */
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package com.bsd.pjcloud.hard.api.entity.manager;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 终端日志文件
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("hard_terminal_log_file")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "终端日志文件")
|
||||
public class HardTerminalLogFileEntity extends Model<HardTerminalLogFileEntity> {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "编号")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备ID(hard_manager.id)
|
||||
*/
|
||||
@Schema(description = "设备ID(hard_manager.id)")
|
||||
private Long hardId;
|
||||
|
||||
/**
|
||||
* 上传时设备名称快照
|
||||
*/
|
||||
@Schema(description = "上传时设备名称快照")
|
||||
private String hardName;
|
||||
|
||||
/**
|
||||
* 上传时设备唯一编号快照
|
||||
*/
|
||||
@Schema(description = "上传时设备唯一编号快照")
|
||||
private String uniqueCode;
|
||||
|
||||
/**
|
||||
* 上传时设备类型快照
|
||||
*/
|
||||
@Schema(description = "上传时设备类型快照")
|
||||
private String hardType;
|
||||
|
||||
/**
|
||||
* 上传时商户ID快照
|
||||
*/
|
||||
@Schema(description = "上传时商户ID快照")
|
||||
private Long merchantId;
|
||||
|
||||
/**
|
||||
* 上传时部门ID快照
|
||||
*/
|
||||
@Schema(description = "上传时部门ID快照")
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 日志所属日期
|
||||
*/
|
||||
@Schema(description = "日志所属日期")
|
||||
private LocalDate logDate;
|
||||
|
||||
/**
|
||||
* 终端日志根目录下的相对路径
|
||||
*/
|
||||
@Schema(description = "终端日志根目录下的相对路径")
|
||||
private String relativePath;
|
||||
|
||||
/**
|
||||
* 原日志文件名
|
||||
*/
|
||||
@Schema(description = "原日志文件名")
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
@Schema(description = "文件大小(字节)")
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 文件内容哈希(当前使用MD5)
|
||||
*/
|
||||
@Schema(description = "文件内容哈希(当前使用MD5)")
|
||||
private String fileHash;
|
||||
|
||||
/**
|
||||
* 终端本地文件最后修改时间
|
||||
*/
|
||||
@Schema(description = "终端本地文件最后修改时间")
|
||||
private LocalDateTime fileModifiedTime;
|
||||
|
||||
/**
|
||||
* 文件中心sys_file记录ID
|
||||
*/
|
||||
@Schema(description = "文件中心sys_file记录ID")
|
||||
private Long sysFileId;
|
||||
|
||||
/**
|
||||
* OSS对象文件名
|
||||
*/
|
||||
@Schema(description = "OSS对象文件名")
|
||||
private String storageFileName;
|
||||
|
||||
/**
|
||||
* 文件访问相对地址
|
||||
*/
|
||||
@Schema(description = "文件访问相对地址")
|
||||
private String storageUrl;
|
||||
|
||||
/**
|
||||
* 上传时应用版本
|
||||
*/
|
||||
@Schema(description = "上传时应用版本")
|
||||
private String appVersion;
|
||||
|
||||
/**
|
||||
* 日志脱敏规则版本
|
||||
*/
|
||||
@Schema(description = "日志脱敏规则版本")
|
||||
private String sanitizeVersion;
|
||||
|
||||
/**
|
||||
* 最近成功上传时间
|
||||
*/
|
||||
@Schema(description = "最近成功上传时间")
|
||||
private LocalDateTime uploadTime;
|
||||
|
||||
/**
|
||||
* 成功上传次数
|
||||
*/
|
||||
@Schema(description = "成功上传次数")
|
||||
private Integer uploadCount;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package com.bsd.pjcloud.hard.api.entity.manager;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 食堂、超市 Android 终端的最新心跳状态。
|
||||
*
|
||||
* <p>每台设备只保留一条最新记录,在线布尔值由服务端按时间阈值实时计算。</p>
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("hard_terminal_status")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "终端最新心跳状态")
|
||||
public class HardTerminalStatusEntity extends Model<HardTerminalStatusEntity> {
|
||||
|
||||
/**
|
||||
* 设备 ID,同时作为主键,对应 {@code hard_manager.id}。
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
@Schema(description = "设备ID")
|
||||
private Long hardId;
|
||||
|
||||
/**
|
||||
* 服务端最近收到心跳的时间,在线计算以此时间为准。
|
||||
*/
|
||||
@Schema(description = "最后在线时间")
|
||||
private LocalDateTime lastOnlineTime;
|
||||
|
||||
/**
|
||||
* 最近一次心跳场景,保存 {@code ONLINE} 或 {@code AUTO_UPDATE} 接口编码。
|
||||
*/
|
||||
@Schema(description = "最近心跳场景")
|
||||
private String heartbeatScene;
|
||||
|
||||
/**
|
||||
* 计划内断开原因;普通在线心跳会清空,当前仅保留“自动更新重启”。
|
||||
*/
|
||||
@Schema(description = "计划内断开原因")
|
||||
private String disconnectReason;
|
||||
|
||||
/**
|
||||
* 终端上报的 APK 展示版本号,无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "APK展示版本号")
|
||||
private String apkVersionName;
|
||||
|
||||
/**
|
||||
* 终端上报的 APK 内部版本码,无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "APK内部版本码")
|
||||
private Long apkVersionCode;
|
||||
|
||||
/**
|
||||
* Android 系统本次启动后的运行时长,单位为秒;终端无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "Android系统运行秒数")
|
||||
private Long systemUptimeSeconds;
|
||||
|
||||
/**
|
||||
* 当前应用进程运行时长,单位为秒;终端无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "应用进程运行秒数")
|
||||
private Long appUptimeSeconds;
|
||||
|
||||
/**
|
||||
* 最近一次心跳的网络类型,例如 ethernet、wifi、4g 或 offline。
|
||||
*/
|
||||
@Schema(description = "网络类型")
|
||||
private String networkType;
|
||||
|
||||
/**
|
||||
* 应用私有目录所在存储卷的可用空间,单位为字节。
|
||||
*/
|
||||
@Schema(description = "可用存储空间,单位字节")
|
||||
private Long availableStorageBytes;
|
||||
|
||||
/**
|
||||
* Root 最近检测状态,固定为 unknown、available 或 unavailable。
|
||||
*/
|
||||
@Schema(description = "Root状态")
|
||||
private String rootStatus;
|
||||
|
||||
/**
|
||||
* 最近一次心跳时当前应用是否为 Device Owner;无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "是否为Device Owner")
|
||||
private Boolean deviceOwner;
|
||||
|
||||
/**
|
||||
* 最近一次心跳时是否处于 Kiosk 锁定任务模式;无法读取时为空。
|
||||
*/
|
||||
@Schema(description = "是否处于Kiosk模式")
|
||||
private Boolean kioskActive;
|
||||
|
||||
/**
|
||||
* 打印机、电子秤、读卡器和副屏等可选硬件状态 JSON 快照。
|
||||
*/
|
||||
@Schema(description = "可选外围硬件状态JSON")
|
||||
private String deviceStatusJson;
|
||||
|
||||
/**
|
||||
* 创建人;设备心跳没有后台用户上下文时为空。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 首次收到该设备心跳的服务端时间。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人;设备心跳没有后台用户上下文时为空。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 最近一次心跳写入的服务端时间。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 数据所属租户 ID,取自已通过当前租户校验的设备记录。
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+7
-1
@@ -183,10 +183,16 @@ public class HardOrderEntity extends Model<HardOrderEntity> {
|
||||
@Schema(description = "退款状态")
|
||||
private String refundStatus;
|
||||
|
||||
/**
|
||||
* 订单实际核销完成时间,后续订单更新不得覆盖
|
||||
*/
|
||||
@Schema(description = "订单实际核销完成时间")
|
||||
private LocalDateTime verificationTime;
|
||||
|
||||
/**
|
||||
* 订单取消时间(退款订单)
|
||||
*/
|
||||
@Schema(description = "订单取消时间")
|
||||
@Schema(description = "订单取消时间(退款订单)")
|
||||
private LocalDateTime applyCancelTime;
|
||||
|
||||
/**
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
package com.bsd.pjcloud.hard.api.entity.order;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 订单识别证据图片,从属于 hard_order 并长期保存争议处理所需的文件快照和识别元数据。
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("hard_order_evidence_image")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单识别证据图片")
|
||||
public class HardOrderEvidenceImageEntity extends Model<HardOrderEvidenceImageEntity> {
|
||||
|
||||
/**
|
||||
* 证据记录自增主键。
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "证据记录主键")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 服务端订单主键,来源于 hard_order.id。
|
||||
*/
|
||||
@Schema(description = "订单主键")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 证据订单引用快照;现场订单取终端订单号,预约等服务端订单取订单号或服务端主键内部引用。
|
||||
*/
|
||||
@Schema(description = "证据订单引用快照")
|
||||
private String androidOrderCode;
|
||||
|
||||
/**
|
||||
* 证据业务类型,保存 HardOrderEvidenceTypeEnum.code。
|
||||
*/
|
||||
@Schema(description = "证据类型")
|
||||
private String evidenceType;
|
||||
|
||||
/**
|
||||
* 图片角色,保存 HardOrderEvidenceRoleEnum.code。
|
||||
*/
|
||||
@Schema(description = "证据图片角色")
|
||||
private String evidenceRole;
|
||||
|
||||
/**
|
||||
* 同一订单、类型和角色下的图片序号,从0开始。
|
||||
*/
|
||||
@Schema(description = "证据图片序号")
|
||||
private Integer sequenceNo;
|
||||
|
||||
/**
|
||||
* 识别用户主键快照;无法识别到平台用户时允许为空。
|
||||
*/
|
||||
@Schema(description = "识别用户主键")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 虹软人脸样本编号快照,来源于 sys_user_face.auto_code。
|
||||
*/
|
||||
@Schema(description = "人脸样本编号")
|
||||
private String faceSampleId;
|
||||
|
||||
/**
|
||||
* 识别时终端命中的后台参考图片地址快照,仅用于与 UPMS 权威记录比对,不直接作为展示地址。
|
||||
*/
|
||||
@Schema(description = "参考人脸来源地址快照")
|
||||
private String referenceImageUrl;
|
||||
|
||||
/**
|
||||
* 终端识别相似度快照,取值0到1。
|
||||
*/
|
||||
@Schema(description = "识别相似度")
|
||||
private BigDecimal similarity;
|
||||
|
||||
/**
|
||||
* 终端识别相似度阈值快照,取值0到1。
|
||||
*/
|
||||
@Schema(description = "识别相似度阈值")
|
||||
private BigDecimal similarityThreshold;
|
||||
|
||||
/**
|
||||
* 产生证据的服务端设备主键,来源于 hard_order.hard_id。
|
||||
*/
|
||||
@Schema(description = "设备主键")
|
||||
private Long hardId;
|
||||
|
||||
/**
|
||||
* 终端实际完成识别的本地时间,用于与订单时间交叉核验。
|
||||
*/
|
||||
@Schema(description = "终端采集时间")
|
||||
private LocalDateTime capturedAt;
|
||||
|
||||
/**
|
||||
* 文件中心 sys_file 主键;状态非 AVAILABLE 时允许为空。
|
||||
*/
|
||||
@Schema(description = "文件中心记录主键")
|
||||
private Long sysFileId;
|
||||
|
||||
/**
|
||||
* 文件中心对象名,仅供服务端读取或下载,不由终端提交。
|
||||
*/
|
||||
@Schema(description = "文件中心对象名")
|
||||
private String storageFileName;
|
||||
|
||||
/**
|
||||
* PC 可使用统一请求层访问的文件相对地址。
|
||||
*/
|
||||
@Schema(description = "图片访问地址")
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 实际保存文件的 SHA-256,用于幂等和防止证据被静默替换。
|
||||
*/
|
||||
@Schema(description = "图片SHA-256")
|
||||
private String imageHash;
|
||||
|
||||
/**
|
||||
* 实际保存文件大小,单位字节。
|
||||
*/
|
||||
@Schema(description = "图片大小,单位字节")
|
||||
private Long imageSize;
|
||||
|
||||
/**
|
||||
* 实际解码图片宽度,单位像素。
|
||||
*/
|
||||
@Schema(description = "图片宽度,单位像素")
|
||||
private Integer imageWidth;
|
||||
|
||||
/**
|
||||
* 实际解码图片高度,单位像素。
|
||||
*/
|
||||
@Schema(description = "图片高度,单位像素")
|
||||
private Integer imageHeight;
|
||||
|
||||
/**
|
||||
* 证据状态,保存 HardOrderEvidenceStatusEnum.code。
|
||||
*/
|
||||
@Schema(description = "证据状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 永久缺失或服务端复制失败的摘要,不保存终端完整文件路径。
|
||||
*/
|
||||
@Schema(description = "失败原因")
|
||||
private String failureReason;
|
||||
|
||||
/**
|
||||
* 产生证据的终端应用版本快照。
|
||||
*/
|
||||
@Schema(description = "终端应用版本")
|
||||
private String appVersion;
|
||||
|
||||
/**
|
||||
* 创建人,由 MyBatis 填充器写入。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 证据占位记录创建时间,由 MyBatis 填充器写入。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人,由 MyBatis 填充器在状态变化时写入。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 最近一次状态、文件或错误信息更新时间。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除标记;订单退款、取消和删除不会联动改变该值。
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 租户主键,由当前请求租户上下文写入并由数据层自动隔离。
|
||||
*/
|
||||
@Schema(description = "租户主键")
|
||||
private Long tenantId;
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.bsd.pjcloud.hard.api.entity.xfj;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 消费机日志
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("hard_xfj_log")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "消费机日志")
|
||||
public class HardXfjLogEntity extends Model<HardXfjLogEntity> {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "编号")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备唯一编号
|
||||
*/
|
||||
@Schema(description = "设备唯一编号")
|
||||
private String uniqueCode;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
@Schema(description = "设备类型")
|
||||
private String hardType;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@Schema(description = "用户id")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* headers
|
||||
*/
|
||||
@Schema(description = "headers")
|
||||
private String headers;
|
||||
|
||||
/**
|
||||
* res
|
||||
*/
|
||||
@Schema(description = "res")
|
||||
private String res;
|
||||
|
||||
/**
|
||||
* req
|
||||
*/
|
||||
@Schema(description = "req")
|
||||
private String req;
|
||||
|
||||
/**
|
||||
* error
|
||||
*/
|
||||
@Schema(description = "error")
|
||||
private String error;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
@Schema(description = "开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
@Schema(description = "结束时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 耗时(毫秒)
|
||||
*/
|
||||
@Schema(description = "耗时(毫秒)")
|
||||
private Long cost;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+565
@@ -0,0 +1,565 @@
|
||||
package com.bsd.pjcloud.hard.api.utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 面向 80mm、576px ESC/POS 打印的动态高度 PNG 绘制器。
|
||||
*
|
||||
* <p>商品名称按真实像素宽度换行,数量列固定右对齐,保证长名称不会与数量错配。</p>
|
||||
*/
|
||||
public class ThermalReceiptImage {
|
||||
|
||||
/** 80mm 打印头常见有效像素宽度。 */
|
||||
public static final int PAPER_WIDTH_PIXELS = 576;
|
||||
|
||||
/** 图片左右安全留白。 */
|
||||
private static final int HORIZONTAL_PADDING = 24;
|
||||
|
||||
/** 图片顶部和底部安全留白。 */
|
||||
private static final int VERTICAL_PADDING = 24;
|
||||
|
||||
/** 每次扩展画布的最小高度。 */
|
||||
private static final int HEIGHT_GROWTH = 512;
|
||||
|
||||
/** 数量列保留宽度。 */
|
||||
private static final int QUANTITY_COLUMN_WIDTH = 96;
|
||||
|
||||
/** 套餐组成项左侧缩进。 */
|
||||
private static final int CHILD_INDENT = 32;
|
||||
|
||||
/** 兑换券商品卡片的内容内边距。 */
|
||||
private static final int PRODUCT_CARD_PADDING = 14;
|
||||
|
||||
/** 相邻兑换券商品卡片的垂直间距。 */
|
||||
private static final int PRODUCT_CARD_GAP = 14;
|
||||
|
||||
/** 中文优先逻辑字体名称。 */
|
||||
private static final String FONT_FAMILY = "SansSerif";
|
||||
|
||||
/** 当前可扩展画布。 */
|
||||
private BufferedImage canvas;
|
||||
|
||||
/** 当前绘制上下文。 */
|
||||
private Graphics2D graphics;
|
||||
|
||||
/** 下一块内容的顶部 Y 坐标。 */
|
||||
private int cursorY = VERTICAL_PADDING;
|
||||
|
||||
/**
|
||||
* 创建 576px 宽的白底收据画布。
|
||||
*/
|
||||
public ThermalReceiptImage() {
|
||||
canvas = new BufferedImage(PAPER_WIDTH_PIXELS, HEIGHT_GROWTH, BufferedImage.TYPE_INT_RGB);
|
||||
graphics = canvas.createGraphics();
|
||||
configureGraphics(graphics);
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
graphics.setColor(Color.BLACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制居中粗体标题。
|
||||
*
|
||||
* @param text 标题文本
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage title(String text) {
|
||||
setFont(Font.BOLD, 42);
|
||||
drawCentered(text, 16);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制带黑色边框的大号打印次数提示,文字超过可用纸宽时自动换行。
|
||||
*
|
||||
* @param text 提示文本,例如“第 1 次打印”
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage banner(String text) {
|
||||
setFont(Font.BOLD, 44);
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
List<String> lines = wrap(text == null ? "" : text, contentWidth() - 32, metrics);
|
||||
int lineHeight = metrics.getHeight() + 4;
|
||||
int height = lines.size() * lineHeight + 26;
|
||||
ensureCapacity(height + 20);
|
||||
graphics.setStroke(new BasicStroke(3F));
|
||||
graphics.drawRect(HORIZONTAL_PADDING, cursorY, contentWidth(), height);
|
||||
for (int index = 0; index < lines.size(); index++) {
|
||||
String line = lines.get(index);
|
||||
int x = HORIZONTAL_PADDING + Math.max(0, (contentWidth() - metrics.stringWidth(line)) / 2);
|
||||
graphics.drawString(line, x, cursorY + 13 + index * lineHeight + metrics.getAscent());
|
||||
}
|
||||
cursorY += height + 20;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制醒目的核销短码。
|
||||
*
|
||||
* @param shortCode 核销短码
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage verificationCode(String shortCode) {
|
||||
setFont(Font.BOLD, 64);
|
||||
drawCentered(shortCode == null || shortCode.isBlank() ? "--" : shortCode, 18);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制一行左右键值信息,值无法完整同行显示时整段从下一行开始并继续换行。
|
||||
*
|
||||
* @param label 左侧字段名
|
||||
* @param value 右侧字段值
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage keyValue(String label, String value) {
|
||||
setFont(Font.PLAIN, 33);
|
||||
String safeLabel = label == null ? "" : label;
|
||||
String safeValue = value == null || value.isBlank() ? "--" : value;
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
int labelWidth = metrics.stringWidth(safeLabel + ":");
|
||||
int inlineValueWidth = contentWidth() - labelWidth - 12;
|
||||
boolean stackValue = metrics.stringWidth(safeValue) > inlineValueWidth;
|
||||
int valueWidth = stackValue ? contentWidth() : Math.max(120, inlineValueWidth);
|
||||
List<String> lines = wrap(safeValue, valueWidth, metrics);
|
||||
int valueLineOffset = stackValue ? 1 : 0;
|
||||
int lineHeight = metrics.getHeight() + 6;
|
||||
ensureCapacity((lines.size() + valueLineOffset) * lineHeight + 10);
|
||||
graphics.drawString(safeLabel + ":", HORIZONTAL_PADDING, cursorY + metrics.getAscent());
|
||||
for (int index = 0; index < lines.size(); index++) {
|
||||
int x = stackValue ? HORIZONTAL_PADDING : HORIZONTAL_PADDING + labelWidth + 12;
|
||||
graphics.drawString(lines.get(index), x,
|
||||
cursorY + (index + valueLineOffset) * lineHeight + metrics.getAscent());
|
||||
}
|
||||
cursorY += (lines.size() + valueLineOffset) * lineHeight + 6;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制商品明细区标题。
|
||||
*
|
||||
* @param text 标题文本
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage sectionTitle(String text) {
|
||||
setFont(Font.BOLD, 36);
|
||||
ensureCapacity(60);
|
||||
graphics.drawString(text == null ? "" : text, HORIZONTAL_PADDING, cursorY + graphics.getFontMetrics().getAscent());
|
||||
cursorY += graphics.getFontMetrics().getHeight() + 10;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制多订单合并兑换券中的紧凑订单摘要。
|
||||
*
|
||||
* <p>订单序号使用黑底标签形成扫描锚点,完整订单号及日期餐段均按实际像素宽度换行;相邻订单仅以
|
||||
* 细线分隔,避免重复商品版式中的大段标题和留白。</p>
|
||||
*
|
||||
* @param index 当前订单在合并批次中的一号起始序号
|
||||
* @param total 合并批次订单总数
|
||||
* @param orderNo 对外展示的完整订单号
|
||||
* @param orderDate 预约订餐日期
|
||||
* @param mealName 餐段名称
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage voucherOrderSummary(int index, int total, String orderNo,
|
||||
String orderDate, String mealName) {
|
||||
int safeTotal = Math.max(1, total);
|
||||
int safeIndex = Math.min(Math.max(1, index), safeTotal);
|
||||
String tag = "订单 " + safeIndex + "/" + safeTotal;
|
||||
String safeOrderNo = orderNo == null || orderNo.isBlank() ? "--" : orderNo;
|
||||
String summary = "订餐日期:" + (orderDate == null || orderDate.isBlank() ? "--" : orderDate)
|
||||
+ " 餐段:" + (mealName == null || mealName.isBlank() ? "--" : mealName);
|
||||
|
||||
setFont(Font.BOLD, 25);
|
||||
FontMetrics tagMetrics = graphics.getFontMetrics();
|
||||
int tagWidth = tagMetrics.stringWidth(tag) + 20;
|
||||
int tagHeight = tagMetrics.getHeight() + 6;
|
||||
|
||||
setFont(Font.BOLD, 31);
|
||||
FontMetrics orderMetrics = graphics.getFontMetrics();
|
||||
int orderX = HORIZONTAL_PADDING + tagWidth + 14;
|
||||
int orderWidth = PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - orderX;
|
||||
List<String> orderLines = wrap(safeOrderNo, Math.max(120, orderWidth), orderMetrics);
|
||||
int orderLineHeight = orderMetrics.getHeight() + 4;
|
||||
int headerHeight = Math.max(tagHeight, orderLines.size() * orderLineHeight);
|
||||
|
||||
setFont(Font.PLAIN, 29);
|
||||
FontMetrics summaryMetrics = graphics.getFontMetrics();
|
||||
List<String> summaryLines = wrap(summary, contentWidth(), summaryMetrics);
|
||||
int summaryLineHeight = summaryMetrics.getHeight() + 4;
|
||||
int separatorHeight = safeIndex < safeTotal ? 18 : 8;
|
||||
int blockHeight = headerHeight + 10 + summaryLines.size() * summaryLineHeight + separatorHeight;
|
||||
ensureCapacity(blockHeight);
|
||||
|
||||
int blockTop = cursorY;
|
||||
graphics.setColor(Color.BLACK);
|
||||
setFont(Font.BOLD, 25);
|
||||
graphics.fillRect(HORIZONTAL_PADDING, blockTop, tagWidth, tagHeight);
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.drawString(tag, HORIZONTAL_PADDING + 10, blockTop + 3 + tagMetrics.getAscent());
|
||||
graphics.setColor(Color.BLACK);
|
||||
|
||||
setFont(Font.BOLD, 31);
|
||||
for (int lineIndex = 0; lineIndex < orderLines.size(); lineIndex++) {
|
||||
graphics.drawString(orderLines.get(lineIndex), orderX,
|
||||
blockTop + lineIndex * orderLineHeight + orderMetrics.getAscent());
|
||||
}
|
||||
|
||||
int summaryTop = blockTop + headerHeight + 10;
|
||||
setFont(Font.PLAIN, 29);
|
||||
for (int lineIndex = 0; lineIndex < summaryLines.size(); lineIndex++) {
|
||||
graphics.drawString(summaryLines.get(lineIndex), HORIZONTAL_PADDING,
|
||||
summaryTop + lineIndex * summaryLineHeight + summaryMetrics.getAscent());
|
||||
}
|
||||
if (safeIndex < safeTotal) {
|
||||
int separatorY = summaryTop + summaryLines.size() * summaryLineHeight + 7;
|
||||
graphics.setStroke(new BasicStroke(1F));
|
||||
graphics.drawLine(HORIZONTAL_PADDING, separatorY,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING, separatorY);
|
||||
}
|
||||
cursorY += blockHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制商品或套餐组成项,商品名换行且数量始终在首行右对齐。
|
||||
*
|
||||
* @param name 商品名称
|
||||
* @param quantity 数量文本
|
||||
* @param child 是否为套餐组成项
|
||||
* @param emphasized 是否使用粗体标记套餐主项
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage item(String name, String quantity, boolean child, boolean emphasized) {
|
||||
return item(name, quantity, child, emphasized, QUANTITY_COLUMN_WIDTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制带数量和金额摘要的发票商品行。
|
||||
*
|
||||
* @param name 商品名称
|
||||
* @param quantity 数量或重量文本
|
||||
* @param amount 含货币符号的商品总价
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage invoiceItem(String name, String quantity, String amount) {
|
||||
return item(name, String.format("%s %s", quantity, amount), false, false, 190);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制兑换券商品卡片,使用文字标签和边框粗细同时区分组合商品与单品。
|
||||
*
|
||||
* <p>商品名称和组合内容均按真实像素宽度完整换行;价格区固定展示单价、数量和小计。
|
||||
* 组合内容只解释套餐构成,不参与当前卡片金额计算。</p>
|
||||
*
|
||||
* @param name 商品快照名称;空值显示“未命名商品”
|
||||
* @param quantity 部分退款后的剩余可履约数量
|
||||
* @param unitPrice 商品快照单价,单位元且不含货币符号
|
||||
* @param subtotal 剩余可履约数量对应的小计,单位元且不含货币符号
|
||||
* @param combo 是否为组合商品
|
||||
* @param componentItems 组合商品组成说明,每项应已包含对应数量;单品或无组成时可为空
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage voucherProduct(String name, int quantity, String unitPrice, String subtotal,
|
||||
boolean combo, List<String> componentItems) {
|
||||
String typeLabel = combo ? "组合商品" : "单品";
|
||||
String safeName = name == null || name.isBlank() ? "未命名商品" : name;
|
||||
String safeUnitPrice = unitPrice == null || unitPrice.isBlank() ? "--" : unitPrice;
|
||||
String safeSubtotal = subtotal == null || subtotal.isBlank() ? "--" : subtotal;
|
||||
List<String> safeComponents = componentItems == null ? List.of() : componentItems.stream()
|
||||
.filter(item -> item != null && !item.isBlank()).toList();
|
||||
int innerWidth = contentWidth() - PRODUCT_CARD_PADDING * 2;
|
||||
|
||||
setFont(Font.BOLD, 28);
|
||||
FontMetrics tagMetrics = graphics.getFontMetrics();
|
||||
int tagHeight = tagMetrics.getHeight() + 6;
|
||||
int tagWidth = tagMetrics.stringWidth(typeLabel) + 20;
|
||||
|
||||
setFont(Font.BOLD, 40);
|
||||
FontMetrics nameMetrics = graphics.getFontMetrics();
|
||||
List<String> nameLines = wrap(safeName, innerWidth, nameMetrics);
|
||||
int nameLineHeight = nameMetrics.getHeight() + 4;
|
||||
|
||||
setFont(Font.PLAIN, 29);
|
||||
FontMetrics priceMetrics = graphics.getFontMetrics();
|
||||
String priceAndQuantity = "单价 ¥" + safeUnitPrice + " x" + Math.max(0, quantity);
|
||||
String subtotalText = "小计 ¥" + safeSubtotal;
|
||||
boolean splitPriceLine = priceMetrics.stringWidth(priceAndQuantity)
|
||||
+ priceMetrics.stringWidth(subtotalText) + 24 > innerWidth;
|
||||
int priceLineHeight = priceMetrics.getHeight() + 4;
|
||||
|
||||
setFont(Font.PLAIN, 29);
|
||||
FontMetrics componentMetrics = graphics.getFontMetrics();
|
||||
int componentLineHeight = componentMetrics.getHeight() + 3;
|
||||
List<List<String>> componentLines = new ArrayList<>();
|
||||
for (String component : safeComponents) {
|
||||
componentLines.add(wrap("- " + component, innerWidth - CHILD_INDENT, componentMetrics));
|
||||
}
|
||||
int componentHeight = componentLines.stream().mapToInt(List::size).sum() * componentLineHeight;
|
||||
int comboSectionHeight = combo && !componentLines.isEmpty()
|
||||
? 18 + componentLineHeight + componentHeight : 0;
|
||||
int cardHeight = PRODUCT_CARD_PADDING + tagHeight + 10 + nameLines.size() * nameLineHeight + 8
|
||||
+ (splitPriceLine ? priceLineHeight * 2 : priceLineHeight) + comboSectionHeight
|
||||
+ PRODUCT_CARD_PADDING;
|
||||
ensureCapacity(cardHeight + PRODUCT_CARD_GAP);
|
||||
|
||||
int cardTop = cursorY;
|
||||
int contentX = HORIZONTAL_PADDING + PRODUCT_CARD_PADDING;
|
||||
int contentY = cardTop + PRODUCT_CARD_PADDING;
|
||||
graphics.setColor(Color.BLACK);
|
||||
graphics.setStroke(new BasicStroke(combo ? 4F : 2F));
|
||||
graphics.drawRect(HORIZONTAL_PADDING, cardTop, contentWidth(), cardHeight);
|
||||
|
||||
setFont(Font.BOLD, 28);
|
||||
if (combo) {
|
||||
graphics.fillRect(contentX, contentY, tagWidth, tagHeight);
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.drawString(typeLabel, contentX + 10, contentY + 3 + tagMetrics.getAscent());
|
||||
graphics.setColor(Color.BLACK);
|
||||
} else {
|
||||
graphics.setStroke(new BasicStroke(2F));
|
||||
graphics.drawRect(contentX, contentY, tagWidth, tagHeight);
|
||||
graphics.drawString(typeLabel, contentX + 10, contentY + 3 + tagMetrics.getAscent());
|
||||
}
|
||||
contentY += tagHeight + 10;
|
||||
|
||||
setFont(Font.BOLD, 40);
|
||||
for (String line : nameLines) {
|
||||
graphics.drawString(line, contentX, contentY + nameMetrics.getAscent());
|
||||
contentY += nameLineHeight;
|
||||
}
|
||||
contentY += 8;
|
||||
|
||||
setFont(Font.PLAIN, 29);
|
||||
graphics.drawString(priceAndQuantity, contentX, contentY + priceMetrics.getAscent());
|
||||
if (splitPriceLine) {
|
||||
contentY += priceLineHeight;
|
||||
}
|
||||
graphics.drawString(subtotalText,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - PRODUCT_CARD_PADDING
|
||||
- priceMetrics.stringWidth(subtotalText),
|
||||
contentY + priceMetrics.getAscent());
|
||||
contentY += priceLineHeight;
|
||||
|
||||
if (combo && !componentLines.isEmpty()) {
|
||||
contentY += 8;
|
||||
graphics.setStroke(new BasicStroke(1F));
|
||||
graphics.drawLine(contentX, contentY,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - PRODUCT_CARD_PADDING, contentY);
|
||||
contentY += 10;
|
||||
setFont(Font.BOLD, 29);
|
||||
graphics.drawString("组合内容", contentX, contentY + componentMetrics.getAscent());
|
||||
contentY += componentLineHeight;
|
||||
setFont(Font.PLAIN, 29);
|
||||
for (List<String> lines : componentLines) {
|
||||
for (String line : lines) {
|
||||
graphics.drawString(line, contentX + CHILD_INDENT,
|
||||
contentY + componentMetrics.getAscent());
|
||||
contentY += componentLineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
cursorY += cardHeight + PRODUCT_CARD_GAP;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制兑换券商品合计,使用粗分隔线和大号金额形成票据金额层级。
|
||||
*
|
||||
* @param totalAmount 剩余可履约商品合计,单位元且不含货币符号
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage voucherTotal(String totalAmount) {
|
||||
String safeAmount = totalAmount == null || totalAmount.isBlank() ? "--" : totalAmount;
|
||||
String label = "商品合计";
|
||||
String amount = "¥" + safeAmount;
|
||||
setFont(Font.BOLD, 42);
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
int blockHeight = metrics.getHeight() + 30;
|
||||
ensureCapacity(blockHeight + 14);
|
||||
graphics.setColor(Color.BLACK);
|
||||
graphics.setStroke(new BasicStroke(3F));
|
||||
graphics.drawLine(HORIZONTAL_PADDING, cursorY,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING, cursorY);
|
||||
int baseline = cursorY + 15 + metrics.getAscent();
|
||||
graphics.drawString(label, HORIZONTAL_PADDING, baseline);
|
||||
graphics.drawString(amount, PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - metrics.stringWidth(amount), baseline);
|
||||
graphics.drawLine(HORIZONTAL_PADDING, cursorY + blockHeight,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING, cursorY + blockHeight);
|
||||
cursorY += blockHeight + 14;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定尾列宽度绘制一条可换行明细。
|
||||
*
|
||||
* @param name 左侧名称
|
||||
* @param trailing 首行右对齐摘要
|
||||
* @param child 是否为套餐组成项
|
||||
* @param emphasized 是否使用粗体
|
||||
* @param trailingWidth 尾列预留像素宽度
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
private ThermalReceiptImage item(String name, String trailing, boolean child, boolean emphasized,
|
||||
int trailingWidth) {
|
||||
setFont(emphasized ? Font.BOLD : Font.PLAIN, child ? 30 : 34);
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
int indent = child ? CHILD_INDENT : 0;
|
||||
String prefix = child ? "- " : "";
|
||||
String safeName = prefix + (name == null || name.isBlank() ? "未命名商品" : name);
|
||||
List<String> lines = wrap(safeName, contentWidth() - indent - trailingWidth, metrics);
|
||||
int lineHeight = metrics.getHeight() + 5;
|
||||
ensureCapacity(lines.size() * lineHeight + 8);
|
||||
for (int index = 0; index < lines.size(); index++) {
|
||||
graphics.drawString(lines.get(index), HORIZONTAL_PADDING + indent,
|
||||
cursorY + index * lineHeight + metrics.getAscent());
|
||||
}
|
||||
String safeTrailing = trailing == null || trailing.isBlank() ? "x0" : trailing;
|
||||
graphics.drawString(safeTrailing, PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - metrics.stringWidth(safeTrailing),
|
||||
cursorY + metrics.getAscent());
|
||||
cursorY += lines.size() * lineHeight + 6;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制横向分隔线。
|
||||
*
|
||||
* @return 当前绘制器
|
||||
*/
|
||||
public ThermalReceiptImage divider() {
|
||||
ensureCapacity(28);
|
||||
graphics.setStroke(new BasicStroke(2F));
|
||||
graphics.drawLine(HORIZONTAL_PADDING, cursorY + 10,
|
||||
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING, cursorY + 10);
|
||||
cursorY += 28;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出裁剪到实际高度的 PNG 字节。
|
||||
*
|
||||
* @return PNG 字节
|
||||
* @throws IOException PNG 编码失败
|
||||
*/
|
||||
public byte[] toPngBytes() throws IOException {
|
||||
int finalHeight = cursorY + VERTICAL_PADDING;
|
||||
BufferedImage result = new BufferedImage(PAPER_WIDTH_PIXELS, finalHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D resultGraphics = result.createGraphics();
|
||||
resultGraphics.setColor(Color.WHITE);
|
||||
resultGraphics.fillRect(0, 0, result.getWidth(), result.getHeight());
|
||||
resultGraphics.drawImage(canvas, 0, 0, null);
|
||||
resultGraphics.dispose();
|
||||
graphics.dispose();
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
ImageIO.write(result, "png", output);
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前字体。
|
||||
*
|
||||
* @param style 字体样式
|
||||
* @param size 字体像素大小
|
||||
*/
|
||||
private void setFont(int style, int size) {
|
||||
graphics.setFont(new Font(FONT_FAMILY, style, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制一行居中文本并推进游标。
|
||||
*
|
||||
* @param text 文本
|
||||
* @param bottomSpace 底部留白
|
||||
*/
|
||||
private void drawCentered(String text, int bottomSpace) {
|
||||
String safeText = text == null ? "" : text;
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
ensureCapacity(metrics.getHeight() + bottomSpace);
|
||||
int x = Math.max(HORIZONTAL_PADDING,
|
||||
(PAPER_WIDTH_PIXELS - metrics.stringWidth(safeText)) / 2);
|
||||
graphics.drawString(safeText, x, cursorY + metrics.getAscent());
|
||||
cursorY += metrics.getHeight() + bottomSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按实际字体宽度拆分字符串,单个超宽字符仍至少独占一行。
|
||||
*
|
||||
* @param text 原文本
|
||||
* @param maxWidth 最大像素宽度
|
||||
* @param metrics 当前字体度量
|
||||
* @return 非空行列表
|
||||
*/
|
||||
private List<String> wrap(String text, int maxWidth, FontMetrics metrics) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
StringBuilder line = new StringBuilder();
|
||||
String safeText = text == null ? "" : text;
|
||||
for (int offset = 0; offset < safeText.length();) {
|
||||
int codePoint = safeText.codePointAt(offset);
|
||||
String character = new String(Character.toChars(codePoint));
|
||||
if (line.length() > 0 && metrics.stringWidth(line + character) > maxWidth) {
|
||||
lines.add(line.toString());
|
||||
line.setLength(0);
|
||||
}
|
||||
line.append(character);
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
if (line.length() > 0 || lines.isEmpty()) lines.add(line.toString());
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保画布能够容纳下一块内容,并在扩容时保留字体、画笔和已绘制像素。
|
||||
*
|
||||
* @param requiredHeight 下一块内容需要的高度
|
||||
*/
|
||||
private void ensureCapacity(int requiredHeight) {
|
||||
if (cursorY + requiredHeight <= canvas.getHeight()) return;
|
||||
int newHeight = canvas.getHeight();
|
||||
while (cursorY + requiredHeight > newHeight) newHeight += HEIGHT_GROWTH;
|
||||
Font font = graphics.getFont();
|
||||
Color color = graphics.getColor();
|
||||
BufferedImage expanded = new BufferedImage(PAPER_WIDTH_PIXELS, newHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D expandedGraphics = expanded.createGraphics();
|
||||
configureGraphics(expandedGraphics);
|
||||
expandedGraphics.setColor(Color.WHITE);
|
||||
expandedGraphics.fillRect(0, 0, expanded.getWidth(), expanded.getHeight());
|
||||
expandedGraphics.drawImage(canvas, 0, 0, null);
|
||||
expandedGraphics.setFont(font);
|
||||
expandedGraphics.setColor(color);
|
||||
graphics.dispose();
|
||||
canvas = expanded;
|
||||
graphics = expandedGraphics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置文字抗锯齿和高质量图片渲染参数。
|
||||
*
|
||||
* @param target 待配置绘制上下文
|
||||
*/
|
||||
private static void configureGraphics(Graphics2D target) {
|
||||
target.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
target.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算扣除左右安全留白后的有效宽度。
|
||||
*
|
||||
* @return 内容宽度
|
||||
*/
|
||||
private static int contentWidth() {
|
||||
return PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING * 2;
|
||||
}
|
||||
}
|
||||
+5
@@ -63,6 +63,11 @@ public class HardInvoiceVo extends HardInvoiceEntity implements BaseDeptInterfac
|
||||
|
||||
private String hardType;
|
||||
|
||||
/**
|
||||
* 任务绑定的尔印 USB 打印机资产名称。
|
||||
*/
|
||||
private String printerHardName;
|
||||
|
||||
/**
|
||||
* 用户相关
|
||||
*/
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.bsd.pjcloud.hard.api.vo;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.entity.HardPrinterBindingEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.HardPrinterConfigEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 业务终端与无屏 USB 打印机绑定关系及展示信息。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "USB 打印机绑定详情")
|
||||
public class HardPrinterBindingVo extends HardPrinterBindingEntity {
|
||||
|
||||
/** 来源业务终端名称。 */
|
||||
@Schema(description = "来源业务终端名称")
|
||||
private String sourceHardName;
|
||||
|
||||
/** 来源业务终端类型。 */
|
||||
@Schema(description = "来源业务终端类型")
|
||||
private String sourceHardType;
|
||||
|
||||
/** 逻辑打印机资产名称。 */
|
||||
@Schema(description = "逻辑打印机资产名称")
|
||||
private String printerHardName;
|
||||
|
||||
/** 目标 USB 打印机当前配置;尚未保存时返回默认配置。 */
|
||||
@Schema(description = "USB 打印机配置")
|
||||
private HardPrinterConfigEntity printerConfig;
|
||||
}
|
||||
+60
@@ -6,6 +6,11 @@ import com.bsd.pjcloud.hard.api.vo.base.BaseHardGroupInterface;
|
||||
import com.bsd.pjcloud.wallet.api.vo.base.BaseMchInterface;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备管理视图,包含设备基础资料及仅用于接口返回的关联、运行状态信息。
|
||||
*/
|
||||
@Data
|
||||
public class HardManagerVo extends HardManagerEntity implements BaseDeptInterface, BaseMchInterface, BaseHardGroupInterface {
|
||||
|
||||
@@ -29,4 +34,59 @@ public class HardManagerVo extends HardManagerEntity implements BaseDeptInterfac
|
||||
|
||||
private String hardGroupName;
|
||||
|
||||
/**
|
||||
* 终端是否在线;超市、食堂设备返回布尔值,其他设备类型返回 {@code null} 表示暂不支持。
|
||||
*/
|
||||
private Boolean online;
|
||||
|
||||
/**
|
||||
* 服务端最近收到终端心跳的时间;尚未上报或设备类型不支持时为空。
|
||||
*/
|
||||
private LocalDateTime lastOnlineTime;
|
||||
|
||||
/**
|
||||
* 当前 APK 展示版本号,来源于终端最近一次心跳;未上报时为空。
|
||||
*/
|
||||
private String apkVersionName;
|
||||
|
||||
/**
|
||||
* 当前 APK 内部版本码,来源于终端最近一次心跳;未上报时为空。
|
||||
*/
|
||||
private Long apkVersionCode;
|
||||
|
||||
/**
|
||||
* 断开连接的可解释原因;除主动自动更新外均为基于心跳超时的推断结果。
|
||||
*/
|
||||
private String disconnectReason;
|
||||
|
||||
/** 当前来源业务终端所绑定的 EY_PRINT 打印机资产 ID;未绑定或当前行为打印机时为空。 */
|
||||
private Long boundPrinterHardId;
|
||||
|
||||
/** 当前来源业务终端所绑定的 EY_PRINT 设备名称。 */
|
||||
private String boundPrinterHardName;
|
||||
|
||||
/** 当前 EY_PRINT 打印机资产反向绑定的来源业务终端 ID;未绑定或当前行为来源设备时为空。 */
|
||||
private Long boundSourceHardId;
|
||||
|
||||
/** 当前 EY_PRINT 打印机资产反向绑定的来源业务终端名称。 */
|
||||
private String boundSourceHardName;
|
||||
|
||||
/** EY_PRINT 型号;未维护配置或非打印设备时为空。 */
|
||||
private String printerModel;
|
||||
|
||||
/** EY_PRINT 的 USB 厂商编号 VID;尚未真机回填时为空。 */
|
||||
private Integer usbVendorId;
|
||||
|
||||
/** EY_PRINT 的 USB 产品编号 PID;尚未真机回填时为空。 */
|
||||
private Integer usbProductId;
|
||||
|
||||
/** 热敏纸宽度,首期固定为 80mm。 */
|
||||
private Integer paperWidth;
|
||||
|
||||
/** 自动切刀开关,1 开启、0 关闭。 */
|
||||
private String autoCut;
|
||||
|
||||
/** 图片指令模式,首选 GS_V0,兼容 ESC_STAR。 */
|
||||
private String imageMode;
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 终端日志设备聚合摘要。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端日志设备摘要")
|
||||
public class HardTerminalLogDeviceVo {
|
||||
|
||||
private Long hardId;
|
||||
private String hardName;
|
||||
private String uniqueCode;
|
||||
private String hardType;
|
||||
private Long merchantId;
|
||||
private Long deptId;
|
||||
private Long fileCount;
|
||||
private LocalDateTime latestUploadTime;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 终端日志文件树节点。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "终端日志文件树节点")
|
||||
public class HardTerminalLogFileTreeVo {
|
||||
|
||||
private String key;
|
||||
private String label;
|
||||
private String relativePath;
|
||||
private Boolean directory;
|
||||
private Long fileId;
|
||||
private Long fileSize;
|
||||
private LocalDate logDate;
|
||||
private LocalDateTime uploadTime;
|
||||
private List<HardTerminalLogFileTreeVo> children = new ArrayList<>();
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 终端日志上传预检结果。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "终端日志上传预检结果")
|
||||
public class HardTerminalLogPreflightVo {
|
||||
|
||||
private Long id;
|
||||
private Boolean needUpload;
|
||||
private String normalizedPath;
|
||||
private String message;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 终端日志尾部预览。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "终端日志预览")
|
||||
public class HardTerminalLogPreviewVo {
|
||||
|
||||
private Long id;
|
||||
private String originalName;
|
||||
private String relativePath;
|
||||
private String content;
|
||||
private Integer lineCount;
|
||||
private Long fileSize;
|
||||
private Boolean truncated;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.manager;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 终端日志上传结果。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "终端日志上传结果")
|
||||
public class HardTerminalLogUploadResultVo {
|
||||
|
||||
private Long id;
|
||||
private Boolean uploaded;
|
||||
private Boolean changed;
|
||||
private String relativePath;
|
||||
private Integer uploadCount;
|
||||
private String message;
|
||||
}
|
||||
+10
@@ -21,4 +21,14 @@ public class HardCarVo extends HardCarEntity {
|
||||
// 购买次数【统计使用】
|
||||
private Long payCount;
|
||||
|
||||
/**
|
||||
* 已完成退款份数,只统计售后状态为完成的商品明细;空响应按零处理。
|
||||
*/
|
||||
private Integer refundedProductCount;
|
||||
|
||||
/**
|
||||
* 当前剩余可履约份数,计算规则为原购份数减已完成退款份数,最小为零。
|
||||
*/
|
||||
private Integer effectiveProductCount;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.order;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEvidenceImageEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* PC 订单详情使用的订单证据图片,附带稳定编码对应的中文名称。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "订单识别证据图片详情")
|
||||
public class HardOrderEvidenceImageVo extends HardOrderEvidenceImageEntity {
|
||||
|
||||
/**
|
||||
* 证据类型中文名称,未知历史编码时回退原编码。
|
||||
*/
|
||||
@Schema(description = "证据类型名称")
|
||||
private String evidenceTypeName;
|
||||
|
||||
/**
|
||||
* 图片角色中文名称,未知历史编码时回退原编码。
|
||||
*/
|
||||
@Schema(description = "证据图片角色名称")
|
||||
private String evidenceRoleName;
|
||||
|
||||
/**
|
||||
* 证据状态中文名称,未知历史编码时回退原编码。
|
||||
*/
|
||||
@Schema(description = "证据状态名称")
|
||||
private String statusName;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.order;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 终端按本地订单号对账时返回的服务端订单与证据快照。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "订单证据对账结果")
|
||||
public class HardOrderEvidenceResolveVo {
|
||||
|
||||
/**
|
||||
* 服务端订单主键。
|
||||
*/
|
||||
@Schema(description = "服务端订单主键")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 服务端订单状态,终端仅在现有支付流程确认成功后上传证据。
|
||||
*/
|
||||
@Schema(description = "订单状态")
|
||||
private String orderStatus;
|
||||
|
||||
/**
|
||||
* 当前服务端已登记的证据位置和状态,用于终端幂等跳过成功项。
|
||||
*/
|
||||
@Schema(description = "证据列表")
|
||||
private List<HardOrderEvidenceImageVo> evidenceImages;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.order;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 终端提交元数据、复制参考图或上传现场图后的幂等处理结果。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "订单证据处理结果")
|
||||
public class HardOrderEvidenceUploadResultVo {
|
||||
|
||||
/**
|
||||
* 服务端证据记录主键。
|
||||
*/
|
||||
@Schema(description = "证据记录主键")
|
||||
private Long evidenceId;
|
||||
|
||||
/**
|
||||
* 服务端订单主键。
|
||||
*/
|
||||
@Schema(description = "订单主键")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 处理后的证据状态。
|
||||
*/
|
||||
@Schema(description = "证据状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* AVAILABLE 状态下的图片访问地址,其他状态允许为空。
|
||||
*/
|
||||
@Schema(description = "图片访问地址")
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 是否实际上传或复制了新文件;幂等命中时为 false。
|
||||
*/
|
||||
@Schema(description = "是否新写入文件")
|
||||
private Boolean uploaded;
|
||||
|
||||
/**
|
||||
* 面向终端同步日志的简短处理结果。
|
||||
*/
|
||||
@Schema(description = "处理结果说明")
|
||||
private String message;
|
||||
}
|
||||
+5
@@ -74,4 +74,9 @@ public class HardOrderVo extends HardOrderEntity implements BaseUserDeptInterfac
|
||||
*/
|
||||
private Long walletTypeId;
|
||||
|
||||
/**
|
||||
* 订单详情中的长期识别证据列表,按证据类型、角色和序号排序。
|
||||
*/
|
||||
private List<HardOrderEvidenceImageVo> evidenceImages;
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.entity.xfj.HardXfjLogEntity;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class HardXfjLogVo extends HardXfjLogEntity {
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String hardName;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjBaseVo {
|
||||
|
||||
public static final Integer STATUS_SUCCESS = 1;
|
||||
public static final Integer STATUS_FAIL = 0;
|
||||
|
||||
public static final String DEFAULT_SUCCESS_MSG = "成功";
|
||||
public static final String DEFAULT_FAIL_MSG = "服务端错误";
|
||||
|
||||
/**
|
||||
* 1:成功
|
||||
* 0:失败
|
||||
*/
|
||||
private Integer Status = STATUS_SUCCESS;
|
||||
|
||||
/**
|
||||
* 错误消息(Status为0时屏幕显示此内容,最多不超过8个汉字)
|
||||
*/
|
||||
private String Msg = DEFAULT_SUCCESS_MSG;
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjConsumTransactionsVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 现金余额(允许两位小数)
|
||||
*/
|
||||
private String Money;
|
||||
|
||||
/**
|
||||
* 补贴余额(允许两位小数)
|
||||
*/
|
||||
private String Subsidy;
|
||||
|
||||
/**
|
||||
* 剩余次数
|
||||
*/
|
||||
private String Times;
|
||||
|
||||
/**
|
||||
* 赠送余额(允许两位小数)
|
||||
*/
|
||||
private String Integral;
|
||||
|
||||
/**
|
||||
* 入场时间(固定值:"")
|
||||
*/
|
||||
private String InTime;
|
||||
|
||||
/**
|
||||
* 出场时间(固定值:"")
|
||||
*/
|
||||
private String OutTime;
|
||||
|
||||
/**
|
||||
* 累计使用时间:天时分秒
|
||||
*/
|
||||
private String CumulativeTime;
|
||||
|
||||
/**
|
||||
* 实际扣费金额(信息查询模式下返回"0")
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 语音段(用于安卓系统设备)
|
||||
*/
|
||||
private String VoiceID;
|
||||
|
||||
/**
|
||||
* 自定义显示文本(Status为1时传入,屏幕显示此值,内容内使用\r\n换行,最多支持4行,每行不超过8个汉字)
|
||||
*/
|
||||
private String Text;
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjCountsVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 自定义显示文本(Status为1时传入,屏幕显示此值,内容内使用\n换行,最多支持4行,每行不超过8个汉字)
|
||||
*/
|
||||
private String Text;
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjKeepAliveVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 二维码交易订单号,没定单号时为空
|
||||
*/
|
||||
private String Qrorder;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjMenusVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 菜品名称
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 菜品单价
|
||||
*/
|
||||
private String Price;
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOffLinesVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* Status为1时返回此内容(返回是请求示例中的order信息)
|
||||
*/
|
||||
private String Order;
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOrderQueryVo extends XfjBaseVo {
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 现金余额(允许两位小数)
|
||||
*/
|
||||
private String Money;
|
||||
|
||||
/**
|
||||
* 补贴余额(允许两位小数)
|
||||
*/
|
||||
private String Subsidy;
|
||||
|
||||
/**
|
||||
* 剩余次数
|
||||
*/
|
||||
private String Times;
|
||||
|
||||
/**
|
||||
* 赠送余额(允许两位小数)
|
||||
*/
|
||||
private String Integral;
|
||||
|
||||
/**
|
||||
* 入场时间(固定值:"")
|
||||
*/
|
||||
private String InTime;
|
||||
|
||||
/**
|
||||
* 出场时间(固定值:"")
|
||||
*/
|
||||
private String OutTime;
|
||||
|
||||
/**
|
||||
* 累计使用时间:天时分秒
|
||||
*/
|
||||
private String CumulativeTime;
|
||||
|
||||
/**
|
||||
* 实际扣费金额(信息查询模式下返回"0")
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 语音段(用于安卓系统设备)
|
||||
*/
|
||||
private String VoiceID;
|
||||
|
||||
/**
|
||||
* 自定义显示文本(Status为1时传入,屏幕显示此值,内容内使用\r\n换行,最多支持4行,每行不超过8个汉字)
|
||||
*/
|
||||
private String Text;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjOrderRefundVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 现金余额(允许两位小数)
|
||||
*/
|
||||
private String Money;
|
||||
|
||||
/**
|
||||
* 补贴余额(允许两位小数)
|
||||
*/
|
||||
private String Subsidy;
|
||||
|
||||
/**
|
||||
* 剩余次数
|
||||
*/
|
||||
private String Times;
|
||||
|
||||
/**
|
||||
* 赠送余额(允许两位小数)
|
||||
*/
|
||||
private String Integral;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 11 (写死 11)用于区分接口
|
||||
*/
|
||||
private Integer CmdType;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjQRCodeTransactionVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String Qrorder;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjServerTimeVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 服务器时间,固定格式为yyyyMMddHHmmssd最后一位d代表星期,星期一为1,星期六为6,星期天为0
|
||||
*/
|
||||
private String Time;
|
||||
|
||||
/**
|
||||
* 1:有白名单更新
|
||||
* 0:无
|
||||
*/
|
||||
private Integer WLUptate;
|
||||
|
||||
/**
|
||||
* 起始页数(当WLUptate为1却WLPage为0时,清除所有白名单)
|
||||
*/
|
||||
private Integer WLPage;
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjTransactionInquiryVo extends XfjBaseVo {
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 10进制卡序列号(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 现金余额(允许两位小数)
|
||||
*/
|
||||
private String Money;
|
||||
|
||||
/**
|
||||
* 补贴余额(允许两位小数)
|
||||
*/
|
||||
private String Subsidy;
|
||||
|
||||
/**
|
||||
* 剩余次数
|
||||
*/
|
||||
private String Times;
|
||||
|
||||
/**
|
||||
* 赠送余额(允许两位小数)
|
||||
*/
|
||||
private String Integral;
|
||||
|
||||
/**
|
||||
* 入场时间(固定值:"")
|
||||
*/
|
||||
private String InTime;
|
||||
|
||||
/**
|
||||
* 出场时间(固定值:"")
|
||||
*/
|
||||
private String OutTime;
|
||||
|
||||
/**
|
||||
* 累计使用时间:天时分秒
|
||||
*/
|
||||
private String CumulativeTime;
|
||||
|
||||
/**
|
||||
* 实际扣费金额(信息查询模式下返回"0")
|
||||
*/
|
||||
private String Amount;
|
||||
|
||||
/**
|
||||
* 语音段(用于安卓系统设备)
|
||||
*/
|
||||
private String VoiceID;
|
||||
|
||||
/**
|
||||
* 自定义显示文本(Status为1时传入,屏幕显示此值,内容内使用\r\n换行,最多支持4行,每行不超过8个汉字)
|
||||
*/
|
||||
private String Text;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjTransactionVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 编号 从最后一笔消费往前算的位置
|
||||
* 1:就是最后一笔
|
||||
* 2:就是倒数第二笔
|
||||
*/
|
||||
private String Number;
|
||||
|
||||
/**
|
||||
* 消费时间(格式:yyyyMMddHHmmss)
|
||||
*/
|
||||
private String Time;
|
||||
|
||||
/**
|
||||
* 卡号10进制(实体卡号或虚拟卡号)
|
||||
*/
|
||||
private String CardNo;
|
||||
|
||||
/**
|
||||
* 姓名最大5个汉字
|
||||
*/
|
||||
private String Name;
|
||||
|
||||
/**
|
||||
* 消费额
|
||||
* - 10.00元
|
||||
*/
|
||||
private String Consume;
|
||||
|
||||
/**
|
||||
* 余额
|
||||
* - 10.00元
|
||||
*/
|
||||
private String Balance;
|
||||
|
||||
|
||||
/**
|
||||
* 消费订单号
|
||||
*/
|
||||
private String OrderSeralid;
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.bsd.pjcloud.hard.api.vo.xfj;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class XfjWhiteListVo extends XfjBaseVo {
|
||||
|
||||
/**
|
||||
* 返回设备提交的序号
|
||||
*/
|
||||
private Integer CommID;
|
||||
|
||||
/**
|
||||
* 页数(获取那一页的数据)
|
||||
*/
|
||||
private Integer Page;
|
||||
|
||||
/**
|
||||
* 名单个数
|
||||
*/
|
||||
private Integer PageLen;
|
||||
|
||||
/**
|
||||
* 白名单数据(1|0000000001|1,序号|卡号|操作)
|
||||
* 1:添加
|
||||
* 0:删除
|
||||
*/
|
||||
private String WLData;
|
||||
|
||||
/**
|
||||
* 1:继续获取
|
||||
* 0:已经更新完成
|
||||
*/
|
||||
private Integer Uptate;
|
||||
|
||||
}
|
||||
@@ -13,6 +13,10 @@
|
||||
<artifactId>pjcloud-hardware-biz</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.bsd</groupId>
|
||||
<artifactId>pjcloud-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.bsd.pjcloud.common.swagger.annotation.EnableOpenApi;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author archetype auto
|
||||
@@ -17,6 +18,7 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
@EnableDiscoveryClient
|
||||
@EnablePjcloudResourceServer
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class HardApp {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HardApp.class, args);
|
||||
|
||||
+47
-1
@@ -9,6 +9,8 @@ import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardInvoiceDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrintRequestDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrintRetryDto;
|
||||
import com.bsd.pjcloud.hard.api.entity.HardInvoiceEntity;
|
||||
import com.bsd.pjcloud.hard.service.HardInvoiceService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -22,6 +24,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 发票记录
|
||||
@@ -48,6 +51,49 @@ public class HardInvoiceController {
|
||||
return R.ok(hardInvoiceService.print(orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建发票或补建兑换券打印任务;已打印兑换券必须使用带专用权限的重打接口。
|
||||
*
|
||||
* @param request 订单、打印类型和可选来源终端
|
||||
* @return 创建、复用或失败重试后的打印任务
|
||||
*/
|
||||
@Operation(summary = "创建打印任务", description = "创建发票或补建兑换券打印任务")
|
||||
@SysLog("创建打印任务")
|
||||
@Idempotent(expireTime = 5, key = "'/hard-invoice/print:' + #request.getOrderId() + ':' + #request.getPrintType()", info = "正在创建打印任务,请勿重复操作")
|
||||
@PostMapping("/print")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardInvoice_admin_print')")
|
||||
public R<?> createPrintTask(@Valid @RequestBody HardPrintRequestDto request) {
|
||||
return R.ok(hardInvoiceService.createPrintTask(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试原失败任务,重试版本增加但打印序号不变。
|
||||
*
|
||||
* @param request 失败任务 ID,以及原任务缺少来源时可补录的来源终端 ID
|
||||
* @return 重试后的任务
|
||||
*/
|
||||
@Operation(summary = "重试失败打印任务")
|
||||
@SysLog("重试失败打印任务")
|
||||
@PostMapping("/retry")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardInvoice_admin_print')")
|
||||
public R<?> retry(@Valid @RequestBody HardPrintRetryDto request) {
|
||||
return R.ok(hardInvoiceService.retryTask(request.getHistoryId(), request.getSourceHardId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对已打印凭证创建下一打印序号并记录人工原因。
|
||||
*
|
||||
* @param request 已打印任务 ID 和重打原因
|
||||
* @return 新打印任务
|
||||
*/
|
||||
@Operation(summary = "人工重打兑换券")
|
||||
@SysLog("人工重打兑换券")
|
||||
@PostMapping("/reprint")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardInvoice_voucher_reprint')")
|
||||
public R<?> reprint(@Valid @RequestBody HardPrintRetryDto request) {
|
||||
return R.ok(hardInvoiceService.reprintTask(request.getHistoryId(), request.getReprintReason()));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardInvoice_view')")
|
||||
@@ -100,4 +146,4 @@ public class HardInvoiceController {
|
||||
public List<HardInvoiceEntity> export(HardInvoiceEntity hardInvoice, Long[] ids) {
|
||||
return hardInvoiceService.list(Wrappers.lambdaQuery(hardInvoice).in(ArrayUtil.isNotEmpty(ids), HardInvoiceEntity::getId, ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package com.bsd.pjcloud.hard.controller;
|
||||
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrinterBindingDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrinterConfigDto;
|
||||
import com.bsd.pjcloud.hard.service.HardPrinterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 业务终端与无屏尔印 USB 打印机的绑定和配置管理接口。
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/hard-printer")
|
||||
@Tag(description = "hard-printer", name = "尔印 USB 打印机管理")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class HardPrinterController {
|
||||
|
||||
/** USB 打印机绑定和配置服务。 */
|
||||
private final HardPrinterService hardPrinterService;
|
||||
|
||||
/**
|
||||
* 查询指定设备当前绑定详情。
|
||||
*
|
||||
* @param hardId 来源或打印设备 ID
|
||||
* @return 绑定详情
|
||||
*/
|
||||
@GetMapping("/binding/{hardId}")
|
||||
@Operation(summary = "查询打印机绑定")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_view')")
|
||||
public R<?> getBinding(@PathVariable Long hardId) {
|
||||
return R.ok(hardPrinterService.getBinding(hardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询来源设备可绑定的 EY_PRINT 打印机资产。
|
||||
*
|
||||
* @param sourceHardId 来源业务终端 ID
|
||||
* @return 可绑定设备列表
|
||||
*/
|
||||
@GetMapping("/available")
|
||||
@Operation(summary = "查询可绑定打印机")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')")
|
||||
public R<?> listAvailable(@RequestParam Long sourceHardId) {
|
||||
return R.ok(hardPrinterService.listAvailablePrinters(sourceHardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询商户下已经绑定打印机的来源终端。
|
||||
*
|
||||
* @param merchantId 商户 ID
|
||||
* @return 来源终端列表
|
||||
*/
|
||||
@GetMapping("/sources")
|
||||
@Operation(summary = "查询已绑定打印机的业务终端")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardInvoice_admin_print')")
|
||||
public R<?> listBoundSources(@RequestParam Long merchantId) {
|
||||
return R.ok(hardPrinterService.listBoundSources(merchantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建来源业务终端与 USB 打印机的一对一绑定。
|
||||
*
|
||||
* @param dto 绑定请求
|
||||
* @return 绑定详情
|
||||
*/
|
||||
@PostMapping("/binding")
|
||||
@Operation(summary = "绑定 USB 打印机")
|
||||
@SysLog("绑定 USB 打印机")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')")
|
||||
public R<?> bind(@Valid @RequestBody HardPrinterBindingDto dto) {
|
||||
return R.ok(hardPrinterService.bind(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除来源终端的打印机绑定。
|
||||
*
|
||||
* @param sourceHardId 来源业务终端 ID
|
||||
* @return 空成功响应
|
||||
*/
|
||||
@DeleteMapping("/binding/{sourceHardId}")
|
||||
@Operation(summary = "解绑 USB 打印机")
|
||||
@SysLog("解绑 USB 打印机")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')")
|
||||
public R<Void> unbind(@PathVariable Long sourceHardId) {
|
||||
hardPrinterService.unbind(sourceHardId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 USB 打印机当前参数。
|
||||
*
|
||||
* @param printerHardId EY_PRINT 逻辑打印机资产 ID
|
||||
* @return 当前或默认配置
|
||||
*/
|
||||
@GetMapping("/config/{printerHardId}")
|
||||
@Operation(summary = "查询 USB 打印机配置")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_view')")
|
||||
public R<?> getConfig(@PathVariable Long printerHardId) {
|
||||
return R.ok(hardPrinterService.getConfig(printerHardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 USB 打印机参数。
|
||||
*
|
||||
* @param dto 配置请求
|
||||
* @return 保存后的配置
|
||||
*/
|
||||
@PutMapping("/config")
|
||||
@Operation(summary = "保存 USB 打印机配置")
|
||||
@SysLog("保存 USB 打印机配置")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')")
|
||||
public R<?> saveConfig(@Valid @RequestBody HardPrinterConfigDto dto) {
|
||||
return R.ok(hardPrinterService.saveConfig(dto));
|
||||
}
|
||||
}
|
||||
+121
@@ -8,6 +8,10 @@ import com.bsd.pjcloud.common.security.util.SecurityUtils;
|
||||
import com.bsd.pjcloud.hard.api.constant.HardProductNatureEnum;
|
||||
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
|
||||
import com.bsd.pjcloud.hard.api.constant.order.HardPayTypeEnum;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrintRequestDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrintResultDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrintRetryDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.HardPrinterInfoReportDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.async.SyncOfflineReqVo;
|
||||
import com.bsd.pjcloud.hard.api.dto.hard.*;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardChartLatitudeVo;
|
||||
@@ -40,11 +44,18 @@ public class HardApiController {
|
||||
|
||||
private final HardManagerService hardManagerService;
|
||||
private final HardInvoiceService hardInvoiceService;
|
||||
private final HardPrinterService hardPrinterService;
|
||||
private final HardCarService hardCarService;
|
||||
private final HardOrderService hardwareOrderService;
|
||||
private final HardOrderSyncService hardOrderSyncService;
|
||||
private final HardApiService hardApiService;
|
||||
private final HardOrderTotalService hardOrderTotalService;
|
||||
private final HardTerminalPrintService hardTerminalPrintService;
|
||||
|
||||
/**
|
||||
* 食堂、超市 Android 终端心跳状态服务。
|
||||
*/
|
||||
private final HardTerminalStatusService hardTerminalStatusService;
|
||||
|
||||
@PostMapping("/bind")
|
||||
@Operation(summary = "绑定服务端设备")
|
||||
@@ -52,6 +63,20 @@ public class HardApiController {
|
||||
return R.ok(hardManagerService.bind(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收食堂、超市 Android 终端的在线心跳,设备身份由请求头中的设备 ID 确认。
|
||||
*
|
||||
* @param hardId 当前绑定设备 ID
|
||||
* @param dto APK 版本与心跳场景
|
||||
* @return 无业务数据的成功响应
|
||||
*/
|
||||
@PostMapping("/terminal-heartbeat")
|
||||
@Operation(summary = "上报终端心跳")
|
||||
public R<Void> terminalHeartbeat(@RequestHeader("DEVICE-ID") Long hardId, @Valid @RequestBody TerminalHeartbeatDto dto) {
|
||||
hardTerminalStatusService.heartbeat(hardId, dto);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取硬件配置信息")
|
||||
public R<HardManagerVo> get(@RequestParam Long id) {
|
||||
@@ -146,6 +171,102 @@ public class HardApiController {
|
||||
return R.ok(hardInvoiceService.printInvoice(orderId, historyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前食堂或超市终端所绑定的无屏 USB 打印机及打印参数。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @return 当前绑定和打印机配置;尚未绑定时数据为空
|
||||
*/
|
||||
@GetMapping("/printer/bootstrap")
|
||||
@Operation(summary = "获取业务终端打印机绑定")
|
||||
public R<?> printerBootstrap(@RequestHeader("DEVICE-ID") Long hardId) {
|
||||
return R.ok(hardPrinterService.getSourceBinding(hardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 由当前食堂或超市终端上报其实际连接打印机的型号、VID 和 PID。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @param dto Android USB Host 枚举出的打印机信息
|
||||
* @return 保留纸宽、切刀和图片模式后的完整打印机配置
|
||||
*/
|
||||
@PostMapping("/printer/info")
|
||||
@Operation(summary = "业务终端上报 USB 打印机信息")
|
||||
public R<?> reportPrinterInfo(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardPrinterInfoReportDto dto) {
|
||||
return R.ok(hardPrinterService.reportDeviceInfo(hardId, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取当前食堂或超市终端所绑定打印机尚未完成的任务,补偿离线期间遗漏的 MQTT 消息。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @return 待打印任务列表
|
||||
*/
|
||||
@GetMapping("/printer/tasks")
|
||||
@Operation(summary = "拉取业务终端代理打印待办")
|
||||
public R<?> printerTasks(@RequestHeader("DEVICE-ID") Long hardId) {
|
||||
return R.ok(hardInvoiceService.listPendingTasks(hardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 由当前食堂或超市终端代理提交 USB 打印结果。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @param result 任务、订单、版本和结果
|
||||
* @return 接受或幂等忽略时返回 {@code true}
|
||||
*/
|
||||
@PostMapping("/printer/result")
|
||||
@Operation(summary = "提交业务终端代理打印结果")
|
||||
public R<Boolean> printerResult(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardPrintResultDto result) {
|
||||
return R.ok(hardInvoiceService.reportPrintResult(hardId, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务终端按可信 DEVICE-ID 创建打印任务,禁止请求体跨商户指定来源设备。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @param request 订单和打印类型
|
||||
* @return 创建、复用或重试后的任务
|
||||
*/
|
||||
@PostMapping("/printer/task")
|
||||
@Operation(summary = "业务终端创建打印任务")
|
||||
public R<?> createPrinterTask(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardPrintRequestDto request) {
|
||||
request.setSourceHardId(hardId);
|
||||
return R.ok(hardInvoiceService.createPrintTask(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前业务终端为指定订单生成的发票、兑换券及最终打印结果。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @param orderId 核销记录中的服务端订单 ID
|
||||
* @return 当前设备范围内的服务端打印记录,结果不会读取终端本地数据库
|
||||
*/
|
||||
@GetMapping("/printer/records")
|
||||
@Operation(summary = "查询业务终端订单打印记录")
|
||||
public R<List<HardInvoiceVo>> printerRecords(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@RequestParam Long orderId) {
|
||||
return R.ok(hardTerminalPrintService.listRecords(hardId, orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 由当前业务终端对本机已打印的核销兑换券创建下一打印序号。
|
||||
*
|
||||
* @param hardId DEVICE-ID 请求头中的来源业务终端 ID
|
||||
* @param request 已打印任务 ID 和操作员填写的重打原因
|
||||
* @return 新建或并发场景下复用的兑换券打印任务
|
||||
*/
|
||||
@PostMapping("/printer/reprint")
|
||||
@Operation(summary = "业务终端重新打印核销兑换券")
|
||||
public R<?> reprintVoucher(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardPrintRetryDto request) {
|
||||
return R.ok(hardTerminalPrintService.reprintVoucher(
|
||||
hardId, request.getHistoryId(), request.getReprintReason()));
|
||||
}
|
||||
|
||||
@PostMapping("/sync-offline-order")
|
||||
@Operation(summary = "同步离线订单")
|
||||
@Idempotent(expireTime = 1, info = "正在处理中,请勿重复操作")
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.bsd.pjcloud.hard.controller.hard;
|
||||
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardOrderEvidenceSubmitDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardOrderEvidenceUploadDto;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardOrderEvidenceResolveVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardOrderEvidenceUploadResultVo;
|
||||
import com.bsd.pjcloud.hard.service.HardOrderEvidenceImageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 收银终端订单识别证据接口,使用统一 Token、租户头和 DEVICE-ID 校验调用方身份。
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/hard-order-evidence")
|
||||
@Tag(description = "hard-order-evidence", name = "订单识别证据")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class HardOrderEvidenceImageController {
|
||||
|
||||
/**
|
||||
* 订单识别证据业务服务。
|
||||
*/
|
||||
private final HardOrderEvidenceImageService hardOrderEvidenceImageService;
|
||||
|
||||
/**
|
||||
* 对账终端订单并返回服务端证据状态。
|
||||
*
|
||||
* @param hardId 当前绑定设备主键
|
||||
* @param androidOrderCode 终端本地订单号,预约核销证据允许为空
|
||||
* @param orderId 服务端订单主键,现场下单证据允许为空
|
||||
* @return 服务端订单和证据列表
|
||||
*/
|
||||
@GetMapping("/ad/resolve")
|
||||
@Operation(summary = "按终端订单号或服务端订单ID对账识别证据")
|
||||
public R<HardOrderEvidenceResolveVo> resolve(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@RequestParam(required = false) String androidOrderCode,
|
||||
@RequestParam(required = false) Long orderId) {
|
||||
return R.ok(hardOrderEvidenceImageService.resolve(hardId, androidOrderCode, orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记证据元数据、复制参考图或报告终端图片永久缺失。
|
||||
*
|
||||
* @param hardId 当前绑定设备主键
|
||||
* @param dto 证据元数据和目标状态
|
||||
* @return 处理后的证据状态
|
||||
*/
|
||||
@PostMapping("/ad/submit")
|
||||
@Operation(summary = "提交订单识别证据元数据")
|
||||
public R<HardOrderEvidenceUploadResultVo> submit(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardOrderEvidenceSubmitDto dto) {
|
||||
return R.ok(hardOrderEvidenceImageService.submit(hardId, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传现场识别图片,服务端校验图片内容后写入订单专属文件目录。
|
||||
*
|
||||
* @param hardId 当前绑定设备主键
|
||||
* @param dto 订单与识别元数据
|
||||
* @param file JPEG 或 PNG 现场图片
|
||||
* @return 幂等上传结果
|
||||
*/
|
||||
@PostMapping(value = "/ad/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "上传订单现场识别图片")
|
||||
public R<HardOrderEvidenceUploadResultVo> upload(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @ModelAttribute HardOrderEvidenceUploadDto dto,
|
||||
@RequestPart("file") MultipartFile file) {
|
||||
return R.ok(hardOrderEvidenceImageService.upload(hardId, dto, file));
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.bsd.pjcloud.hard.controller.hard;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.hard.api.dto.manager.HardTerminalLogFileQueryDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.manager.HardTerminalLogPreflightDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.manager.HardTerminalLogUploadDto;
|
||||
import com.bsd.pjcloud.hard.api.entity.manager.HardTerminalLogFileEntity;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalLogDeviceVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalLogFileTreeVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalLogPreflightVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalLogPreviewVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.manager.HardTerminalLogUploadResultVo;
|
||||
import com.bsd.pjcloud.hard.service.HardTerminalLogFileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 终端日志文件。
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/hardTerminalLogFile")
|
||||
@Tag(description = "hardTerminalLogFile", name = "终端日志文件管理")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class HardTerminalLogFileController {
|
||||
|
||||
private final HardTerminalLogFileService hardTerminalLogFileService;
|
||||
|
||||
@Operation(summary = "终端日志上传预检")
|
||||
@PostMapping("/ad/preflight")
|
||||
public R<HardTerminalLogPreflightVo> preflight(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @RequestBody HardTerminalLogPreflightDto dto) {
|
||||
return R.ok(hardTerminalLogFileService.preflight(hardId, dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "上传脱敏后的终端日志")
|
||||
@PostMapping(value = "/ad/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public R<HardTerminalLogUploadResultVo> upload(@RequestHeader("DEVICE-ID") Long hardId,
|
||||
@Valid @ModelAttribute HardTerminalLogUploadDto dto,
|
||||
@RequestPart("file") MultipartFile file) {
|
||||
return R.ok(hardTerminalLogFileService.upload(hardId, dto, file));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询终端日志文件")
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_view')")
|
||||
public R<Page<HardTerminalLogFileEntity>> page(@ParameterObject Page<HardTerminalLogFileEntity> page,
|
||||
@ParameterObject HardTerminalLogFileQueryDto dto) {
|
||||
return R.ok(hardTerminalLogFileService.scopePage(page, dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "按设备聚合终端日志")
|
||||
@GetMapping("/device-page")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_view')")
|
||||
public R<Page<HardTerminalLogDeviceVo>> devicePage(@ParameterObject Page<HardTerminalLogDeviceVo> page,
|
||||
@ParameterObject HardTerminalLogFileQueryDto dto) {
|
||||
return R.ok(hardTerminalLogFileService.devicePage(page, dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询指定设备的日志文件树")
|
||||
@GetMapping("/file-tree")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_view')")
|
||||
public R<List<HardTerminalLogFileTreeVo>> fileTree(@RequestParam Long hardId,
|
||||
@ParameterObject HardTerminalLogFileQueryDto dto) {
|
||||
return R.ok(hardTerminalLogFileService.fileTree(hardId, dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "预览终端日志尾部")
|
||||
@GetMapping("/{id}/preview")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_preview')")
|
||||
public R<HardTerminalLogPreviewVo> preview(@PathVariable Long id) {
|
||||
return R.ok(hardTerminalLogFileService.preview(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "下载完整终端日志")
|
||||
@GetMapping("/{id}/download")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_down')")
|
||||
public void download(@PathVariable Long id, HttpServletResponse response) {
|
||||
hardTerminalLogFileService.download(id, response);
|
||||
}
|
||||
|
||||
@Operation(summary = "立即拉取指定终端当前日志")
|
||||
@SysLog("立即拉取终端当前日志")
|
||||
@PostMapping("/pull/{hardId}")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardTerminalLogFile_pull')")
|
||||
public R<Boolean> pull(@PathVariable Long hardId) {
|
||||
return R.ok(hardTerminalLogFileService.pull(hardId));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class HardManagerController {
|
||||
@DeleteMapping
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardManager_del')")
|
||||
public R removeById(@RequestBody Long[] ids) {
|
||||
return R.ok(hardManagerService.removeBatchByIds(CollUtil.toList(ids)));
|
||||
return R.ok(hardManagerService.removeWithPrinterCheck(CollUtil.toList(ids)));
|
||||
}
|
||||
|
||||
@ResponseExcel
|
||||
|
||||
+1
-1
@@ -313,7 +313,7 @@ public class HardOrderController {
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardwareOrder_view')")
|
||||
public R getById(@PathVariable("id") Long id) {
|
||||
return R.ok(hardwareOrderService.join(id));
|
||||
return R.ok(hardwareOrderService.joinWithEvidence(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "离线订单手动支付", description = "离线订单手动支付")
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package com.bsd.pjcloud.hard.controller.xfj;
|
||||
|
||||
import com.bsd.pjcloud.common.security.annotation.Inner;
|
||||
import com.bsd.pjcloud.hard.api.vo.xfj.XfjCountsVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.xfj.XfjServerTimeVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.xfj.XfjTransactionInquiryVo;
|
||||
import com.bsd.pjcloud.hard.api.vo.xfj.XfjOrderRefundVo;
|
||||
import com.bsd.pjcloud.hard.service.HardXfjService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备预约管理
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/hxz")
|
||||
@Tag(description = "hxz", name = "消费机接口")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class HardXfjController {
|
||||
|
||||
private final HardXfjService hardXfjService;
|
||||
|
||||
@Operation(summary = "获取服务器时间接口")
|
||||
@PostMapping("/v1/ServerTime")
|
||||
@Inner(value = false)
|
||||
public XfjServerTimeVo serverTime(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.serverTime(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户刷卡综合接口")
|
||||
@PostMapping("/v1/ConsumTransactions")
|
||||
@Inner(value = false)
|
||||
public Object consumTransactions(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.consumTransactions(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户刷卡消费结果查询接口")
|
||||
@PostMapping("/v1/orderQuery")
|
||||
@Inner(value = false)
|
||||
public Object orderQuery(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.orderQuery(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "终端查询日统计接口")
|
||||
@PostMapping("/v1/Counts")
|
||||
@Inner(value = false)
|
||||
public XfjCountsVo counts(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.counts(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "终端设备待查状态,按上翻/下翻键向服务器请求,服务器返回一条消费明细")
|
||||
@PostMapping("/v1/transaction")
|
||||
@Inner(value = false)
|
||||
public Object transaction(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.transaction(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "订单退款接口")
|
||||
@PostMapping("/v1/OrderRefund")
|
||||
@Inner(value = false)
|
||||
public XfjOrderRefundVo orderRefund(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.orderRefund(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "KeepAlive")
|
||||
@PostMapping("/v1/KeepAlive")
|
||||
@Inner(value = false)
|
||||
public Object keepAlive(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.keepAlive(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "ConCode")
|
||||
@PostMapping("/v1/ConCode/:deviceCode/:price")
|
||||
@Inner(value = false)
|
||||
public Object conCode(@PathParam("deviceCode") String deviceCode, @PathParam("price") String price, HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.conCode(deviceCode, price, httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "付款码(二维码)支付接口")
|
||||
@PostMapping("/v1/QRCodeTransaction")
|
||||
@Inner(value = false)
|
||||
public Object qrCodeTransaction(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.qrCodeTransaction(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "二维码支付结果查询接口")
|
||||
@PostMapping("/v1/TransactionInquiry")
|
||||
@Inner(value = false)
|
||||
public XfjTransactionInquiryVo transactionInquiry(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.transactionInquiry(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "菜品查询接口")
|
||||
@PostMapping("/v1/menus")
|
||||
@Inner(value = false)
|
||||
public Object menus(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.menus(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "记账扣费流水上传接口")
|
||||
@PostMapping("/v1/OffLines")
|
||||
@Inner(value = false)
|
||||
public Object offLines(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.offLines(httpEntity.getBody(), headers);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取卡号白名单接口")
|
||||
@PostMapping("/v1/WhiteList")
|
||||
@Inner(value = false)
|
||||
public Object whiteList(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
|
||||
return hardXfjService.whiteList(httpEntity.getBody(), headers);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.bsd.pjcloud.hard.controller.xfj;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.hard.api.dto.xfj.HardXfjLogDto;
|
||||
import com.bsd.pjcloud.hard.service.HardXfjLogService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 消费机日志
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/xfj-log")
|
||||
@Tag(description = "xfj-log", name = "消费机日志管理")
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class HardXfjLogController {
|
||||
|
||||
private final HardXfjLogService hardXfjLogService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardXfjLog_view')")
|
||||
public R getHardXfjLogPage(@ParameterObject Page page, @ParameterObject HardXfjLogDto dto) {
|
||||
return R.ok(hardXfjLogService.pageList(page, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询消费机日志
|
||||
*/
|
||||
@Operation(summary = "通过id查询", description = "通过id查询")
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardXfjLog_view')")
|
||||
public R getById(@PathVariable("id") Long id) {
|
||||
return R.ok(hardXfjLogService.getById(id));
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user