$validator->setLang($testLang)); function orderEditTimeExpect(bool $condition, string $message): void { if (!$condition) { fwrite(STDERR, "FAIL: {$message}\n"); exit(1); } } $valid = [ 'id' => 1, 'patient_id' => 0, 'order_type' => 3, 'payment_time' => '2026-08-22 11:37:27', 'create_time' => '2026-08-22 11:40:03', ]; orderEditTimeExpect( (new OrderValidate())->scene('edit_time')->check($valid), 'valid payment and creation times pass validation' ); $withoutPaymentTime = $valid; $withoutPaymentTime['payment_time'] = ''; orderEditTimeExpect( (new OrderValidate())->scene('edit_time')->check($withoutPaymentTime), 'unpaid orders may keep an empty payment time' ); $invalidPaymentTime = $valid; $invalidPaymentTime['payment_time'] = '2026-99-99 25:61:61'; orderEditTimeExpect( !(new OrderValidate())->scene('edit_time')->check($invalidPaymentTime), 'invalid payment time is rejected' ); $missingCreateTime = $valid; $missingCreateTime['create_time'] = ''; orderEditTimeExpect( !(new OrderValidate())->scene('edit_time')->check($missingCreateTime), 'creation time is required' ); $logic = file_get_contents(dirname(__DIR__) . '/app/adminapi/logic/order/OrderLogic.php'); $controller = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/order/OrderController.php'); orderEditTimeExpect(is_string($logic), 'order logic source is readable'); orderEditTimeExpect( str_contains($logic, "array_key_exists('payment_time', \$params)") && str_contains($logic, "in_array((int)\$order->status, [2, 4], true)") && str_contains($logic, 'bool $canEditTime = false') && str_contains($logic, 'normalizeEditedCreateTime('), 'order edit protects payment status and supports legacy creation timestamps' ); orderEditTimeExpect( is_string($controller) && str_contains($controller, "EDIT_TIME_PERMISSION = 'order.order/editTime'") && str_contains($controller, 'AuthLogic::getAuthByAdminId($this->adminId)') && str_contains($controller, "goCheck('edit_time')"), 'controller enforces the dedicated order time permission' ); $logicReflection = new ReflectionClass(OrderLogic::class); $normalizeCreateTime = $logicReflection->getMethod('normalizeEditedCreateTime'); orderEditTimeExpect( $normalizeCreateTime->invoke(null, 1724300000, '2026-08-22 11:40:03') === strtotime('2026-08-22 11:40:03'), 'legacy integer creation times remain Unix timestamps' ); orderEditTimeExpect( $normalizeCreateTime->invoke(null, '2026-08-22 10:00:00', '2026-08-22 11:40:03') === '2026-08-22 11:40:03', 'datetime creation times remain canonical strings' ); $adminView = file_get_contents(dirname(__DIR__, 2) . '/admin/src/views/order/index.vue'); orderEditTimeExpect(is_string($adminView), 'admin order view source is readable'); orderEditTimeExpect( str_contains($adminView, 'v-model="editOrderForm.payment_time"') && str_contains($adminView, 'v-model="editOrderForm.create_time"') && str_contains($adminView, "hasPermission(['order.order/editTime'])") && str_contains($adminView, 'v-if="canEditOrderTime"') && str_contains($adminView, 'payload.payment_time = editOrderForm.value.payment_time') && str_contains($adminView, 'payload.create_time = editOrderForm.value.create_time'), 'admin edit dialog submits payment and creation times' ); $menuSql = file_get_contents( dirname(__DIR__) . '/sql/1.9.20260901/add_order_edit_time_menu.sql' ); orderEditTimeExpect( is_string($menuSql) && str_contains($menuSql, "'order.order/editTime'") && str_contains($menuSql, "'修改支付单时间'") && !str_contains($menuSql, 'system_role_menu'), 'role management exposes the dedicated order time permission' ); echo "Order edit time contract: OK\n";