ai-terminal-mvp.sql 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. -- ============================================================
  2. -- 医梦 AI 中台 统一入口客户端 MVP 数据库迁移
  3. -- 版本: 2026-05-31
  4. -- 原则: 不 DROP 现有表,仅 ALTER 补字段 + CREATE 新表
  5. -- ============================================================
  6. -- -----------------------------------------------------------
  7. -- 1. 新表:设备注册中心
  8. -- -----------------------------------------------------------
  9. CREATE TABLE IF NOT EXISTS ai_device_registry (
  10. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  11. device_id VARCHAR(64) NOT NULL,
  12. project_id VARCHAR(64) NOT NULL,
  13. hospital_id VARCHAR(64) NOT NULL,
  14. device_type VARCHAR(64) NOT NULL,
  15. management_mode VARCHAR(32) NOT NULL DEFAULT 'emoon_managed',
  16. vendor VARCHAR(64),
  17. model VARCHAR(128),
  18. location_json JSON,
  19. capabilities_json JSON,
  20. status VARCHAR(32) NOT NULL DEFAULT 'pending',
  21. admission_level VARCHAR(8) NOT NULL DEFAULT 'B',
  22. client_version VARCHAR(64),
  23. last_heartbeat DATETIME,
  24. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  25. updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  26. UNIQUE KEY uk_device_id (device_id),
  27. KEY idx_project_hospital (project_id, hospital_id)
  28. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  29. -- -----------------------------------------------------------
  30. -- 2. 新表:设备场景绑定
  31. -- -----------------------------------------------------------
  32. CREATE TABLE IF NOT EXISTS ai_device_scene_binding (
  33. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  34. device_id VARCHAR(64) NOT NULL,
  35. scene_code VARCHAR(64) NOT NULL,
  36. ui_template VARCHAR(64) NOT NULL,
  37. agent_bindings_json JSON NOT NULL,
  38. tool_scopes_json JSON,
  39. card_scopes_json JSON,
  40. status VARCHAR(32) NOT NULL DEFAULT 'enabled',
  41. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  42. updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  43. UNIQUE KEY uk_device_scene (device_id, scene_code)
  44. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  45. -- -----------------------------------------------------------
  46. -- 3. 新表:设备事件日志
  47. -- -----------------------------------------------------------
  48. CREATE TABLE IF NOT EXISTS ai_device_event (
  49. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  50. event_id VARCHAR(96) NOT NULL,
  51. device_id VARCHAR(64) NOT NULL,
  52. event_type VARCHAR(64) NOT NULL,
  53. event_payload JSON,
  54. trace_id VARCHAR(96),
  55. occurred_at DATETIME NOT NULL,
  56. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  57. UNIQUE KEY uk_device_event (device_id, event_id),
  58. KEY idx_device_time (device_id, occurred_at)
  59. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  60. -- -----------------------------------------------------------
  61. -- 4. 新表:设备命令
  62. -- -----------------------------------------------------------
  63. CREATE TABLE IF NOT EXISTS ai_device_command (
  64. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  65. command_id VARCHAR(96) NOT NULL,
  66. device_id VARCHAR(64) NOT NULL,
  67. command_type VARCHAR(64) NOT NULL,
  68. payload_json JSON NOT NULL,
  69. status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
  70. expire_at DATETIME NOT NULL,
  71. ack_at DATETIME,
  72. ack_result_json JSON,
  73. trace_id VARCHAR(96),
  74. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  75. UNIQUE KEY uk_command_id (command_id),
  76. KEY idx_device_status (device_id, status)
  77. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  78. -- -----------------------------------------------------------
  79. -- 5. 新表:会话消息
  80. -- -----------------------------------------------------------
  81. CREATE TABLE IF NOT EXISTS ai_conversation_message (
  82. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  83. message_id VARCHAR(96) NOT NULL,
  84. conversation_id VARCHAR(96) NOT NULL,
  85. role VARCHAR(32) NOT NULL,
  86. content TEXT,
  87. event_json JSON,
  88. trace_id VARCHAR(96),
  89. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  90. UNIQUE KEY uk_message_id (message_id),
  91. KEY idx_conv_time (conversation_id, created_at)
  92. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  93. -- -----------------------------------------------------------
  94. -- 6. 新表:任务实例
  95. -- -----------------------------------------------------------
  96. CREATE TABLE IF NOT EXISTS ai_task_instance (
  97. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  98. task_id VARCHAR(96) NOT NULL,
  99. project_id VARCHAR(64) NOT NULL,
  100. conversation_id VARCHAR(96) NOT NULL,
  101. task_type VARCHAR(64) NOT NULL,
  102. status VARCHAR(32) NOT NULL,
  103. current_step VARCHAR(64) NOT NULL,
  104. agent_code VARCHAR(64),
  105. context_json JSON,
  106. patient_id VARCHAR(96),
  107. device_id VARCHAR(64),
  108. trace_id VARCHAR(96),
  109. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  110. updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  111. UNIQUE KEY uk_task_id (task_id),
  112. KEY idx_project (project_id),
  113. KEY idx_conv_status (conversation_id, status)
  114. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  115. -- -----------------------------------------------------------
  116. -- 7. 新表:文件对象
  117. -- -----------------------------------------------------------
  118. CREATE TABLE IF NOT EXISTS ai_file_object (
  119. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  120. file_id VARCHAR(96) NOT NULL,
  121. project_id VARCHAR(64) NOT NULL,
  122. hospital_id VARCHAR(64),
  123. device_id VARCHAR(64),
  124. business_type VARCHAR(64) NOT NULL,
  125. sha256 VARCHAR(128),
  126. storage_path VARCHAR(512) NOT NULL,
  127. retention_policy VARCHAR(32) NOT NULL,
  128. sensitive_level VARCHAR(32) NOT NULL,
  129. status VARCHAR(32) NOT NULL DEFAULT 'available',
  130. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  131. UNIQUE KEY uk_file_id (file_id),
  132. KEY idx_project_type (project_id, business_type)
  133. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  134. -- -----------------------------------------------------------
  135. -- 7.5 新表:卡片动作日志(幂等审计)
  136. -- -----------------------------------------------------------
  137. CREATE TABLE IF NOT EXISTS ai_card_action_log (
  138. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  139. action_id VARCHAR(96) NOT NULL,
  140. card_instance_id VARCHAR(96) NOT NULL,
  141. action_name VARCHAR(64) NOT NULL,
  142. idempotency_key VARCHAR(128) NOT NULL,
  143. action_payload_json JSON,
  144. status VARCHAR(32) NOT NULL DEFAULT 'submitted',
  145. trace_id VARCHAR(96),
  146. created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  147. UNIQUE KEY uk_idempotency (idempotency_key),
  148. KEY idx_card_action (card_instance_id, action_name)
  149. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  150. -- -----------------------------------------------------------
  151. -- 8. 存量表 ai_conversation 补字段
  152. -- 现有实体: com.emoon.mcp.domain.AiConversation
  153. -- 已有字段: id, projectId, agentId, conversationId, status 等
  154. -- -----------------------------------------------------------
  155. DROP PROCEDURE IF EXISTS emoon_add_column_if_absent;
  156. DELIMITER //
  157. CREATE PROCEDURE emoon_add_column_if_absent(
  158. IN p_table_name VARCHAR(128),
  159. IN p_column_name VARCHAR(128),
  160. IN p_ddl TEXT
  161. )
  162. BEGIN
  163. IF NOT EXISTS (
  164. SELECT 1 FROM information_schema.columns
  165. WHERE table_schema = DATABASE()
  166. AND table_name = p_table_name
  167. AND column_name = p_column_name
  168. ) THEN
  169. SET @sql = p_ddl;
  170. PREPARE stmt FROM @sql;
  171. EXECUTE stmt;
  172. DEALLOCATE PREPARE stmt;
  173. END IF;
  174. END//
  175. DELIMITER ;
  176. DROP PROCEDURE IF EXISTS emoon_add_index_if_absent;
  177. DELIMITER //
  178. CREATE PROCEDURE emoon_add_index_if_absent(
  179. IN p_table_name VARCHAR(128),
  180. IN p_index_name VARCHAR(128),
  181. IN p_ddl TEXT
  182. )
  183. BEGIN
  184. IF NOT EXISTS (
  185. SELECT 1 FROM information_schema.statistics
  186. WHERE table_schema = DATABASE()
  187. AND table_name = p_table_name
  188. AND index_name = p_index_name
  189. ) THEN
  190. SET @sql = p_ddl;
  191. PREPARE stmt FROM @sql;
  192. EXECUTE stmt;
  193. DEALLOCATE PREPARE stmt;
  194. END IF;
  195. END//
  196. DELIMITER ;
  197. CALL emoon_add_column_if_absent('ai_conversation', 'hospital_id',
  198. 'ALTER TABLE ai_conversation ADD COLUMN hospital_id VARCHAR(64) NULL COMMENT ''医院标识''');
  199. CALL emoon_add_column_if_absent('ai_conversation', 'device_id',
  200. 'ALTER TABLE ai_conversation ADD COLUMN device_id VARCHAR(64) NULL COMMENT ''统一入口终端设备ID''');
  201. CALL emoon_add_column_if_absent('ai_conversation', 'client_message_id',
  202. 'ALTER TABLE ai_conversation ADD COLUMN client_message_id VARCHAR(96) NULL COMMENT ''客户端消息幂等ID''');
  203. CALL emoon_add_index_if_absent('ai_conversation', 'idx_ai_conversation_device',
  204. 'CREATE INDEX idx_ai_conversation_device ON ai_conversation(device_id)');
  205. -- -----------------------------------------------------------
  206. -- 9. 存量表 ai_card_instance 补字段
  207. -- 现有实体: com.emoon.mcp.domain.AiCardInstance
  208. -- 已有字段: id, conversationId, cardKey, instanceId, status 等
  209. -- -----------------------------------------------------------
  210. CALL emoon_add_column_if_absent('ai_card_instance', 'task_id',
  211. 'ALTER TABLE ai_card_instance ADD COLUMN task_id VARCHAR(96) NULL COMMENT ''统一入口终端任务ID''');
  212. CALL emoon_add_column_if_absent('ai_card_instance', 'trace_id',
  213. 'ALTER TABLE ai_card_instance ADD COLUMN trace_id VARCHAR(96) NULL COMMENT ''链路追踪ID''');
  214. CALL emoon_add_index_if_absent('ai_card_instance', 'idx_ai_card_instance_task',
  215. 'CREATE INDEX idx_ai_card_instance_task ON ai_card_instance(task_id)');
  216. -- -----------------------------------------------------------
  217. -- 10. 存量表 ai_card_definition 补索引
  218. -- 现有实体: com.emoon.system.domain.AiCardDefinition
  219. -- 已有字段: cardKey, version, name, schemaJson, actionsJson, status, isSystem, isLatest
  220. -- -----------------------------------------------------------
  221. CALL emoon_add_index_if_absent('ai_card_definition', 'idx_ai_card_def_key',
  222. 'CREATE INDEX idx_ai_card_def_key ON ai_card_definition(card_key)');
  223. DROP PROCEDURE IF EXISTS emoon_add_column_if_absent;
  224. DROP PROCEDURE IF EXISTS emoon_add_index_if_absent;
  225. -- -----------------------------------------------------------
  226. -- 11. Seed 数据:核心卡片定义
  227. -- -----------------------------------------------------------
  228. INSERT INTO ai_card_definition(card_key, version, name, schema_json, actions_json, status, is_system, is_latest)
  229. VALUES
  230. ('department-selection', '1.0', '科室选择', JSON_OBJECT('required', JSON_ARRAY('departments')), JSON_ARRAY(JSON_OBJECT('actionName', 'select_department')), '0', '1', TRUE),
  231. ('doctor-selection', '1.0', '医生选择', JSON_OBJECT('required', JSON_ARRAY('doctors')), JSON_ARRAY(JSON_OBJECT('actionName', 'select_doctor')), '0', '1', TRUE),
  232. ('time-slot-selection', '1.0', '号源选择', JSON_OBJECT('required', JSON_ARRAY('slots')), JSON_ARRAY(JSON_OBJECT('actionName', 'select_time_slot')), '0', '1', TRUE),
  233. ('confirm-appointment', '1.0', '挂号确认', JSON_OBJECT('required', JSON_ARRAY('summary')), JSON_ARRAY(JSON_OBJECT('actionName', 'confirm_appointment', 'requiredConfirm', TRUE)), '0', '1', TRUE),
  234. ('appointment-success', '1.0', '挂号成功', JSON_OBJECT('required', JSON_ARRAY('appointmentNo')), JSON_ARRAY(), '0', '1', TRUE),
  235. ('route-card', '1.0', '路线导航', JSON_OBJECT('required', JSON_ARRAY('routeText')), JSON_ARRAY(JSON_OBJECT('actionName', 'start_navigation')), '0', '1', TRUE),
  236. ('tongue-capture', '1.0', '舌象采集', JSON_OBJECT('required', JSON_ARRAY('uploadField')), JSON_ARRAY(JSON_OBJECT('actionName', 'submit_tongue_image')), '0', '1', TRUE),
  237. ('tongue-diagnosis-result', '1.0', '舌诊结果', JSON_OBJECT('required', JSON_ARRAY('summary')), JSON_ARRAY(), '0', '1', TRUE)
  238. ON DUPLICATE KEY UPDATE
  239. name = VALUES(name),
  240. schema_json = VALUES(schema_json),
  241. actions_json = VALUES(actions_json),
  242. status = VALUES(status),
  243. is_system = VALUES(is_system),
  244. is_latest = VALUES(is_latest);
  245. -- -----------------------------------------------------------
  246. -- 12. Seed 数据:自助机设备和场景
  247. -- -----------------------------------------------------------
  248. INSERT IGNORE INTO ai_device_registry(device_id, project_id, hospital_id, device_type, status, admission_level, capabilities_json)
  249. VALUES ('EMOON-KIOSK-001', 'hospital_demo', 'H001', 'self_service_kiosk', 'activated', 'A',
  250. JSON_ARRAY('touch','camera','id_card_ocr','file_upload'));
  251. INSERT IGNORE INTO ai_device_scene_binding(device_id, scene_code, ui_template, agent_bindings_json, card_scopes_json)
  252. VALUES ('EMOON-KIOSK-001', 'outpatient_kiosk', 'kiosk_home_v1',
  253. JSON_OBJECT('defaultAgent','opd-guide-agent','allowedAgents',JSON_ARRAY('opd-guide-agent','opd-triage-agent','opd-registration-agent','tongue-diagnosis-agent')),
  254. JSON_ARRAY('department-selection','doctor-selection','time-slot-selection','confirm-appointment','appointment-success','tongue-capture','tongue-diagnosis-result'));