CaiWuXiTong/modules/EcommerceFinance/Services/FinanceLedgerShopMappingBulk.php
shanshi 20dc695549 feat: 完善财务台账账户维护与分页体验 (#412)
合并账户维护、筛选、列设置、无刷新分页、录入模板及批量映射工具。
2026-10-21 11:00:56 +08:00

118 lines
4.1 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use App\Models\Auth\User;
use Illuminate\Support\Facades\DB;
/**
* 按店铺台账批量建立“店铺提现”来源值到账户简称的映射。
*
* 只处理店铺名称完全一致、回款账户简称存在于账户总表的未映射记录,
* 其余来源值保留人工确认。
*/
class FinanceLedgerShopMappingBulk
{
public function preview(): array
{
$connection = $this->connection();
$shopAccounts = $connection->table('fin_shops')
->whereNotNull('account_short')
->where('account_short', '<>', '')
->where('account_short', '<>', '/')
->pluck('account_short', 'shop_name');
$masterAccounts = array_fill_keys(
$connection->table('fin_accounts')->distinct()->pluck('account_short')->all(),
true,
);
$rows = $connection->table('fin_account_map')
->where('source', 'shop')
->where(function ($query): void {
$query->whereNull('account_short')->orWhere('account_short', '');
})
->orderBy('source_value')
->get(['id', 'source_value']);
$candidates = [];
$unmatched = [];
foreach ($rows as $row) {
$sourceValue = trim((string) $row->source_value);
$accountShort = trim((string) ($shopAccounts[$sourceValue] ?? ''));
if ($accountShort === '' || ! isset($masterAccounts[$accountShort])) {
$unmatched[] = $sourceValue;
continue;
}
$candidates[] = [
'id' => (int) $row->id,
'source_value' => $sourceValue,
'account_short' => $accountShort,
];
}
return [
'total' => $rows->count(),
'matched' => count($candidates),
'unmatched' => count($unmatched),
'candidates' => $candidates,
'unmatched_values' => $unmatched,
];
}
public function apply(?User $user = null): array
{
$preview = $this->preview();
$connection = $this->connection();
$userName = (string) ($user?->name ?? '');
$userId = (int) ($user?->id ?? 0);
$now = now();
$mapped = 0;
if ($preview['candidates'] !== []) {
$connection->transaction(function () use ($connection, $preview, $userName, $userId, $now, &$mapped): void {
foreach ($preview['candidates'] as $candidate) {
$updated = $connection->table('fin_account_map')
->where('id', $candidate['id'])
->where(function ($query): void {
$query->whereNull('account_short')->orWhere('account_short', '');
})
->update([
'account_short' => $candidate['account_short'],
'updated_by' => $userName,
'updated_at' => $now,
]);
if ($updated !== 1) {
continue;
}
$connection->table('fin_account_map_events')->insert([
'source' => 'shop',
'source_value' => $candidate['source_value'],
'old_account_short' => null,
'new_account_short' => $candidate['account_short'],
'user_id' => $userId,
'user_name' => $userName,
'created_at' => $now,
]);
$mapped++;
}
});
}
if ($mapped > 0) {
app(FinanceLedgerCalculator::class)->recalculate();
}
return [
'total' => $preview['total'],
'mapped' => $mapped,
'unmatched' => $preview['unmatched'],
];
}
private function connection()
{
return DB::connection((string) config('upstream_mirrors.connection', 'business_mirror'));
}
}