fix: calculate withdrawal compliance from dated amounts

This commit is contained in:
92451 2026-09-24 19:26:13 +08:00
parent e224cbebc3
commit efeb7ecccb
6 changed files with 283 additions and 24 deletions

View File

@ -33,7 +33,8 @@ class WithdrawalManagementReport
* filters:array{platform:string,withdrawalPath:string,storeEntity:string,keyword:string,balanceStatus:string},
* platforms:list<string>,paths:list<string>,storeEntities:list<string>,rows:Collection<int,StoreReceivingAccount>,
* balances:array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}>,
* reserveStandards:array<int,int|null>,summary:array{source:int,matched:int,ambiguous:int,unmatched:int,not_ready:int},
* reserveStandards:array<int,int|null>,withdrawalAmounts:array<int,float|null>,compliance:array<int,string|null>,
* summary:array{source:int,matched:int,ambiguous:int,unmatched:int,not_ready:int},
* balanceColumnLabel:string,balanceDate:string,sort:array{field:string,direction:string}
* }
*/
@ -81,9 +82,23 @@ class WithdrawalManagementReport
),
],
)->all();
$withdrawalAmounts = $rows->mapWithKeys(
fn (StoreReceivingAccount $row): array => [
(int) $row->id => $this->withdrawalAmountFor($row, $balanceDate),
],
)->all();
$compliance = $rows->mapWithKeys(
fn (StoreReceivingAccount $row): array => [
(int) $row->id => $this->complianceFor(
$balances[(int) $row->id] ?? ['status' => 'not_ready', 'balance' => null],
$reserveStandards[(int) $row->id] ?? null,
$withdrawalAmounts[(int) $row->id] ?? null,
),
],
)->all();
$rows = $sort['field'] === ''
? $this->sortByBalanceStatus($rows, $balances)
: $this->sortRows($rows, $sort, $balances, $reserveStandards);
: $this->sortRows($rows, $sort, $balances, $reserveStandards, $withdrawalAmounts, $compliance);
return [
'filters' => $filters,
@ -93,6 +108,8 @@ class WithdrawalManagementReport
'rows' => $rows,
'balances' => $balances,
'reserveStandards' => $reserveStandards,
'withdrawalAmounts' => $withdrawalAmounts,
'compliance' => $compliance,
'summary' => [
'source' => $this->shopBalances->countForDate($balanceDate),
'matched' => (int) $balanceCounts->get('matched', 0),
@ -178,13 +195,15 @@ class WithdrawalManagementReport
* @param array{field:string,direction:string} $sort
* @param array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}> $balances
* @param array<int,int|null> $reserveStandards
* @param array<int,float|null> $withdrawalAmounts
* @param array<int,string|null> $compliance
* @return Collection<int,StoreReceivingAccount>
*/
private function sortRows(Collection $rows, array $sort, array $balances, array $reserveStandards): Collection
private function sortRows(Collection $rows, array $sort, array $balances, array $reserveStandards, array $withdrawalAmounts, array $compliance): Collection
{
return $rows->sort(function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($sort, $balances, $reserveStandards): int {
$leftValue = $this->sortValue($left, $sort['field'], $balances, $reserveStandards);
$rightValue = $this->sortValue($right, $sort['field'], $balances, $reserveStandards);
return $rows->sort(function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($sort, $balances, $reserveStandards, $withdrawalAmounts, $compliance): int {
$leftValue = $this->sortValue($left, $sort['field'], $balances, $reserveStandards, $withdrawalAmounts, $compliance);
$rightValue = $this->sortValue($right, $sort['field'], $balances, $reserveStandards, $withdrawalAmounts, $compliance);
$comparison = $this->compareSortValues($leftValue, $rightValue, $sort['direction']);
return $comparison !== 0
@ -196,8 +215,10 @@ class WithdrawalManagementReport
/**
* @param array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}> $balances
* @param array<int,int|null> $reserveStandards
* @param array<int,float|null> $withdrawalAmounts
* @param array<int,string|null> $compliance
*/
private function sortValue(StoreReceivingAccount $row, string $field, array $balances, array $reserveStandards): mixed
private function sortValue(StoreReceivingAccount $row, string $field, array $balances, array $reserveStandards, array $withdrawalAmounts, array $compliance): mixed
{
return match ($field) {
'platform' => $row->store_platform,
@ -211,10 +232,34 @@ class WithdrawalManagementReport
? $balances[(int) $row->id]['balance']
: null,
'reserve_standard' => $reserveStandards[(int) $row->id] ?? null,
'withdrawal_amount' => $withdrawalAmounts[(int) $row->id] ?? null,
'compliance' => $compliance[(int) $row->id] ?? null,
default => null,
};
}
private function withdrawalAmountFor(StoreReceivingAccount $row, string $balanceDate): ?float
{
if ($row->withdrawal_balance_date !== $balanceDate || $row->withdrawal_amount === null) {
return null;
}
return is_numeric($row->withdrawal_amount) ? (float) $row->withdrawal_amount : null;
}
/** @param array{status:string,balance:?float} $balance */
private function complianceFor(array $balance, ?int $reserve, ?float $withdrawalAmount): ?string
{
if (($balance['status'] ?? '') !== 'matched' || $balance['balance'] === null || $reserve === null || $withdrawalAmount === null) {
return null;
}
$remainingCents = (int) round((float) $balance['balance'] * 100)
- (int) round($withdrawalAmount * 100);
return $remainingCents <= $reserve * 100 ? 'yes' : 'no';
}
private function compareSortValues(mixed $left, mixed $right, string $direction): int
{
$leftEmpty = $left === null || $left === '';

View File

@ -34,7 +34,8 @@ class WithdrawalManagementSpreadsheet
* @param array{
* rows:\Illuminate\Support\Collection<int,StoreReceivingAccount>,
* balances:array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}>,
* reserveStandards:array<int,int|null>,balanceColumnLabel:string,balanceDate:string
* reserveStandards:array<int,int|null>,withdrawalAmounts:array<int,float|null>,compliance:array<int,string|null>,
* balanceColumnLabel:string,balanceDate:string
* } $report
*/
public function export(array $report): StreamedResponse
@ -52,17 +53,17 @@ class WithdrawalManagementSpreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('提现管理');
$this->writeHeader($sheet, self::CONFIG_HEADERS);
$this->sizeColumns($sheet, [14, 30, 20, 32, 16, 24, 22]);
$this->writeHeader($sheet, [...self::CONFIG_HEADERS, '提现金额']);
$this->sizeColumns($sheet, [14, 30, 20, 32, 16, 24, 22, 18]);
$instructions = $spreadsheet->createSheet();
$instructions->setTitle('填写说明');
$instructions->fromArray([
['提现管理导入说明'],
['1. 店铺平台、ERP店铺名称用于定位店铺;有平台店铺ID时优先按平台和ID匹配。'],
['2. 店铺主体、提现路径、提现账户名称、银行卡号后四位为可更新字段。'],
['2. 店铺主体、提现路径、提现账户名称、银行卡号后四位和提现金额为可更新字段。'],
['3. 可更新字段留空表示保持原值,填写“' . self::CLEAR_VALUE . '”表示清空原值。'],
['4. 银行卡号只填写后四位数字。上传后会先展示预览,确认后才写入。'],
['4. 提现金额须为非负数,最多两位小数,只用于导入当天对应的余额数据日期;上传后先预览再确认。'],
], null, 'A1');
$instructions->getStyle('A1')->getFont()->setBold(true)->setSize(14);
$instructions->getColumnDimension('A')->setWidth(88);
@ -96,6 +97,7 @@ class WithdrawalManagementSpreadsheet
}
[$headerIndex, $headers] = $this->headers($matrix);
$indexes = array_flip($headers);
$balanceDate = now()->subDay()->toDateString();
$accounts = StoreReceivingAccount::withoutGlobalScopes()
->where('company_id', $companyId)
->whereNull('deleted_at')
@ -113,6 +115,9 @@ class WithdrawalManagementSpreadsheet
foreach (self::CONFIG_HEADERS as $header) {
$values[$header] = trim((string) ($row[$indexes[$header]] ?? ''));
}
$values['提现金额'] = isset($indexes['提现金额'])
? trim((string) ($row[$indexes['提现金额']] ?? ''))
: '';
if (count(array_filter($values, static fn (string $value): bool => $value !== '')) === 0) {
continue;
}
@ -153,7 +158,7 @@ class WithdrawalManagementSpreadsheet
$seen[(int) $account->id] = $line;
try {
$changes = $this->changes($account, $values);
$changes = $this->changes($account, $values, $balanceDate);
} catch (\InvalidArgumentException $exception) {
$errors[] = "第 {$line} 行" . $exception->getMessage();
continue;
@ -169,11 +174,13 @@ class WithdrawalManagementSpreadsheet
'receiving_method' => '提现路径',
'receiving_account_name' => '提现账户名称',
'bank_card_last_four' => '银行卡号后四位',
'withdrawal_amount' => '提现金额',
'withdrawal_balance_date' => '提现金额',
];
$changedLabels = array_map(
$changedLabels = array_unique(array_map(
static fn (string $field): string => $labels[$field],
array_keys($changes),
);
));
$plan[] = [
'id' => (int) $account->id,
'updated_at' => (string) $account->getRawOriginal('updated_at'),
@ -230,6 +237,14 @@ class WithdrawalManagementSpreadsheet
]);
}
if (isset($item['changes']['withdrawal_balance_date'])
&& $item['changes']['withdrawal_balance_date'] !== null
&& $item['changes']['withdrawal_balance_date'] !== now()->subDay()->toDateString()) {
throw ValidationException::withMessages([
'import' => '余额日期已滚动,请重新上传并预览提现金额。',
]);
}
foreach ($item['changes'] as $field => $value) {
if ($field === 'bank_card_last_four') {
$account->bank_card_last_four = $value;
@ -258,6 +273,8 @@ class WithdrawalManagementSpreadsheet
foreach ($report['rows'] as $row) {
$match = $report['balances'][(int) $row->id] ?? ['status' => 'not_ready', 'balance' => null];
$reserve = $report['reserveStandards'][(int) $row->id] ?? null;
$withdrawalAmount = $report['withdrawalAmounts'][(int) $row->id] ?? null;
$compliance = $report['compliance'][(int) $row->id] ?? null;
$textValues = [
1 => (string) ($row->store_platform ?? ''),
2 => (string) ($row->erp_store_name ?? ''),
@ -281,14 +298,22 @@ class WithdrawalManagementSpreadsheet
} else {
$sheet->setCellValue([9, $rowNumber], $reserve);
}
$sheet->setCellValueExplicit([10, $rowNumber], '待接入', DataType::TYPE_STRING);
$sheet->setCellValueExplicit([11, $rowNumber], '待核验', DataType::TYPE_STRING);
if ($withdrawalAmount === null) {
$sheet->setCellValueExplicit([10, $rowNumber], '待接入', DataType::TYPE_STRING);
} else {
$sheet->setCellValue([10, $rowNumber], $withdrawalAmount);
}
$sheet->setCellValueExplicit([11, $rowNumber], match ($compliance) {
'yes' => '是',
'no' => '否',
default => '待核验',
}, DataType::TYPE_STRING);
$rowNumber++;
}
if ($rowNumber > 2) {
$sheet->getStyle('H2:I' . ($rowNumber - 1))->getNumberFormat()->setFormatCode('#,##0.00');
$sheet->getStyle('H2:I' . ($rowNumber - 1))->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
$sheet->getStyle('H2:J' . ($rowNumber - 1))->getNumberFormat()->setFormatCode('#,##0.00');
$sheet->getStyle('H2:J' . ($rowNumber - 1))->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
}
$sheet->getStyle('C2:C' . max(2, $rowNumber - 1))->getNumberFormat()->setFormatCode('@');
$sheet->getStyle('G2:G' . max(2, $rowNumber - 1))->getNumberFormat()->setFormatCode('@');
@ -310,6 +335,9 @@ class WithdrawalManagementSpreadsheet
]);
}
}
if (count(array_keys($headers, '提现金额', true)) > 1) {
throw ValidationException::withMessages(['import' => '导入表头只能包含一个“提现金额”列。']);
}
return [(int) $index, $headers];
}
@ -342,7 +370,7 @@ class WithdrawalManagementSpreadsheet
}
/** @param array<string,string> $values @return array<string,string|null> */
private function changes(StoreReceivingAccount $account, array $values): array
private function changes(StoreReceivingAccount $account, array $values, string $balanceDate): array
{
$fields = [
'店铺主体' => ['field' => 'store_entity_name', 'current' => $account->store_entity_name, 'max' => 191, 'clear' => ''],
@ -373,6 +401,25 @@ class WithdrawalManagementSpreadsheet
}
}
$amount = $values['提现金额'] ?? '';
if ($amount !== '' && $amount !== '待接入') {
if ($amount === self::CLEAR_VALUE) {
if ($account->withdrawal_amount !== null || $account->withdrawal_balance_date !== null) {
$changes['withdrawal_amount'] = null;
$changes['withdrawal_balance_date'] = null;
}
} else {
if (preg_match('/^\d{1,10}(?:\.\d{1,2})?$/', $amount) !== 1) {
throw new \InvalidArgumentException('的提现金额须为非负数,最多两位小数');
}
$normalized = number_format((float) $amount, 2, '.', '');
if ($account->withdrawal_amount !== $normalized || $account->withdrawal_balance_date !== $balanceDate) {
$changes['withdrawal_amount'] = $normalized;
$changes['withdrawal_balance_date'] = $balanceDate;
}
}
}
return $changes;
}

