Files
zyt/server/database/migrations/2026_09_09_prescription_ai_analysis.sql
T
2026-09-10 15:19:17 +08:00

161 lines
7.4 KiB
SQL

-- Clinician-only asynchronous prescription analysis; no automatic prescription writes.
-- Apply before enabling prescription_analysis.ENABLED. Existing report tables are unchanged.
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_subject` (
`prescription_id` bigint unsigned NOT NULL,
`revision` int unsigned NOT NULL DEFAULT 1,
`clinical_hash` char(64) NOT NULL,
`first_batch_id` bigint unsigned NOT NULL DEFAULT 0,
`latest_batch_id` bigint unsigned NOT NULL DEFAULT 0,
`updated_at` int unsigned NOT NULL,
PRIMARY KEY (`prescription_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_batch` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`event_key` char(64) NOT NULL,
`prescription_id` bigint unsigned NOT NULL,
`prescription_revision` int unsigned NOT NULL,
`clinical_hash` char(64) NOT NULL,
`patient_id` bigint unsigned NOT NULL DEFAULT 0,
`diagnosis_id` bigint unsigned NOT NULL DEFAULT 0,
`doctor_id` int unsigned NOT NULL DEFAULT 0,
`actor_id` int unsigned NOT NULL,
`trigger_type` varchar(40) NOT NULL,
`reason` varchar(500) NOT NULL DEFAULT '',
`ai_assisted` varchar(16) NOT NULL DEFAULT 'unknown',
`status` varchar(32) NOT NULL DEFAULT 'preparing',
`validity` varchar(32) NOT NULL DEFAULT 'current',
`comparison_type` varchar(40) NOT NULL DEFAULT 'latest_context',
`baseline_eligible` tinyint NOT NULL DEFAULT 0,
`baseline_exclusions_json` text NULL,
`prescription_cipher` longtext NOT NULL,
`context_cipher` longtext NULL,
`access_cipher` longtext NULL,
`source_hash` char(64) NOT NULL DEFAULT '',
`source_diagnosis_ids_json` text NULL,
`source_summary_json` text NULL,
`missing_json` text NULL,
`coverage_status` varchar(32) NOT NULL DEFAULT 'pending',
`cutoff_at` int unsigned NOT NULL DEFAULT 0,
`decision_at` int unsigned NOT NULL,
`wait_until` int unsigned NOT NULL,
`next_run_at` int unsigned NOT NULL,
`prepare_attempts` int unsigned NOT NULL DEFAULT 0,
`lock_token` varchar(64) NOT NULL DEFAULT '',
`lock_until` int unsigned NOT NULL DEFAULT 0,
`error_code` varchar(64) NOT NULL DEFAULT '',
`created_at` int unsigned NOT NULL,
`updated_at` int unsigned NOT NULL,
PRIMARY KEY (`id`), UNIQUE KEY `uk_event` (`event_key`),
KEY `idx_due` (`status`,`next_run_at`,`lock_until`),
KEY `idx_rx` (`prescription_id`,`id`),
KEY `idx_patient` (`patient_id`,`created_at`,`id`),
KEY `idx_diagnosis` (`diagnosis_id`,`id`),
KEY `idx_doctor` (`doctor_id`,`created_at`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_task` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`batch_id` bigint unsigned NOT NULL,
`model_key` varchar(16) NOT NULL,
`status` varchar(32) NOT NULL DEFAULT 'queued',
`attempts` int unsigned NOT NULL DEFAULT 0,
`total_attempts` int unsigned NOT NULL DEFAULT 0,
`manual_retries` int unsigned NOT NULL DEFAULT 0,
`next_run_at` int unsigned NOT NULL,
`lock_token` varchar(64) NOT NULL DEFAULT '',
`lock_until` int unsigned NOT NULL DEFAULT 0,
`progress_cipher` longtext NULL,
`error_code` varchar(64) NOT NULL DEFAULT '',
`result_id` bigint unsigned NOT NULL DEFAULT 0,
`started_at` int unsigned NOT NULL DEFAULT 0,
`finished_at` int unsigned NOT NULL DEFAULT 0,
`updated_at` int unsigned NOT NULL,
PRIMARY KEY (`id`), UNIQUE KEY `uk_batch_model` (`batch_id`,`model_key`),
KEY `idx_due` (`model_key`,`status`,`next_run_at`,`lock_until`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_result` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`batch_id` bigint unsigned NOT NULL,
`model_key` varchar(16) NOT NULL,
`body_cipher` longtext NOT NULL,
`score` decimal(10,6) NULL,
`herb_score` decimal(10,6) NULL,
`comparison_status` varchar(32) NOT NULL,
`comparison_reason_code` varchar(64) NOT NULL DEFAULT '',
`coverage_status` varchar(32) NOT NULL,
`model_name` varchar(100) NOT NULL DEFAULT '',
`prompt_version` varchar(100) NOT NULL DEFAULT '',
`algorithm_version` varchar(100) NOT NULL DEFAULT '',
`dictionary_version` varchar(100) NOT NULL DEFAULT '',
`generated_at` int unsigned NOT NULL,
PRIMARY KEY (`id`), UNIQUE KEY `uk_result` (`batch_id`,`model_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_review` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`result_id` bigint unsigned NOT NULL,
`admin_id` int unsigned NOT NULL,
`status` varchar(32) NOT NULL,
`comment_cipher` text NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`), KEY `idx_result` (`result_id`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_attempt` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`task_id` bigint unsigned NOT NULL,
`attempt_no` int unsigned NOT NULL,
`status` varchar(32) NOT NULL,
`error_code` varchar(64) NOT NULL DEFAULT '',
`started_at` int unsigned NOT NULL,
`finished_at` int unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), UNIQUE KEY `uk_attempt` (`task_id`,`attempt_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_limit` (
`limit_key` varchar(100) NOT NULL,
`used_count` int unsigned NOT NULL DEFAULT 0,
`updated_at` int unsigned NOT NULL,
PRIMARY KEY (`limit_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `zyt_prescription_ai_request` (
`request_key` char(64) NOT NULL,
`actor_id` int unsigned NOT NULL,
`request_hash` char(64) NOT NULL,
`prescription_id` bigint unsigned NOT NULL DEFAULT 0,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`request_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Exact permission checks also live in logic; missing menu rows never grant access.
SET @rx_ai_parent := (SELECT id FROM zyt_system_menu WHERE perms='tcm.prescription/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 COALESCE(@rx_ai_parent,0),'A',p.label,'',85,p.perm,'','','','',0,1,0,UNIX_TIMESTAMP(),UNIX_TIMESTAMP()
FROM (
SELECT '处方AI状态' label,'tcm.prescriptionAi/statuses' perm UNION ALL
SELECT '处方AI历史','tcm.prescriptionAi/reports' UNION ALL
SELECT '处方AI报告','tcm.prescriptionAi/detail' UNION ALL
SELECT '重新分析处方','tcm.prescriptionAi/regenerate' UNION ALL
SELECT '重试处方AI','tcm.prescriptionAi/retry' UNION ALL
SELECT '复核处方AI','tcm.prescriptionAi/review' UNION ALL
SELECT '处方AI医生统计','tcm.prescriptionAi/statistics'
) p WHERE NOT EXISTS (SELECT 1 FROM zyt_system_menu m WHERE m.perms=p.perm);
-- Preserve the existing distinction between AI reading and generation permissions.
INSERT IGNORE INTO zyt_system_role_menu (role_id,menu_id)
SELECT DISTINCT rm.role_id, target.id FROM zyt_system_role_menu rm
JOIN zyt_system_menu old ON old.id=rm.menu_id
JOIN zyt_system_menu target ON target.perms IN
('tcm.prescriptionAi/statuses','tcm.prescriptionAi/reports','tcm.prescriptionAi/detail','tcm.prescriptionAi/statistics')
WHERE old.perms='tcm.diagnosis/patientAiReports';
INSERT IGNORE INTO zyt_system_role_menu (role_id,menu_id)
SELECT DISTINCT rm.role_id, target.id FROM zyt_system_role_menu rm
JOIN zyt_system_menu old ON old.id=rm.menu_id
JOIN zyt_system_menu target ON target.perms IN
('tcm.prescriptionAi/regenerate','tcm.prescriptionAi/retry','tcm.prescriptionAi/review')
WHERE old.perms='tcm.diagnosis/generatePatientAiReport';