Files
zyt/artifacts/prescription-ai-runtime/apply_progress_migration.php
T
2026-09-10 15:19:17 +08:00

51 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
// Local-only additive schema upgrade. Never prints connection secrets or patient content.
$root = dirname(__DIR__, 2) . '/server/';
chdir($root);
require $root . 'vendor/autoload.php';
$app = new \think\App($root);
$app->initialize();
use think\facade\Db;
try {
$connection = (string) config('database.default');
$cfg = (array) config('database.connections.' . $connection);
if (!in_array((string) ($cfg['hostname'] ?? ''), ['127.0.0.1', 'localhost', '::1'], true)
|| ($cfg['prefix'] ?? '') !== 'zyt_') {
throw new RuntimeException('Local database/prefix guard rejected');
}
$active = Db::name('prescription_ai_task')->whereIn('status', ['queued', 'running', 'retry_wait'])->count()
+ Db::name('prescription_ai_batch')->where('validity', 'current')->whereIn('status', ['preparing', 'waiting_sources', 'queued', 'running', 'retry_wait'])->count();
if ($active > 0) { throw new RuntimeException('Tasks must finish before this local upgrade'); }
$snapshot = static function (): array {
return [
'batches' => Db::name('prescription_ai_batch')->count(),
'results' => Db::name('prescription_ai_result')->count(),
'attempts' => Db::name('prescription_ai_attempt')->count(),
'tasks_hash' => hash('sha256', json_encode(Db::name('prescription_ai_task')->field('id,status,attempts,total_attempts,manual_retries,result_id')->order('id')->select()->toArray())),
];
};
$before = $snapshot();
$present = isset(Db::name('prescription_ai_task')->getFields()['progress_json']);
$sqlFile = $root . 'database/migrations/2026_09_10_prescription_ai_progress.sql';
$result = ['time' => date('c'), 'local_database' => true, 'column_previously_present' => $present,
'migration_sha256' => hash_file('sha256', $sqlFile), 'applied' => false];
if (in_array('--apply', $argv, true)) {
Db::execute('SET SESSION lock_wait_timeout = 5');
foreach (explode(';', preg_replace('/^--.*$/m', '', file_get_contents($sqlFile))) as $statement) {
if (trim($statement) !== '') { Db::execute($statement); }
}
$columns = Db::query("SHOW COLUMNS FROM `zyt_prescription_ai_task` LIKE 'progress_json'");
if (count($columns) !== 1 || $columns[0]['Type'] !== 'varchar(2048)' || $columns[0]['Null'] !== 'YES') {
throw new RuntimeException('Unexpected progress column definition');
}
$result['applied'] = true;
$result['business_state_unchanged'] = $before === $snapshot();
if (!$result['business_state_unchanged']) { throw new RuntimeException('Business task state changed during upgrade'); }
}
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), PHP_EOL;
} catch (Throwable $e) {
echo json_encode(['error' => get_class($e), 'code' => $e->getCode()]), PHP_EOL;
exit(1);
}