快递应付 → 快递报价 下新增「快递入驻」页:生成入驻二维码发给快递方,
对方免登录在手机上填写主体、联系人、结算账户、报价并上传资质,
内部对接人初审、管理员财务复审后生成首版报价,协议签署完成后开通。
- 新增三张表 business_courier_onboardings / _files / _events(纯新增,不改旧表)
- 免登录填报页 /onboarding/courier/{token}:40 位随机 token,可暂存、提交、退回后补件,
链接有效期可延长、可作废;银行账号加密存储;附件存 uploads 盘
- 内置纯 PHP 二维码生成(App\Support\QrCode,SVG 输出,无新增依赖)
- 财务复审:确认系统快递名称(唯一),快递所填报价写成报价管理的 CourierQuoteVersion,
可直接在协议签署页发起报价确认单;初审人不能复审
- 开通:需匹配到已签署的协议签署任务,或填写线下签署说明
- 快递资料页新增「入驻快递」页签,列出已开通的入驻快递
- 新增 CourierOnboardingWorkflowTest(8 个用例),更新快递报价目录顺序断言
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gzfsnV3fJ89CoUy1D9V9g
99 lines
4.0 KiB
PHP
99 lines
4.0 KiB
PHP
<?php
|
|
|
|
namespace App\Business;
|
|
|
|
use App\Models\Business\CourierOnboarding;
|
|
use App\Support\CourierOnboardingService;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
|
/**
|
|
* 「快递入驻」列表页数据:状态统计、筛选和分页。
|
|
*/
|
|
class CourierOnboardingPageData
|
|
{
|
|
/** 列表页状态分组,键为页面筛选值。 */
|
|
public const STATUS_GROUPS = [
|
|
'pending' => ['label' => '待处理', 'statuses' => [CourierOnboarding::STATUS_SUBMITTED, CourierOnboarding::STATUS_FIRST_APPROVED, CourierOnboarding::STATUS_APPROVED]],
|
|
'filling' => ['label' => '待快递填写', 'statuses' => [CourierOnboarding::STATUS_INVITED, CourierOnboarding::STATUS_RETURNED]],
|
|
'activated' => ['label' => '已开通', 'statuses' => [CourierOnboarding::STATUS_ACTIVATED]],
|
|
'closed' => ['label' => '已关闭', 'statuses' => [CourierOnboarding::STATUS_REJECTED, CourierOnboarding::STATUS_CANCELLED]],
|
|
'all' => ['label' => '全部', 'statuses' => []],
|
|
];
|
|
|
|
public function __construct(private readonly CourierOnboardingService $service)
|
|
{
|
|
}
|
|
|
|
public function page(): array
|
|
{
|
|
$group = (string) request()->query('group', 'pending');
|
|
$group = array_key_exists($group, self::STATUS_GROUPS) ? $group : 'pending';
|
|
$search = trim((string) request()->query('search', ''));
|
|
$limit = min(max((int) setting('default.list_limit', 25), 1), 100);
|
|
|
|
$data = [
|
|
'view' => 'business.pages.courier-payables.courier-onboarding',
|
|
'onboardingReady' => $this->service->ready(),
|
|
'onboardingGroup' => $group,
|
|
'onboardingSearch' => $search,
|
|
'onboardingGroups' => self::STATUS_GROUPS,
|
|
'onboardingCounts' => [],
|
|
'onboardingRows' => new LengthAwarePaginator([], 0, $limit),
|
|
];
|
|
|
|
if (! $data['onboardingReady']) {
|
|
return $data;
|
|
}
|
|
|
|
$counts = $this->service->statusCounts();
|
|
$data['onboardingCounts'] = collect(self::STATUS_GROUPS)
|
|
->map(static fn (array $config): int => $config['statuses'] === []
|
|
? array_sum($counts)
|
|
: array_sum(array_intersect_key($counts, array_flip($config['statuses']))))
|
|
->all();
|
|
|
|
$query = CourierOnboarding::query()->withCount('files');
|
|
$statuses = self::STATUS_GROUPS[$group]['statuses'];
|
|
if ($statuses !== []) {
|
|
$query->whereIn('status', $statuses);
|
|
}
|
|
if ($search !== '') {
|
|
$like = '%' . addcslashes($search, '%_\\') . '%';
|
|
$query->where(static function ($builder) use ($like): void {
|
|
foreach (['number', 'invite_label', 'company_full_name', 'network_name', 'courier_name', 'contact_name', 'contact_phone'] as $column) {
|
|
$builder->orWhere($column, 'like', $like);
|
|
}
|
|
});
|
|
}
|
|
|
|
$data['onboardingRows'] = $query->latest('updated_at')->latest('id')
|
|
->paginate($limit)
|
|
->withQueryString();
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* 快递资料页「入驻快递」页签:已开通的入驻快递,作为新快递的主数据。
|
|
*/
|
|
public function activated(int $page, int $limit): LengthAwarePaginator
|
|
{
|
|
if (! $this->service->ready()) {
|
|
return new LengthAwarePaginator([], 0, $limit, $page);
|
|
}
|
|
|
|
$search = trim((string) request()->query('search', ''));
|
|
$query = CourierOnboarding::query()->where('status', CourierOnboarding::STATUS_ACTIVATED);
|
|
if ($search !== '') {
|
|
$like = '%' . addcslashes($search, '%_\\') . '%';
|
|
$query->where(static function ($builder) use ($like): void {
|
|
foreach (['courier_name', 'company_full_name', 'network_name', 'contact_name'] as $column) {
|
|
$builder->orWhere($column, 'like', $like);
|
|
}
|
|
});
|
|
}
|
|
|
|
return $query->latest('activated_at')->paginate($limit, ['*'], 'page', $page)->withQueryString();
|
|
}
|
|
}
|