-- zyt projection and integration state for the Luoyang pharmacy ERP. -- Existing-order DDL is guarded because this migration may be resumed after a partial deployment. SET @schema_name := DATABASE(); SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_order_no' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_order_no` varchar(40) NULL DEFAULT NULL COMMENT ''Remote ej ERP order number'' AFTER `ship_mode`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_submit_time' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_submit_time` int unsigned NOT NULL DEFAULT 0 AFTER `ej_pharmacy_order_no`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_status' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_status` varchar(32) NOT NULL DEFAULT '''' AFTER `ej_pharmacy_submit_time`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_review_status' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_review_status` varchar(24) NOT NULL DEFAULT '''' AFTER `ej_pharmacy_status`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_status_version' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_status_version` bigint unsigned NOT NULL DEFAULT 0 AFTER `ej_pharmacy_review_status`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_current_step' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_current_step` varchar(100) NOT NULL DEFAULT '''' AFTER `ej_pharmacy_status_version`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND COLUMN_NAME = 'ej_pharmacy_remark' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD COLUMN `ej_pharmacy_remark` varchar(500) NOT NULL DEFAULT '''' AFTER `ej_pharmacy_current_step`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Repair a partial deployment that created the order number as NOT NULL DEFAULT ''. ALTER TABLE `zyt_tcm_prescription_order` MODIFY COLUMN `ej_pharmacy_order_no` varchar(40) NULL DEFAULT NULL COMMENT 'Remote ej ERP order number'; UPDATE `zyt_tcm_prescription_order` SET `ej_pharmacy_order_no` = NULL WHERE `ej_pharmacy_order_no` = ''; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND INDEX_NAME = 'uk_ej_pharmacy_order_no' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD UNIQUE KEY `uk_ej_pharmacy_order_no` (`ej_pharmacy_order_no`)' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_tcm_prescription_order' AND INDEX_NAME = 'idx_ej_pharmacy_status' ), 'SELECT 1', 'ALTER TABLE `zyt_tcm_prescription_order` ADD KEY `idx_ej_pharmacy_status` (`ej_pharmacy_status`,`ej_pharmacy_status_version`)' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- EJ callbacks may carry longer Unicode carrier codes and tracking numbers than the legacy order projection. ALTER TABLE `zyt_tcm_prescription_order` MODIFY COLUMN `express_company` varchar(32) NOT NULL DEFAULT '' COMMENT 'Carrier code', MODIFY COLUMN `tracking_number` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tracking number'; ALTER TABLE `zyt_express_tracking` MODIFY COLUMN `tracking_number` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tracking number'; ALTER TABLE `zyt_express_trace` MODIFY COLUMN `tracking_number` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tracking number'; CREATE TABLE IF NOT EXISTS `zyt_ej_medicine_catalog` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `medicine_code` varchar(32) NOT NULL, `name` varchar(120) NOT NULL, `brand` varchar(120) NOT NULL DEFAULT '', `unit` varchar(24) NOT NULL, `settlement_price` decimal(12,4) NOT NULL DEFAULT 0.0000, `retail_price` decimal(12,4) NOT NULL DEFAULT 0.0000, `status` tinyint unsigned NOT NULL DEFAULT 1, `catalog_version` bigint unsigned NOT NULL DEFAULT 0, `remote_deleted` tinyint unsigned NOT NULL DEFAULT 0, `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_medicine_code` (`medicine_code`), KEY `idx_catalog_version` (`catalog_version`), KEY `idx_name_status` (`name`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS `zyt_ej_medicine_mapping` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `local_medicine_id` bigint unsigned NOT NULL, `medicine_code` varchar(32) NOT NULL, `status` tinyint unsigned NOT NULL DEFAULT 1, `operator_id` bigint unsigned NOT NULL DEFAULT 0, `operator_name` varchar(80) NOT NULL DEFAULT '', `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, `delete_time` int unsigned DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_local_medicine` (`local_medicine_id`), UNIQUE KEY `uk_medicine_code` (`medicine_code`), KEY `idx_medicine_status` (`medicine_code`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_ej_medicine_mapping' AND INDEX_NAME = 'uk_medicine_code' ), 'SELECT 1', 'ALTER TABLE `zyt_ej_medicine_mapping` ADD UNIQUE KEY `uk_medicine_code` (`medicine_code`)' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; CREATE TABLE IF NOT EXISTS `zyt_ej_pharmacy_submission` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `prescription_order_id` bigint unsigned NOT NULL, `source_revision` int unsigned NOT NULL DEFAULT 1, `request_hash` char(64) NOT NULL, `request_id` varchar(64) NOT NULL DEFAULT '', `remote_order_no` varchar(40) NOT NULL DEFAULT '', `status` varchar(24) NOT NULL DEFAULT 'PENDING', `http_status` int unsigned NOT NULL DEFAULT 0, `response_summary` json DEFAULT NULL, `error_message` varchar(1000) NOT NULL DEFAULT '', `operator_id` bigint unsigned NOT NULL DEFAULT 0, `operator_name` varchar(80) NOT NULL DEFAULT '', `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_revision` (`prescription_order_id`,`source_revision`), KEY `idx_status_time` (`status`,`update_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS `zyt_pharmacy_submission_claim` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `prescription_order_id` bigint unsigned NOT NULL, `source_revision` int unsigned NOT NULL DEFAULT 1, `target` varchar(16) NOT NULL, `status` varchar(24) NOT NULL DEFAULT 'PENDING', `claim_token` char(32) NOT NULL, `idempotency_key` char(64) NOT NULL, `remote_order_no` varchar(64) NOT NULL DEFAULT '', `request_id` varchar(64) NOT NULL DEFAULT '', `error_message` varchar(1000) NOT NULL DEFAULT '', `operator_id` bigint unsigned NOT NULL DEFAULT 0, `operator_name` varchar(80) NOT NULL DEFAULT '', `claimed_at` int unsigned NOT NULL DEFAULT 0, `lease_expires_at` int unsigned NOT NULL DEFAULT 0, `completed_at` int unsigned NOT NULL DEFAULT 0, `failed_at` int unsigned NOT NULL DEFAULT 0, `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_revision` (`prescription_order_id`,`source_revision`), UNIQUE KEY `uk_claim_token` (`claim_token`), KEY `idx_status_time` (`status`,`update_time`), KEY `idx_target_remote` (`target`,`remote_order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ALTER TABLE `zyt_pharmacy_submission_claim` MODIFY COLUMN `status` varchar(24) NOT NULL DEFAULT 'PENDING'; SET @ddl := IF( EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'zyt_pharmacy_submission_claim' AND COLUMN_NAME = 'lease_expires_at' ), 'SELECT 1', 'ALTER TABLE `zyt_pharmacy_submission_claim` ADD COLUMN `lease_expires_at` int unsigned NOT NULL DEFAULT 0 AFTER `claimed_at`' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; UPDATE `zyt_pharmacy_submission_claim` SET `lease_expires_at` = `claimed_at` + 300 WHERE `status` = 'PENDING' AND `lease_expires_at` = 0 AND `claimed_at` > 0; CREATE TABLE IF NOT EXISTS `zyt_pharmacy_submission_claim_audit` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `claim_id` bigint unsigned NOT NULL, `prescription_order_id` bigint unsigned NOT NULL, `source_revision` int unsigned NOT NULL DEFAULT 1, `target` varchar(16) NOT NULL, `action` varchar(32) NOT NULL, `from_status` varchar(24) NOT NULL, `to_status` varchar(24) NOT NULL, `remote_order_no` varchar(64) NOT NULL DEFAULT '', `note` varchar(1000) NOT NULL DEFAULT '', `operator_id` bigint unsigned NOT NULL DEFAULT 0, `operator_name` varchar(80) NOT NULL DEFAULT '', `create_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `idx_claim_time` (`claim_id`,`create_time`), KEY `idx_order_time` (`prescription_order_id`,`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS `zyt_ej_pharmacy_callback_inbox` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `event_id` char(36) NOT NULL, `pharmacy_order_no` varchar(40) NOT NULL, `source_order_no` varchar(64) NOT NULL, `event_type` varchar(48) NOT NULL, `status_version` bigint unsigned NOT NULL DEFAULT 0, `payload` json NOT NULL, `process_status` varchar(24) NOT NULL DEFAULT 'PENDING', `processed_time` int unsigned NOT NULL DEFAULT 0, `error_message` varchar(1000) NOT NULL DEFAULT '', `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_event_id` (`event_id`), KEY `idx_process_status` (`process_status`,`id`), KEY `idx_order_version` (`pharmacy_order_no`,`status_version`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS `zyt_ej_pharmacy_sync_state` ( `id` tinyint unsigned NOT NULL, `cursor` bigint unsigned NOT NULL DEFAULT 0, `last_success_time` int unsigned NOT NULL DEFAULT 0, `last_failure_time` int unsigned NOT NULL DEFAULT 0, `last_error_summary` varchar(500) NOT NULL DEFAULT '', `lock_token` char(32) NOT NULL DEFAULT '', `lock_expires_at` int unsigned NOT NULL DEFAULT 0, `create_time` int unsigned NOT NULL DEFAULT 0, `update_time` int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO `zyt_ej_pharmacy_sync_state` (`id`, `cursor`, `create_time`, `update_time`) VALUES (1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()) ON DUPLICATE KEY UPDATE `id` = VALUES(`id`); -- Page sits beside the existing doctor medicine library; API nodes make every endpoint visible to AuthMiddleware. SET @doctor_medicine_menu_id := ( SELECT `id` FROM `zyt_system_menu` WHERE `perms` = 'doctor.medicine/lists' OR (`component` = 'doctor/medicine' AND `type` = 'C') ORDER BY (`perms` = 'doctor.medicine/lists') DESC, `id` ASC LIMIT 1 ); SET @doctor_medicine_parent_id := ( SELECT `pid` FROM `zyt_system_menu` WHERE `id` = @doctor_medicine_menu_id LIMIT 1 ); INSERT INTO `zyt_system_menu` ( `pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time` ) SELECT @doctor_medicine_parent_id, 'C', '洛阳药房药材映射', '', 0, 'pharmacy.medicineMapping/lists', 'pharmacy/medicine-mapping', 'pharmacy/medicine_mapping/index', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() FROM DUAL WHERE @doctor_medicine_parent_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM `zyt_system_menu` WHERE `perms` = 'pharmacy.medicineMapping/lists' ); SET @medicine_mapping_menu_id := ( SELECT `id` FROM `zyt_system_menu` WHERE `perms` = 'pharmacy.medicineMapping/lists' LIMIT 1 ); INSERT INTO `zyt_system_menu` ( `pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time` ) SELECT @medicine_mapping_menu_id, 'A', `permission_node`.`name`, '', `permission_node`.`sort`, `permission_node`.`perms`, '', '', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() FROM ( SELECT '查看同步状态' AS `name`, 10 AS `sort`, 'pharmacy.medicineMapping/status' AS `perms` UNION ALL SELECT '检索远端药材', 20, 'pharmacy.medicineMapping/catalogOptions' UNION ALL SELECT '同步远端目录', 30, 'pharmacy.medicineMapping/sync' UNION ALL SELECT '保存药材映射', 40, 'pharmacy.medicineMapping/save' UNION ALL SELECT '解除药材映射', 50, 'pharmacy.medicineMapping/unlink' ) AS `permission_node` WHERE @medicine_mapping_menu_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM `zyt_system_menu` AS `existing_menu` WHERE `existing_menu`.`perms` = `permission_node`.`perms` ); -- New clients use this explicit unified-upload permission. The historical -- submitGancaoRecipel node remains valid and its controller still routes by ship_mode. SET @prescription_order_menu_id := ( SELECT `id` FROM `zyt_system_menu` WHERE `perms` = 'tcm.prescriptionOrder/lists' LIMIT 1 ); INSERT INTO `zyt_system_menu` ( `pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time` ) SELECT @prescription_order_menu_id, 'A', '上传药房', '', 10, 'tcm.prescriptionOrder/uploadToPharmacy', '', '', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() FROM DUAL WHERE @prescription_order_menu_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM `zyt_system_menu` WHERE `perms` = 'tcm.prescriptionOrder/uploadToPharmacy' ); -- Historical route retained as a controlled compatibility alias for the same pharmacy-upload capability. INSERT INTO `zyt_system_menu` ( `pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time` ) SELECT @prescription_order_menu_id, 'A', '上传药房(兼容入口)', '', 11, 'tcm.prescriptionOrder/submitGancaoRecipel', '', '', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() FROM DUAL WHERE @prescription_order_menu_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM `zyt_system_menu` WHERE `perms` = 'tcm.prescriptionOrder/submitGancaoRecipel' ); -- Manual reconciliation can finalize an uncertain remote result or release it for retry, -- so it requires a separate high-risk permission from ordinary pharmacy upload. INSERT INTO `zyt_system_menu` ( `pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time` ) SELECT @prescription_order_menu_id, 'A', '人工核对甘草提交', '', 12, 'tcm.prescriptionOrder/confirmGancaoSubmission', '', '', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() FROM DUAL WHERE @prescription_order_menu_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM `zyt_system_menu` WHERE `perms` = 'tcm.prescriptionOrder/confirmGancaoSubmission' );