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