View File

@ -891,6 +891,8 @@ class Pages extends Controller
'withdrawalStoreEntities' => $report['storeEntities'],
'withdrawalBalances' => $report['balances'],
'withdrawalReserveStandards' => $report['reserveStandards'],
'withdrawalAmounts' => $report['withdrawalAmounts'],
'withdrawalCompliance' => $report['compliance'],
'withdrawalBalanceSummary' => $report['summary'],
'withdrawalBalanceColumnLabel' => $report['balanceColumnLabel'],
'withdrawalBalanceDate' => $report['balanceDate'],

View File

@ -23,6 +23,8 @@ class StoreReceivingAccount extends Model
'bank_card_last_four' => 'b',
'store_id' => 'i',
'source_platform' => 'r',
'withdrawal_amount' => 'm',
'withdrawal_balance_date' => 'd',
];
protected $table = 'business_store_receiving_accounts';
@ -35,6 +37,7 @@ class StoreReceivingAccount extends Model
'payment_password', 'payment_verification_method', 'financial_account', 'financial_password',
'payment_verification_method_secondary', 'withdrawal_path', 'withdrawal_account_name',
'bank_card_last_four', 'store_id', 'source_platform',
'withdrawal_amount', 'withdrawal_balance_date',
];
protected $casts = [
@ -213,6 +216,26 @@ class StoreReceivingAccount extends Model
$this->setMeta('source_platform', $this->clean($value));
}
public function getWithdrawalAmountAttribute(): ?string
{
return $this->meta('withdrawal_amount');
}
public function setWithdrawalAmountAttribute($value): void
{
$this->setMeta('withdrawal_amount', $this->clean($value));
}
public function getWithdrawalBalanceDateAttribute(): ?string
{
return $this->meta('withdrawal_balance_date');
}
public function setWithdrawalBalanceDateAttribute($value): void
{
$this->setMeta('withdrawal_balance_date', $this->clean($value));
}
private function clean($value): ?string
{
$value = trim((string) $value);

View File

@ -168,6 +168,8 @@
? '聚水潭店铺:' . $balanceMatch['source_store_name'] . ';余额日期:' . $withdrawalBalanceDate
: '余额日期:' . $withdrawalBalanceDate;
$reserveStandard = $withdrawalReserveStandards[(int) $row->id] ?? null;
$withdrawalAmount = $withdrawalAmounts[(int) $row->id] ?? null;
$compliance = $withdrawalCompliance[(int) $row->id] ?? null;
@endphp
<tr>
<td data-withdrawal-column="platform">{{ $row->store_platform ?: '未配置' }}</td>
@ -179,8 +181,16 @@
<td data-withdrawal-column="bank_card">{{ $bankCard }}</td>
<td class="num" data-withdrawal-column="balance" title="{{ $balanceTitle }}">{{ $balanceText }}</td>
<td class="num" data-withdrawal-column="reserve_standard">{{ $reserveStandard === null ? '待配置' : number_format($reserveStandard, 2) }}</td>
<td class="num" data-withdrawal-column="withdrawal_amount">待接入</td>
<td data-withdrawal-column="compliance"><span class="biz-badge biz-badge-no">待核验</span></td>
<td class="num" data-withdrawal-column="withdrawal_amount">{{ $withdrawalAmount === null ? '待接入' : number_format($withdrawalAmount, 2) }}</td>
<td data-withdrawal-column="compliance">
@if ($compliance === 'yes')
<span class="biz-badge biz-badge-yes">是</span>
@elseif ($compliance === 'no')
<span class="biz-badge biz-badge-no">否</span>
@else
<span class="biz-badge biz-badge-no">待核验</span>
@endif
</td>
</tr>
@empty
<tr>

View File

@ -4,9 +4,13 @@ namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Models\Business\StoreReceivingAccount;
use App\Business\WithdrawalManagementReport;
use Illuminate\Http\UploadedFile;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Mockery;
use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@ -25,6 +29,8 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
'银行卡号(后四位)',
];
private const IMPORT_HEADERS = [...self::HEADERS, '提现金额'];
/** @var list<string> */
private array $temporaryFiles = [];
@ -163,7 +169,7 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
$spreadsheet = IOFactory::load($path);
try {
$this->assertSame(self::HEADERS, $spreadsheet->getSheet(0)->rangeToArray('A1:G1')[0]);
$this->assertSame(self::IMPORT_HEADERS, $spreadsheet->getSheet(0)->rangeToArray('A1:H1')[0]);
$this->assertSame('填写说明', $spreadsheet->getSheet(1)->getTitle());
$this->assertStringContainsString('[清空]', (string) $spreadsheet->getSheet(1)->getCell('A4')->getValue());
} finally {
@ -319,6 +325,132 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
$this->assertSame('他人已修改主体', $store->fresh()->store_entity_name);
}
public function test_compliance_uses_balance_minus_current_date_withdrawal_and_matches_export(): void
{
Carbon::setTestNow('2026-09-24 09:00:00');
try {
$stores = [];
foreach ([
['达标测试店', '5000.00', '2026-09-23'],
['未达标测试店', '4999.99', '2026-09-23'],
['过期金额测试店', '5000.00', '2026-09-22'],
['缺少金额测试店', null, null],
['缺少余额测试店', '5000.00', '2026-09-23'],
] as [$name, $amount, $date]) {
$stores[$name] = StoreReceivingAccount::query()->create([
'company_id' => $this->company->id,
'store_platform' => '京东',
'erp_store_name' => $name,
'store_entity_name' => '测试主体',
'withdrawal_amount' => $amount,
'withdrawal_balance_date' => $date,
]);
}
$stores['缺少标准测试店'] = StoreReceivingAccount::query()->create([
'company_id' => $this->company->id,
'store_platform' => '唯品会',
'erp_store_name' => '缺少标准测试店',
'store_entity_name' => '测试主体',
'withdrawal_amount' => '5000.00',
'withdrawal_balance_date' => '2026-09-23',
]);
$balances = [];
foreach ($stores as $store) {
$balances[(int) $store->id] = [
'status' => $store->erp_store_name === '缺少余额测试店' ? 'unmatched' : 'matched',
'balance' => $store->erp_store_name === '缺少余额测试店' ? null : 10000.00,
'source_store_name' => $store->erp_store_name,
'source_platform' => '京东',
];
}
$mirror = Mockery::mock(JushuitanShopBalanceRepository::class);
$mirror->shouldReceive('matchesForStores')->andReturn($balances);
$mirror->shouldReceive('countForDate')->andReturn(6);
$this->app->instance(JushuitanShopBalanceRepository::class, $mirror);
$report = $this->app->make(WithdrawalManagementReport::class)->results(request());
$this->assertSame('yes', $report['compliance'][(int) $stores['达标测试店']->id]);
$this->assertSame('no', $report['compliance'][(int) $stores['未达标测试店']->id]);
$this->assertNull($report['compliance'][(int) $stores['过期金额测试店']->id]);
$this->assertNull($report['compliance'][(int) $stores['缺少金额测试店']->id]);
$this->assertNull($report['compliance'][(int) $stores['缺少余额测试店']->id]);
$this->assertNull($report['compliance'][(int) $stores['缺少标准测试店']->id]);
$page = $this->loginAs()->get(route('business.pages.show', [
'section' => 'fund-management',
'page' => 'withdrawal-management',
'platform' => '京东',
]));
$page->assertOk()
->assertSeeText('待核验');
$this->assertMatchesRegularExpression('/data-withdrawal-column="compliance">\s*<span class="biz-badge biz-badge-yes">是<\/span>/', $page->getContent());
$this->assertMatchesRegularExpression('/data-withdrawal-column="compliance">\s*<span class="biz-badge biz-badge-no">否<\/span>/', $page->getContent());
$response = $this->get(route('business.withdrawal-management.export', ['platform' => '京东']));
$path = $this->temporaryPath('withdrawal-compliance-export-');
file_put_contents($path, $response->streamedContent());
$spreadsheet = IOFactory::load($path);
try {
$sheet = $spreadsheet->getActiveSheet();
$values = [];
for ($row = 2; $row <= $sheet->getHighestDataRow(); $row++) {
$values[(string) $sheet->getCell("B{$row}")->getValue()] = [
$sheet->getCell("J{$row}")->getValue(),
$sheet->getCell("K{$row}")->getValue(),
];
}
$this->assertSame([5000.0, '是'], $values['达标测试店']);
$this->assertSame([4999.99, '否'], $values['未达标测试店']);
$this->assertSame(['待接入', '待核验'], $values['过期金额测试店']);
$this->assertSame(['待接入', '待核验'], $values['缺少金额测试店']);
$this->assertSame([5000.0, '待核验'], $values['缺少余额测试店']);
} finally {
$spreadsheet->disconnectWorksheets();
}
} finally {
Carbon::setTestNow();
}
}
public function test_withdrawal_amount_import_is_validated_and_bound_to_current_balance_date(): void
{
Carbon::setTestNow('2026-09-24 09:00:00');
try {
$store = StoreReceivingAccount::query()->create([
'company_id' => $this->company->id,
'store_platform' => '京东',
'erp_store_name' => '金额导入测试店',
'store_entity_name' => '测试主体',
]);
$this->loginAs();
$this->withExceptionHandling()
->post(route('business.withdrawal-management.import.preview'), [
'import' => $this->workbook([['京东', '金额导入测试店', '', '', '', '', '', '-1']]),
])
->assertSessionHasErrors('import');
$this->assertNull($store->fresh()->withdrawal_amount);
$previewResponse = $this->post(route('business.withdrawal-management.import.preview'), [
'import' => $this->workbook([['京东', '金额导入测试店', '', '', '', '', '', '5000.00']]),
]);
$previewResponse->assertSessionHas('withdrawalImportPreview', function (array $preview): bool {
return $preview['preview'][0]['changes'] === '提现金额';
});
$preview = $previewResponse->getSession()->get('withdrawalImportPreview');
$this->post(route('business.withdrawal-management.import.commit'), [
'preview_token' => $preview['token'],
])->assertSessionHasNoErrors();
$this->assertSame('5000.00', $store->fresh()->withdrawal_amount);
$this->assertSame('2026-09-23', $store->fresh()->withdrawal_balance_date);
} finally {
Carbon::setTestNow();
}
}
/** @param list<list<mixed>> $rows */
private function workbook(array $rows): UploadedFile
{
@ -326,7 +458,7 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('提现管理');
$sheet->fromArray(self::HEADERS, null, 'A1');
$sheet->fromArray(self::IMPORT_HEADERS, null, 'A1');
if ($rows !== []) {
$sheet->fromArray($rows, null, 'A2');
}