================================================================
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
