*/ public static array $uris = []; public function __construct(int $adminId = 0) { } /** @return array */ public function getAdminUri(): array { return self::$uris; } } function prescriptionPermissionExpect(bool $condition, string $message): void { if (!$condition) { fwrite(STDERR, "FAIL: {$message}\n"); exit(1); } } prescriptionPermissionExpect( class_alias(PrescriptionGenerateAuthCacheDouble::class, 'app\\common\\cache\\AdminAuthCache'), 'permission cache double is installed before logic autoload' ); $reflection = new ReflectionClass(DiagnosisAiLogic::class); prescriptionPermissionExpect( $reflection->getConstant('PERMISSION_GENERATE_PRESCRIPTION') === 'tcm.diagnosis/aigenerateprescription', 'prescription generation has a dedicated permission' ); PrescriptionGenerateAuthCacheDouble::$uris = ['tcm.diagnosis/aiAssistant']; prescriptionPermissionExpect( DiagnosisAiLogic::prepareAssistant(1, 'prescription_generate', '', 7, ['root' => 0]) === null, 'general assistant permission alone cannot generate a prescription' ); prescriptionPermissionExpect( DiagnosisAiLogic::getError() === '权限不足,无法使用 AI 生成处方', 'denial names the missing prescription permission' ); prescriptionPermissionExpect( DiagnosisAiLogic::assistant(1, 'prescription_generate', '', 7, ['root' => 0]) === null, 'blocking endpoint rejects the task before any upstream call' ); prescriptionPermissionExpect( DiagnosisAiLogic::prepareAssistant(1, 'PRESCRIPTION_GENERATE', '', 7, ['root' => 0]) === null && DiagnosisAiLogic::getError() === '权限不足,无法使用 AI 生成处方', 'task name normalization cannot bypass the dedicated permission' ); prescriptionPermissionExpect( DiagnosisAiLogic::prepareAssistant(0, 'summary', '', 7, ['root' => 0]) === null && DiagnosisAiLogic::getError() === '诊单ID必须大于0', 'ordinary assistant tasks continue past the prescription permission check' ); PrescriptionGenerateAuthCacheDouble::$uris = ['tcm.diagnosis/aiGeneratePrescription']; prescriptionPermissionExpect( DiagnosisAiLogic::prepareAssistant(1, 'prescription_generate', '', 7, ['root' => 0]) === null, 'prescription permission alone does not bypass the general assistant permission' ); prescriptionPermissionExpect( DiagnosisAiLogic::getError() === '权限不足,无法使用诊单 AI 助手', 'general assistant permission remains required' ); $hasPermission = $reflection->getMethod('hasPermission'); prescriptionPermissionExpect( $hasPermission->invoke(null, 7, ['root' => 0], 'tcm.diagnosis/aigenerateprescription') === true, 'new permission is read from role permissions' ); prescriptionPermissionExpect( $hasPermission->invoke(null, 1, ['root' => 1], 'tcm.diagnosis/aigenerateprescription') === true, 'root keeps its existing permission bypass' ); $migration = file_get_contents( dirname(__DIR__) . '/database/migrations/2026_10_08_diagnosis_ai_generate_prescription_permission.sql' ); prescriptionPermissionExpect( is_string($migration) && str_contains($migration, "'tcm.diagnosis/aiGeneratePrescription'") && str_contains($migration, "'AI生成处方'"), 'management role tree registers the dedicated checkbox' ); prescriptionPermissionExpect( !str_contains($migration, 'zyt_system_role_menu'), 'migration does not silently grant the new permission to existing roles' ); echo "Diagnosis AI prescription generation permission: OK\n";