PDO::ERRMODE_EXCEPTION]); $actual = str_replace('\\', '/', (string) $pdo->query('SELECT @@datadir')->fetchColumn()); if (strtolower(rtrim($actual, '/')) !== strtolower(rtrim($expectedDirectory, '/'))) { throw new RuntimeException('数据目录不匹配,拒绝创建测试数据库'); } $database = 'daily_contact_test_' . bin2hex(random_bytes(6)); $checks = 0; $ok = static function (bool $condition, string $message) use (&$checks): void { if (!$condition) { throw new RuntimeException($message); } $checks++; }; $sql = preg_replace('/^--.*$/m', '', (string) file_get_contents(dirname(__DIR__) . '/sql/1.9.20261110/01_assistant_service_daily_contact.sql')); $migrate = static function () use ($pdo, $sql): void { foreach (explode(';', (string) $sql) as $statement) { if (trim($statement) !== '') { $query = $pdo->query($statement); $query->closeCursor(); } } }; $pdo->exec("CREATE DATABASE `{$database}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); try { $pdo->exec("USE `{$database}`"); $pdo->exec('CREATE TABLE zyt_assistant_service_action(id BIGINT UNSIGNED PRIMARY KEY,diagnosis_id INT,assignee_id INT,business_state VARCHAR(20),completed_at INT,completion_note VARCHAR(200)) ENGINE=InnoDB'); $pdo->exec("INSERT INTO zyt_assistant_service_action VALUES(1,101,11,'completed',1234,'历史哨兵')"); $pdo->exec('CREATE TABLE zyt_tcm_diagnosis_todo(id INT PRIMARY KEY,status INT,notified_at INT) ENGINE=InnoDB'); $pdo->exec('INSERT INTO zyt_tcm_diagnosis_todo VALUES(1,1,1234)'); $before = $pdo->query('SELECT * FROM zyt_assistant_service_action')->fetchAll(PDO::FETCH_ASSOC); $todos = $pdo->query('SELECT * FROM zyt_tcm_diagnosis_todo')->fetchAll(PDO::FETCH_ASSOC); $migrate(); $columns = $pdo->query('SHOW COLUMNS FROM zyt_assistant_service_action')->fetchAll(PDO::FETCH_ASSOC); $columns = array_column($columns, null, 'Field'); $ok(isset($columns['contact_required'], $columns['completed_by']), '缺少明确联系标记或真实完成操作者字段'); $ok((string) $columns['contact_required']['Default'] === '0' && (string) $columns['completed_by']['Default'] === '0', '旧动作不应自动推断联系属性或操作者'); $row = $pdo->query('SELECT * FROM zyt_assistant_service_action WHERE id=1')->fetch(PDO::FETCH_ASSOC); $ok((int) $row['contact_required'] === 0 && (int) $row['completed_by'] === 0, '历史动作必须保留未知属性'); unset($row['contact_required'], $row['completed_by']); $ok($row === $before[0], '迁移不应改写历史执行记录'); $pdo->exec('UPDATE zyt_assistant_service_action SET contact_required=1,completed_by=13 WHERE id=1'); $insert = $pdo->prepare('INSERT INTO zyt_assistant_service_daily_contact(business_date,assistant_id,diagnosis_id,patient_id,source_type,source_id,source_order_id,reason,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)'); $insert->execute(['2026-11-10',11,101,201,'revisit',1,100,'合成到期',1000,1000]); $plan = $pdo->query('SELECT * FROM zyt_assistant_service_daily_contact')->fetchAll(PDO::FETCH_ASSOC); $migrate(); $migrate(); $ok($plan === $pdo->query('SELECT * FROM zyt_assistant_service_daily_contact')->fetchAll(PDO::FETCH_ASSOC), '重复迁移不应改写计划'); $row = $pdo->query('SELECT contact_required,completed_by FROM zyt_assistant_service_action WHERE id=1')->fetch(PDO::FETCH_ASSOC); $ok((int) $row['contact_required'] === 1 && (int) $row['completed_by'] === 13, '重复迁移不应覆盖新增字段真实值'); $ok($todos === $pdo->query('SELECT * FROM zyt_tcm_diagnosis_todo')->fetchAll(PDO::FETCH_ASSOC), '迁移不应改变提醒status或notified_at'); $duplicate = false; try { $insert->execute(['2026-11-10',11,101,201,'revisit',1,100,'重复',1001,1001]); } catch (PDOException $e) { $duplicate = $e->getCode() === '23000'; } $ok($duplicate, '数据库未保护当日来源幂等'); foreach ([['2026-11-10',11,101,201,'revisit',1,101], ['2026-11-11',11,101,201,'revisit',1,100], ['2026-11-10',15,101,201,'revisit',1,100], ['2026-11-10',11,101,201,'action',1,0]] as $values) { $insert->execute(array_merge($values, ['独立责任/周期',1001,1001])); } $ok((int) $pdo->query('SELECT COUNT(*) FROM zyt_assistant_service_daily_contact')->fetchColumn() === 5, '新周期、新自然日、新责任医助、另一来源须可独立入计划'); $indexes = $pdo->query('SHOW INDEX FROM zyt_assistant_service_daily_contact')->fetchAll(PDO::FETCH_ASSOC); $ok(in_array('idx_day_cursor', array_column($indexes, 'Key_name'), true) && in_array('idx_day_staff_patient', array_column($indexes, 'Key_name'), true), '缺少日游标或医助患者聚合索引'); echo 'ASSISTANT_SERVICE_DAILY_CONTACT_MIGRATION assertions=' . $checks . ' PASS mysql=' . $pdo->query('SELECT VERSION()')->fetchColumn() . ' disposable=1 migration_runs=3' . PHP_EOL; } finally { if (preg_match('/^daily_contact_test_[a-f0-9]{12}$/D', $database)) { $pdo->exec("DROP DATABASE `{$database}`"); } }