feat(admin): warning flash支持可选链接并提示重复批量投递可直达上次复盘

This commit is contained in:
萝卜
2026-03-17 18:24:15 +08:00
parent 397717e9bc
commit 7dd2e2e40a
3 changed files with 109 additions and 8 deletions

View File

@@ -1605,9 +1605,21 @@ class PlatformOrderController extends Controller
// 注意:当前阶段仍由 Job 内逐条写 meta 与错误原因;后续可再升级为分片 Job + 结果聚合。
$orderIds = $orders->pluck('id')->map(fn ($id) => (int) $id)->values()->all();
// run_id用于把一次批量执行关联起来便于后续追溯/筛选/可观测。
// 说明run_id 在投递阶段就生成,这样运营在页面成功提示里即可复制 run_id 并进入批次复盘页。
$runId = 'BAS' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);
// 幂等/防抖(最小实现):避免运营短时间内重复点击导致重复投递同一批次。
// 说明:这里做“短 TTL 的一次性锁”,不引入新表;后续可演进为批次表 + 幂等 key。
// key 口径scope + filters + ids + limit同一集合的重复点击会被拦截
$lockKey = \App\Support\BatchDispatchLock::makeKey(
'admin:bas:dispatch',
$scope,
(string) $filterSummary,
$orderIds,
(int) $limit,
);
if (! \App\Support\BatchDispatchLock::acquire(
'admin:bas:dispatch',
$scope,
@@ -1615,14 +1627,22 @@ class PlatformOrderController extends Controller
$orderIds,
(int) $limit,
60,
'1',
(string) $runId,
)) {
return redirect()->back()->with('warning', '检测到刚刚已提交过同一批次的 BAS 任务1 分钟内)。为避免重复投递,本次未再次提交。');
}
$existing = (string) (\App\Support\BatchDispatchLock::getExistingValue($lockKey) ?? '');
$existing = trim($existing);
// run_id用于把一次批量执行关联起来便于后续追溯/筛选/可观测。
// 说明run_id 在投递阶段就生成,这样运营在页面成功提示里即可复制 run_id 并进入批次复盘页。
$runId = 'BAS' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);
$res = redirect()->back()->with('warning', '检测到刚刚已提交过同一批次的 BAS 任务1 分钟内)。为避免重复投递,本次未再次提交。');
// 若锁内已有 run_id作为 value 存储),则给运营一个直达复盘入口。
if ($existing !== '' && str_starts_with($existing, 'BAS')) {
$res = $res
->with('warning_link_href', '/admin/platform-batches/show?type=bas&run_id=' . urlencode($existing))
->with('warning_link_label', '进入上次批次复盘');
}
return $res;
}
\App\Jobs\BatchActivateSubscriptionsJob::dispatch(
$orderIds,
@@ -1744,6 +1764,14 @@ class PlatformOrderController extends Controller
// 幂等/防抖(最小实现):避免运营短时间内重复点击导致重复投递同一批次。
// 说明:这里做“短 TTL 的一次性锁”,不引入新表;后续可演进为批次表 + 幂等 key。
// key 口径scope + filters + ids + limit同一集合的重复点击会被拦截
$lockKey = \App\Support\BatchDispatchLock::makeKey(
'admin:bmpa:dispatch',
$scope,
(string) $filterSummary,
$orderIds,
(int) $limit,
);
if (! \App\Support\BatchDispatchLock::acquire(
'admin:bmpa:dispatch',
$scope,
@@ -1753,7 +1781,18 @@ class PlatformOrderController extends Controller
60,
(string) $runId,
)) {
return redirect()->back()->with('warning', '检测到刚刚已提交过同一批次的 BMPA 任务1 分钟内)。为避免重复投递,本次未再次提交。');
$existing = (string) (\App\Support\BatchDispatchLock::getExistingValue($lockKey) ?? '');
$existing = trim($existing);
$res = redirect()->back()->with('warning', '检测到刚刚已提交过同一批次的 BMPA 任务1 分钟内)。为避免重复投递,本次未再次提交。');
if ($existing !== '' && str_starts_with($existing, 'BMPA')) {
$res = $res
->with('warning_link_href', '/admin/platform-batches/show?type=bmpa&run_id=' . urlencode($existing))
->with('warning_link_label', '进入上次批次复盘');
}
return $res;
}
\App\Jobs\BatchMarkPaidAndActivateJob::dispatch(

View File

@@ -91,7 +91,18 @@
</div>
@endif
@if(session('warning'))
<div class="warning" data-flash="warning">{{ session('warning') }}</div>
<div class="warning" data-flash="warning">
<span>{{ session('warning') }}</span>
@if(session('warning_link_href'))
@php
$flashWarningLinkHref = \App\Support\BackUrl::sanitizeForLinks((string) session('warning_link_href'));
$flashWarningLinkLabel = (string) (session('warning_link_label') ?: '查看');
@endphp
@if($flashWarningLinkHref !== '')
<a href="{{ $flashWarningLinkHref }}" class="btn btn-secondary btn-sm" style="margin-left:8px;">{{ $flashWarningLinkLabel }}</a>
@endif
@endif
</div>
@endif
@if(session('error'))
<div class="error-box" data-flash="error">{{ session('error') }}</div>

View File

@@ -0,0 +1,51 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AdminFlashWarningShouldSupportOptionalLinkTest extends TestCase
{
use RefreshDatabase;
protected function loginAsPlatformAdmin(): void
{
$this->seed();
$this->post('/admin/login', [
'email' => 'platform.admin@demo.local',
'password' => 'Platform@123456',
])->assertRedirect('/admin');
}
public function test_flash_warning_should_render_optional_link_when_session_keys_present(): void
{
$this->loginAsPlatformAdmin();
$res = $this->withSession([
'warning' => '检测到重复提交',
'warning_link_href' => '/admin/platform-batches/show?type=bmpa&run_id=BMPA202603171234560001',
'warning_link_label' => '进入上次批次复盘',
])->get('/admin');
$res->assertOk();
$res->assertSee('检测到重复提交');
$res->assertSee('进入上次批次复盘');
$res->assertSee('/admin/platform-batches/show?type=bmpa&amp;run_id=BMPA202603171234560001', false);
}
public function test_flash_warning_link_should_be_sanitized_to_relative_path(): void
{
$this->loginAsPlatformAdmin();
$res = $this->withSession([
'warning' => 'warn',
'warning_link_href' => 'https://evil.example.com/x',
'warning_link_label' => '查看',
])->get('/admin');
$res->assertOk();
$res->assertDontSee('https://evil.example.com/x');
}
}