diff --git a/app/Jobs/BatchActivateSubscriptionsJob.php b/app/Jobs/BatchActivateSubscriptionsJob.php index 0ed4f97..90dad8d 100644 --- a/app/Jobs/BatchActivateSubscriptionsJob.php +++ b/app/Jobs/BatchActivateSubscriptionsJob.php @@ -52,6 +52,9 @@ class BatchActivateSubscriptionsJob implements ShouldQueue public function handle(SubscriptionActivationService $service): void { + // 批次号:用于把一次队列批量执行关联起来,便于后续追溯/筛选/可观测。 + $runId = 'BAS' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT); + foreach ($this->orderIds as $orderId) { /** @var PlatformOrder|null $order */ $order = PlatformOrder::query()->find($orderId); @@ -81,7 +84,8 @@ class BatchActivateSubscriptionsJob implements ShouldQueue 'admin_id' => $this->adminId, 'subscription_id' => $subscription->id, 'filters' => $this->filterSummary, - 'note' => '批量同步订阅(queue, limit=' . $this->limit . ', matched=' . $this->matchedTotal . ', processed=' . $this->processed . ')', + 'run_id' => $runId, + 'note' => '批量同步订阅(queue, run_id=' . $runId . ', limit=' . $this->limit . ', matched=' . $this->matchedTotal . ', processed=' . $this->processed . ')', ]; data_set($meta, 'audit', $audit); @@ -91,6 +95,7 @@ class BatchActivateSubscriptionsJob implements ShouldQueue 'admin_id' => $this->adminId, 'scope' => $this->scope, 'mode' => 'queue', + 'run_id' => $runId, ]); $order->meta = $meta; diff --git a/tests/Feature/BatchActivateSubscriptionsJobShouldWriteRunIdTest.php b/tests/Feature/BatchActivateSubscriptionsJobShouldWriteRunIdTest.php new file mode 100644 index 0000000..c616416 --- /dev/null +++ b/tests/Feature/BatchActivateSubscriptionsJobShouldWriteRunIdTest.php @@ -0,0 +1,76 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_job_should_write_run_id_into_batch_activation_meta_and_audit(): void + { + $this->loginAsPlatformAdmin(); + + $merchant = Merchant::query()->firstOrFail(); + $plan = Plan::query()->create([ + 'code' => 'job_batch_activate_run_id_plan', + 'name' => 'Job 批量同步 run_id 测试套餐', + 'billing_cycle' => 'monthly', + 'price' => 10, + 'list_price' => 10, + 'status' => 'active', + 'sort' => 10, + 'published_at' => now(), + ]); + + $order = PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_JOB_BATCH_RUN_ID_0001', + 'order_type' => 'new_purchase', + 'status' => 'activated', + 'payment_status' => 'paid', + 'plan_name' => $plan->name, + 'billing_cycle' => $plan->billing_cycle, + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now()->subMinutes(10), + 'paid_at' => now()->subMinutes(9), + 'activated_at' => now()->subMinutes(8), + 'meta' => [], + ]); + + // 同步执行 job 的 handle(不依赖 queue worker),用于锁定写入口径 + $job = new BatchActivateSubscriptionsJob([ + $order->id, + ], 1, 'filtered', 'syncable_only=1', 50, 1, 1); + + $job->handle(app(\App\Support\SubscriptionActivationService::class)); + + $order->refresh(); + + $this->assertNotEmpty((string) data_get($order->meta, 'batch_activation.run_id')); + $this->assertSame((string) data_get($order->meta, 'batch_activation.run_id'), (string) data_get($order->meta, 'audit.0.run_id')); + + $runId = (string) data_get($order->meta, 'batch_activation.run_id'); + $this->assertStringStartsWith('BAS', $runId); + } +}