feat(platform-orders): queue batch activate subscriptions job

This commit is contained in:
萝卜
2026-03-17 12:40:21 +08:00
parent 5158703a3e
commit e9ec968379
4 changed files with 206 additions and 91 deletions

View File

@@ -1592,87 +1592,21 @@ class PlatformOrderController extends Controller
}
$filterSummary = implode('&', $filterSummaryParts);
foreach ($orders as $orderRow) {
try {
$order = PlatformOrder::query()->find($orderRow->id);
if (! $order) {
continue;
}
// 队列化M3 可运维化第一步):先把匹配到的订单 ID 列表投递为一个 Job避免请求超时。
// 注意:当前阶段仍由 Job 内逐条写 meta 与错误原因;后续可再升级为分片 Job + 结果聚合。
$orderIds = $orders->pluck('id')->map(fn ($id) => (int) $id)->values()->all();
// 治理优先:续费单必须绑定订阅(兼容历史脏数据/手工改库等场景)
if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
throw new \InvalidArgumentException('续费单未绑定订阅site_subscription_id 为空),不允许批量同步订阅。');
}
\App\Jobs\BatchActivateSubscriptionsJob::dispatch(
$orderIds,
(int) $admin->id,
$scope,
(string) $filterSummary,
(int) $limit,
(int) $matchedTotal,
(int) $processed,
);
$subscription = $service->activateOrder($orderRow->id, $admin->id);
// 注意activateOrder 过程中会写入 order 的 meta/site_subscription_id 等;此处必须 refresh避免后续写审计时覆盖掉同步结果
$order->refresh();
// 轻量审计:记录批量同步动作(方便追溯)
if ($order) {
$meta = (array) ($order->meta ?? []);
$audit = (array) (data_get($meta, 'audit', []) ?? []);
$nowStr = now()->toDateTimeString();
$audit[] = [
'action' => 'batch_activate_subscription',
'scope' => $scope,
'at' => $nowStr,
'admin_id' => $admin->id,
'subscription_id' => $subscription->id,
'filters' => $filterSummary,
'note' => '批量同步订阅limit=' . $limit . ', matched=' . $matchedTotal . ', processed=' . $processed . ')',
];
data_set($meta, 'audit', $audit);
// 便于筛选/统计:记录最近一次批量同步信息(扁平字段)
data_set($meta, 'batch_activation', [
'at' => $nowStr,
'admin_id' => $admin->id,
'scope' => $scope,
]);
$order->meta = $meta;
$order->save();
}
$success++;
} catch (\Throwable $e) {
$failed++;
$reason = trim((string) $e->getMessage());
$reason = $reason !== '' ? $reason : '未知错误';
$failedReasonCounts[$reason] = ($failedReasonCounts[$reason] ?? 0) + 1;
// 批量同步失败也需要可治理:写入失败原因到订单 meta便于后续筛选/导出/清理
$order = PlatformOrder::query()->find($orderRow->id);
if ($order) {
$meta = (array) ($order->meta ?? []);
data_set($meta, 'subscription_activation_error', [
'message' => $reason,
'at' => now()->toDateTimeString(),
'admin_id' => $admin->id,
]);
$order->meta = $meta;
$order->save();
}
}
}
$msg = '批量同步订阅完成:成功 ' . $success . ' 条,失败 ' . $failed . ' 条(命中 ' . $matchedTotal . ' 条,本次处理 ' . $processed . ' 条limit=' . $limit . '';
if ($failed > 0 && count($failedReasonCounts) > 0) {
arsort($failedReasonCounts);
$top = array_slice($failedReasonCounts, 0, 3, true);
$topText = collect($top)->map(function ($cnt, $reason) {
$reason = mb_substr((string) $reason, 0, 60);
return $reason . '' . $cnt . '';
})->implode('');
$msg .= '失败原因Top' . $topText;
}
return redirect()->back()->with('success', $msg);
return redirect()->back()->with('success', '批量同步订阅任务已提交到队列:命中 ' . $matchedTotal . ' 条,本次处理 ' . $processed . ' 条limit=' . $limit . ')。');
}
public function batchMarkPaidAndActivate(Request $request, SubscriptionActivationService $service): RedirectResponse