$id, 'diagnosis_id' => 91, 'patient_id' => 101]; } } } namespace { require dirname(__DIR__) . '/vendor/autoload.php'; $app = new \think\App(dirname(__DIR__) . '/'); $app->initialize(); set_exception_handler(static function (\Throwable $e): void { fwrite(STDERR, $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL); exit(1); }); $app->config->set(['default' => 'mysql', 'connections' => ['mysql' => [ 'type' => 'mysql', 'hostname' => '127.0.0.1', 'hostport' => 23316, 'database' => 'followup_audio_test', 'username' => 'root', 'password' => 'followup_audio_isolated_test', 'charset' => 'utf8mb4', 'prefix' => 'faa_', 'debug' => false, ]]], 'database'); $db = \think\facade\Db::class; $tables = [ 'admin' => 'id BIGINT PRIMARY KEY, name VARCHAR(30), root INT, disable INT, delete_time BIGINT NULL', 'admin_role' => 'admin_id BIGINT, role_id BIGINT, PRIMARY KEY(admin_id,role_id)', 'admin_dept' => 'admin_id BIGINT, dept_id BIGINT, PRIMARY KEY(admin_id,dept_id)', 'system_role_menu' => 'role_id BIGINT, menu_id BIGINT, PRIMARY KEY(role_id,menu_id)', 'system_menu' => 'id BIGINT PRIMARY KEY, perms VARCHAR(100), is_disable INT', 'tcm_diagnosis' => 'id BIGINT PRIMARY KEY, patient_id BIGINT, assistant_id BIGINT, status INT, delete_time BIGINT NULL', ]; foreach ($tables as $name => $fields) { $db::execute("DROP TABLE IF EXISTS faa_{$name}"); $db::execute("CREATE TABLE faa_{$name} ({$fields}) ENGINE=InnoDB"); } $db::name('admin')->insertAll([ ['id' => 7, 'name' => 'root fixture', 'root' => 1, 'disable' => 0], ['id' => 8, 'name' => 'assistant fixture', 'root' => 0, 'disable' => 0], ['id' => 9, 'name' => 'other fixture', 'root' => 0, 'disable' => 0], ]); $db::name('admin_role')->insert(['admin_id' => 8, 'role_id' => 2]); $db::name('system_menu')->insertAll([ ['id' => 1, 'perms' => 'tcm.diagnosis/edit', 'is_disable' => 0], ['id' => 2, 'perms' => 'tcm.diagnosis/dailyRecord', 'is_disable' => 0], ]); $db::name('system_role_menu')->insert(['role_id' => 2, 'menu_id' => 1]); $db::name('tcm_diagnosis')->insertAll([ ['id' => 91, 'patient_id' => 101, 'assistant_id' => 8, 'status' => 1], ['id' => 92, 'patient_id' => 102, 'assistant_id' => 9, 'status' => 1], ]); $a = \app\common\service\followupaudio\FollowupAudioAccess::class; $checks = 0; $ok = function (bool $yes, string $label) use (&$checks): void { if (!$yes) { throw new \RuntimeException($label); } $checks++; }; $deny = function (callable $f, string $label) use ($ok): void { try { $f(); } catch (\DomainException $e) { $ok(true, $label); return; } $ok(false, $label); }; $other = new \PDO('mysql:host=127.0.0.1;port=23316;dbname=followup_audio_test;charset=utf8mb4', 'root', 'followup_audio_isolated_test', [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); try { $ok((int) $a::diagnosis(91, 8, ['root' => 1])['id'] === 91, 'own patient'); $deny(fn () => $a::diagnosis(92, 8, ['root' => 1]), 'forged cached root must fail'); $ok(!$a::canDaily(8, ['root' => 1]), 'missing daily permission'); $deny(fn () => $a::diagnosis(91, 8, [], true), 'daily write denied'); $db::name('system_role_menu')->insert(['role_id' => 2, 'menu_id' => 2]); $ok($a::canDaily(8, []), 'fresh daily grant'); // A consistent read exists before another connection commits permission revocation. $db::startTrans(); $db::name('system_role_menu')->select(); $other->exec('DELETE FROM faa_system_role_menu WHERE role_id=2 AND menu_id=2'); $ok(!$a::canDaily(8, []), 'revocation must beat repeatable-read snapshot'); $db::rollback(); $db::startTrans(); $db::name('tcm_diagnosis')->where('id', 91)->find(); $other->exec('UPDATE faa_tcm_diagnosis SET assistant_id=9 WHERE id=91'); $deny(fn () => $a::diagnosis(91, 8, []), 'reassignment must beat repeatable-read snapshot'); $db::rollback(); $other->exec('UPDATE faa_tcm_diagnosis SET assistant_id=8 WHERE id=91'); $db::startTrans(); $db::name('admin')->where('id', 8)->find(); $other->exec('UPDATE faa_admin SET disable=1 WHERE id=8'); $deny(fn () => $a::diagnosis(91, 8, []), 'disabled actor must beat repeatable-read snapshot'); $db::rollback(); $other->exec('UPDATE faa_admin SET disable=0 WHERE id=8'); $ok((int) $a::task(1, 8, [])['id'] === 1, 'task binding before rebind'); $other->exec('UPDATE faa_tcm_diagnosis SET patient_id=999 WHERE id=91'); $deny(fn () => $a::task(1, 8, []), 'historical recording must not follow patient rebind'); $other->exec('UPDATE faa_tcm_diagnosis SET status=0 WHERE id=91'); $deny(fn () => $a::diagnosis(91, 7, ['root' => 1]), 'inactive diagnosis even root'); echo "Followup audio real authorization: {$checks} checks passed (isolated MySQL; current-read revocation/reassignment)\n"; } finally { try { $db::rollback(); } catch (\Throwable $e) {} foreach (array_keys($tables) as $name) { $db::execute("DROP TABLE IF EXISTS faa_{$name}"); } } }