完成店铺台账每日余额来源、日期筛选、预留标准和连续3天预警。 Co-authored-by: 92451 <924518135@qq.com> Co-committed-by: 92451 <924518135@qq.com>
603 lines
24 KiB
PHP
603 lines
24 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Services;
|
|
|
|
use App\Business\WithdrawalBalanceAlertSynchronizer;
|
|
use App\Models\Business\StoreReceivingAccount;
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\DatabaseManager;
|
|
use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository;
|
|
use Modules\EcommerceFinance\View\BillingDetailsPageData;
|
|
|
|
class FinanceLedgerShopDataSynchronizer
|
|
{
|
|
public const WITHDRAWAL_SOURCE = FinanceLedgerTransactionWriter::SHOP_WITHDRAWAL_SOURCE;
|
|
|
|
private readonly ConnectionInterface $connection;
|
|
|
|
public function __construct(
|
|
DatabaseManager $databases,
|
|
private readonly JushuitanShopBalanceRepository $shopBalances,
|
|
private readonly BillingDetailsPageData $billingDetails,
|
|
private readonly StoreReceivingBillBalanceSnapshotSynchronizer $balanceSnapshots,
|
|
) {
|
|
$this->connection = $databases->connection(
|
|
(string) config('upstream_mirrors.connection', 'business_mirror'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* balance:array{date:?string,source:int,receiving_bill:int,jushuitan:int,added:int,rebuilt:int,matched:int,amount:float,alerts:array{created:int,updated:int}},
|
|
* withdrawals:array{from:string,to:string,processed:int,written:int,upstream_duplicates:int,unmatched:int,account_missing:int,income:float,expense:float},
|
|
* recalculated:?array
|
|
* }
|
|
*/
|
|
public function sync(?string $from = null, ?string $to = null, bool $recalculate = true, bool $rebuild = false): array
|
|
{
|
|
$previousNight = now()->startOfDay()->subDay()->toDateString();
|
|
$to = $this->validDate($to) ?? $previousNight;
|
|
if ($to > $previousNight) {
|
|
$to = $previousNight;
|
|
}
|
|
$from = $this->validDate($from)
|
|
?? (string) config('finance_ledger.opening_date', '2026-10-17');
|
|
if ($from > $to) {
|
|
$from = $to;
|
|
}
|
|
|
|
$balance = $this->syncShopBalances($to, $rebuild);
|
|
$withdrawals = $this->syncWithdrawals($from, $to);
|
|
$recalculated = $recalculate && ($balance['matched'] > 0 || $withdrawals['written'] > 0)
|
|
? app(FinanceLedgerCalculator::class)->recalculate()
|
|
: null;
|
|
|
|
return [
|
|
'balance' => $balance,
|
|
'withdrawals' => $withdrawals,
|
|
'recalculated' => $recalculated,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{date:?string,source:int,added:int,rebuilt:int,matched:int,amount:float}
|
|
*/
|
|
public function syncShopBalances(?string $through = null, bool $rebuild = false): array
|
|
{
|
|
$through = $this->validDate($through) ?? now()->startOfDay()->subDay()->toDateString();
|
|
$snapshot = $this->balanceSnapshots->sync($through);
|
|
$balanceDate = $snapshot['total'] > 0
|
|
? (string) $snapshot['date']
|
|
: $this->shopBalances->latestBalanceDate(
|
|
CarbonImmutable::parse($through),
|
|
StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE,
|
|
);
|
|
if ($balanceDate === null) {
|
|
return [
|
|
'date' => null,
|
|
'source' => 0,
|
|
'receiving_bill' => 0,
|
|
'jushuitan' => 0,
|
|
'added' => 0,
|
|
'rebuilt' => 0,
|
|
'matched' => 0,
|
|
'amount' => 0.0,
|
|
'alerts' => ['created' => 0, 'updated' => 0],
|
|
];
|
|
}
|
|
|
|
$rebuilt = $rebuild ? $this->rebuildShopLedger($balanceDate) : 0;
|
|
$shops = $this->connection->table('fin_shops')
|
|
->orderBy('id')
|
|
->get(['id', 'shop_name', 'platform', 'account_short']);
|
|
$added = $rebuilt > 0 ? 0 : $this->addConfirmedMissingShops($balanceDate, $shops);
|
|
if ($added > 0) {
|
|
$shops = $this->connection->table('fin_shops')
|
|
->orderBy('id')
|
|
->get(['id', 'shop_name', 'platform', 'account_short']);
|
|
}
|
|
$matches = $this->shopBalances->matchesForStores(
|
|
$shops,
|
|
$balanceDate,
|
|
null,
|
|
StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE,
|
|
);
|
|
$updates = [];
|
|
foreach ($matches as $id => $match) {
|
|
if (($match['status'] ?? '') === 'matched' && $match['balance'] !== null) {
|
|
$updates[(int) $id] = round((float) $match['balance'], 2);
|
|
}
|
|
}
|
|
|
|
if ($updates !== []) {
|
|
$hasBalanceDate = $this->connection->getSchemaBuilder()->hasColumn('fin_shops', 'shop_balance_date');
|
|
foreach (array_chunk($updates, 400, true) as $chunk) {
|
|
$case = [];
|
|
foreach ($chunk as $id => $amount) {
|
|
$case[] = 'WHEN ' . $id . ' THEN ' . number_format($amount, 2, '.', '');
|
|
}
|
|
$values = [
|
|
'shop_balance' => $this->connection->raw(
|
|
'CASE `id` ' . implode(' ', $case) . ' ELSE `shop_balance` END',
|
|
),
|
|
'updated_at' => now(),
|
|
];
|
|
if ($hasBalanceDate) {
|
|
$values['shop_balance_date'] = $balanceDate;
|
|
}
|
|
$this->connection->table('fin_shops')
|
|
->whereIn('id', array_keys($chunk))
|
|
->update($values);
|
|
}
|
|
}
|
|
|
|
$alerts = ['created' => 0, 'updated' => 0];
|
|
if ($updates !== []) {
|
|
$companyIds = StoreReceivingAccount::withoutGlobalScopes()
|
|
->whereNull('deleted_at')
|
|
->distinct()
|
|
->pluck('company_id')
|
|
->filter()
|
|
->map(static fn (mixed $id): int => (int) $id)
|
|
->unique()
|
|
->values();
|
|
$alertSynchronizer = app(WithdrawalBalanceAlertSynchronizer::class);
|
|
foreach ($companyIds as $companyId) {
|
|
$result = $alertSynchronizer->syncForDate(
|
|
$companyId,
|
|
$balanceDate,
|
|
StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE,
|
|
);
|
|
$alerts['created'] += $result['created'];
|
|
$alerts['updated'] += $result['updated'];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'date' => $balanceDate,
|
|
'source' => $this->shopBalances->countForDate(
|
|
$balanceDate,
|
|
StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE,
|
|
),
|
|
'receiving_bill' => $snapshot['receiving_bill'],
|
|
'jushuitan' => $snapshot['jushuitan'],
|
|
'added' => $added,
|
|
'rebuilt' => $rebuilt,
|
|
'matched' => count($updates),
|
|
'amount' => round(array_sum($updates), 2),
|
|
'alerts' => $alerts,
|
|
];
|
|
}
|
|
|
|
private function rebuildShopLedger(string $balanceDate): int
|
|
{
|
|
$sourceRows = $this->connection->table(JushuitanShopBalanceRepository::TABLE)
|
|
->where('balance_date', $balanceDate)
|
|
->where('source_scope', StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE)
|
|
->orderByRaw("CASE WHEN data_source = 'jushuitan' THEN 0 ELSE 1 END")
|
|
->orderBy('id')
|
|
->get(['source_platform', 'store_name']);
|
|
if ($sourceRows->isEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
$receivingByNormalized = [];
|
|
foreach (StoreReceivingAccount::withoutGlobalScopes()->whereNull('deleted_at')->get() as $account) {
|
|
$name = trim((string) ($account->erp_store_name ?? ''));
|
|
if ($name !== '') {
|
|
$receivingByNormalized[JushuitanShopBalanceRepository::normalizeStoreName($name)][] = $account;
|
|
}
|
|
}
|
|
$accountShortByShop = $this->connection->table('fin_account_map')
|
|
->where('source', 'shop')
|
|
->whereNotNull('account_short')
|
|
->where('account_short', '<>', '')
|
|
->pluck('account_short', 'source_value');
|
|
|
|
$now = now();
|
|
$seen = [];
|
|
$rows = [];
|
|
foreach ($sourceRows as $source) {
|
|
$name = trim((string) ($source->store_name ?? ''));
|
|
if ($name === '' || isset($seen[$name])) {
|
|
continue;
|
|
}
|
|
$seen[$name] = true;
|
|
$normalized = JushuitanShopBalanceRepository::normalizeStoreName($name);
|
|
$receivingMatches = $receivingByNormalized[$normalized] ?? [];
|
|
$receiving = count($receivingMatches) === 1 ? $receivingMatches[0] : null;
|
|
$accountShort = trim((string) ($accountShortByShop[$name] ?? ''));
|
|
$bankLastFour = $receiving === null ? '' : trim((string) ($receiving->bank_card_last_four ?? ''));
|
|
$payout = $receiving === null ? '' : trim((string) ($receiving->withdrawal_account_name ?? ''));
|
|
if ($payout !== '' && $bankLastFour !== '') {
|
|
$payout .= ' ' . $bankLastFour;
|
|
}
|
|
$row = [
|
|
'shop_name' => mb_substr($name, 0, 200),
|
|
'platform' => mb_substr($this->financialPlatform((string) ($source->source_platform ?? ''), $name), 0, 50),
|
|
'company_name' => $receiving === null
|
|
? null
|
|
: (mb_substr(trim((string) ($receiving->store_entity_name ?? '')), 0, 100) ?: null),
|
|
'account_short' => $accountShort === '' ? null : mb_substr($accountShort, 0, 100),
|
|
'status' => $receiving === null ? '待确认' : $this->financialShopStatus($receiving->store_status),
|
|
'payout_account' => $payout === '' ? null : mb_substr($payout, 0, 100),
|
|
'shop_balance' => 0,
|
|
'remark' => $receiving === null
|
|
? '由聚水潭店铺余额快照重新生成,待确认回款账户'
|
|
: '由店铺主数据与最新店铺余额快照重新生成',
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
];
|
|
if ($this->connection->getSchemaBuilder()->hasColumn('fin_shops', 'shop_balance_date')) {
|
|
$row['shop_balance_date'] = null;
|
|
}
|
|
$rows[] = $row;
|
|
}
|
|
if ($rows === []) {
|
|
return 0;
|
|
}
|
|
|
|
return $this->connection->transaction(function () use ($rows): int {
|
|
$this->connection->table('fin_shops')->delete();
|
|
foreach (array_chunk($rows, 200) as $chunk) {
|
|
$this->connection->table('fin_shops')->insert($chunk);
|
|
}
|
|
|
|
return count($rows);
|
|
});
|
|
}
|
|
|
|
private function addConfirmedMissingShops(string $balanceDate, $existingShops): int
|
|
{
|
|
$sourceRows = $this->connection->table(JushuitanShopBalanceRepository::TABLE)
|
|
->where('balance_date', $balanceDate)
|
|
->where('source_scope', StoreReceivingBillBalanceSnapshotSynchronizer::SOURCE_SCOPE)
|
|
->get(['source_platform', 'store_name']);
|
|
$existingExact = [];
|
|
$existingNormalized = [];
|
|
foreach ($existingShops as $shop) {
|
|
$name = trim((string) ($shop->shop_name ?? ''));
|
|
if ($name === '') {
|
|
continue;
|
|
}
|
|
$existingExact[$name] = true;
|
|
$existingNormalized[JushuitanShopBalanceRepository::normalizeStoreName($name)] = true;
|
|
}
|
|
|
|
$receivingByName = [];
|
|
foreach (StoreReceivingAccount::withoutGlobalScopes()->get() as $account) {
|
|
$name = trim((string) ($account->erp_store_name ?? ''));
|
|
if ($name === '') {
|
|
continue;
|
|
}
|
|
$receivingByName[JushuitanShopBalanceRepository::normalizeStoreName($name)][] = $account;
|
|
}
|
|
|
|
$seen = [];
|
|
$insert = [];
|
|
$now = now();
|
|
foreach ($sourceRows as $source) {
|
|
$name = trim((string) ($source->store_name ?? ''));
|
|
if ($name === '' || isset($seen[$name]) || isset($existingExact[$name])) {
|
|
continue;
|
|
}
|
|
$seen[$name] = true;
|
|
$normalized = JushuitanShopBalanceRepository::normalizeStoreName($name);
|
|
if (isset($existingNormalized[$normalized])) {
|
|
continue;
|
|
}
|
|
$receivingMatches = $receivingByName[$normalized] ?? [];
|
|
$receiving = count($receivingMatches) === 1 ? $receivingMatches[0] : null;
|
|
$bankLastFour = $receiving === null ? '' : trim((string) ($receiving->bank_card_last_four ?? ''));
|
|
$payout = $receiving === null ? '' : trim((string) ($receiving->withdrawal_account_name ?? ''));
|
|
if ($payout !== '' && $bankLastFour !== '') {
|
|
$payout .= ' ' . $bankLastFour;
|
|
}
|
|
$insert[] = [
|
|
'shop_name' => mb_substr($name, 0, 200),
|
|
'platform' => $this->financialPlatform((string) ($source->source_platform ?? ''), $name),
|
|
'company_name' => $receiving === null
|
|
? null
|
|
: (trim((string) ($receiving->store_entity_name ?? '')) ?: null),
|
|
'account_short' => null,
|
|
'status' => $receiving === null
|
|
? '待确认'
|
|
: (trim((string) ($receiving->store_status ?? '')) ?: '在营'),
|
|
'payout_account' => $payout !== '' ? mb_substr($payout, 0, 100) : null,
|
|
'shop_balance' => 0,
|
|
'shop_balance_date' => null,
|
|
'remark' => $receiving === null
|
|
? '由聚水潭店铺余额快照补充,待确认回款账户'
|
|
: '由店铺收款账户和余额快照补充,待确认回款账户',
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
];
|
|
}
|
|
|
|
if ($insert === []) {
|
|
return 0;
|
|
}
|
|
|
|
$inserted = 0;
|
|
foreach (array_chunk($insert, 200) as $chunk) {
|
|
$inserted += $this->connection->table('fin_shops')->insertOrIgnore($chunk);
|
|
}
|
|
|
|
return $inserted;
|
|
}
|
|
|
|
/**
|
|
* @return array{from:string,to:string,processed:int,written:int,upstream_duplicates:int,unmatched:int,account_missing:int,income:float,expense:float}
|
|
*/
|
|
private function syncWithdrawals(string $from, string $to): array
|
|
{
|
|
$shops = $this->connection->table('fin_shops')
|
|
->orderBy('id')
|
|
->get(['id', 'shop_name', 'platform', 'account_short']);
|
|
[$shopByName, $shopsByNormalized] = $this->shopIndexes($shops);
|
|
$existingKeys = $this->existingWithdrawalKeys($from, $to, $shopByName);
|
|
$filters = $this->billingDetails->exportFilters([
|
|
'start' => $from,
|
|
'end' => $to,
|
|
'remark' => ['提现'],
|
|
]);
|
|
$columns = $this->billingDetails->exportColumns([
|
|
'record_code',
|
|
'store_name',
|
|
'finance_remark',
|
|
'bill_item',
|
|
'occurred_at',
|
|
'income',
|
|
'expense',
|
|
]);
|
|
$rows = [];
|
|
$processed = $writtenIncome = $writtenExpense = 0;
|
|
$duplicates = $unmatched = $accountMissing = 0;
|
|
$now = now();
|
|
|
|
foreach ($this->billingDetails->exportRows($filters, $columns) as $row) {
|
|
$processed++;
|
|
$date = substr(trim((string) ($row['occurred_at'] ?? '')), 0, 10);
|
|
$recordCode = trim((string) ($row['record_code'] ?? ''));
|
|
if ($date === '' || $recordCode === '') {
|
|
continue;
|
|
}
|
|
|
|
$sourcePlatform = trim((string) ($row['platform'] ?? ''));
|
|
$sourceStore = trim((string) ($row['store_name'] ?? ''));
|
|
$shop = $this->matchShop($sourceStore, $sourcePlatform, $shopByName, $shopsByNormalized);
|
|
if ($shop === null) {
|
|
$unmatched++;
|
|
continue;
|
|
}
|
|
|
|
$accountShort = trim((string) ($shop->account_short ?? ''));
|
|
if ($accountShort === '' || $accountShort === '/') {
|
|
$accountMissing++;
|
|
continue;
|
|
}
|
|
|
|
$incomeCents = (int) round((float) ($row['income'] ?? 0) * 100);
|
|
$expenseCents = (int) round((float) ($row['expense'] ?? 0) * 100);
|
|
$netCents = $incomeCents - $expenseCents;
|
|
if ($netCents === 0) {
|
|
continue;
|
|
}
|
|
|
|
// 平台提现是从平台账户流出、银行账户流入;平台退回则反向记账。
|
|
$targetIncomeCents = $netCents < 0 ? abs($netCents) : 0;
|
|
$targetExpenseCents = $netCents > 0 ? $netCents : 0;
|
|
$duplicateKey = implode('|', [
|
|
$date,
|
|
$accountShort,
|
|
$targetIncomeCents,
|
|
$targetExpenseCents,
|
|
]);
|
|
if (isset($existingKeys[$duplicateKey])) {
|
|
$duplicates++;
|
|
continue;
|
|
}
|
|
|
|
$detail = implode(' · ', array_values(array_filter([
|
|
$sourcePlatform !== '' ? $sourcePlatform . '账单提现' : '店铺账单提现',
|
|
trim((string) $shop->shop_name),
|
|
trim((string) ($row['bill_item'] ?? '')),
|
|
])));
|
|
$rowKey = hash('sha256', implode("\0", [
|
|
self::WITHDRAWAL_SOURCE,
|
|
$sourcePlatform,
|
|
$recordCode,
|
|
(string) $shop->shop_name,
|
|
]));
|
|
$rows[] = [
|
|
'txn_date' => $date,
|
|
'account_short' => $accountShort,
|
|
'detail' => mb_substr($detail, 0, 4000),
|
|
'income' => number_format($targetIncomeCents / 100, 2, '.', ''),
|
|
'expense' => number_format($targetExpenseCents / 100, 2, '.', ''),
|
|
'approval_no' => mb_substr(($sourcePlatform !== '' ? $sourcePlatform . ':' : '') . $recordCode, 0, 100),
|
|
'source' => self::WITHDRAWAL_SOURCE,
|
|
'source_value' => mb_substr((string) $shop->shop_name, 0, 200),
|
|
'process_type' => '店铺提现',
|
|
'row_key' => $rowKey,
|
|
'synced_at' => $now,
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
];
|
|
$writtenIncome += $targetIncomeCents;
|
|
$writtenExpense += $targetExpenseCents;
|
|
}
|
|
|
|
foreach (array_chunk($rows, 400) as $chunk) {
|
|
$this->connection->table('fin_transactions')->upsert(
|
|
$chunk,
|
|
['row_key'],
|
|
[
|
|
'txn_date', 'account_short', 'detail', 'income', 'expense', 'approval_no',
|
|
'source', 'source_value', 'process_type', 'synced_at', 'updated_at',
|
|
],
|
|
);
|
|
}
|
|
|
|
return [
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'processed' => $processed,
|
|
'written' => count($rows),
|
|
'upstream_duplicates' => $duplicates,
|
|
'unmatched' => $unmatched,
|
|
'account_missing' => $accountMissing,
|
|
'income' => round($writtenIncome / 100, 2),
|
|
'expense' => round($writtenExpense / 100, 2),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string,string>
|
|
*/
|
|
private function existingWithdrawalKeys(string $from, string $to, array $shopByName): array
|
|
{
|
|
$rows = $this->connection->table('fin_transactions')
|
|
->whereBetween('txn_date', [$from, $to])
|
|
->where('source', '<>', self::WITHDRAWAL_SOURCE)
|
|
->where(function ($query): void {
|
|
$query->where('source', 'shop')->orWhere('process_type', '店铺提现');
|
|
})
|
|
->get(['txn_date', 'account_short', 'source_value', 'income', 'expense']);
|
|
|
|
$keys = [];
|
|
foreach ($rows as $row) {
|
|
$shop = $shopByName[trim((string) ($row->source_value ?? ''))]
|
|
?? $shopByName[trim((string) ($row->account_short ?? ''))]
|
|
?? null;
|
|
$account = trim((string) ($shop->account_short ?? $row->account_short ?? ''));
|
|
if ($account === '') {
|
|
continue;
|
|
}
|
|
$key = implode('|', [
|
|
substr((string) $row->txn_date, 0, 10),
|
|
$account,
|
|
(int) round((float) $row->income * 100),
|
|
(int) round((float) $row->expense * 100),
|
|
]);
|
|
$keys[$key] = true;
|
|
}
|
|
|
|
return $keys;
|
|
}
|
|
|
|
/**
|
|
* @return array{0:array<string,object>,1:array<string,list<object>>}
|
|
*/
|
|
private function shopIndexes($shops): array
|
|
{
|
|
$byName = [];
|
|
$byNormalized = [];
|
|
foreach ($shops as $shop) {
|
|
$name = trim((string) ($shop->shop_name ?? ''));
|
|
if ($name === '') {
|
|
continue;
|
|
}
|
|
$byName[$name] = $shop;
|
|
$normalized = JushuitanShopBalanceRepository::normalizeStoreName($name);
|
|
$byNormalized[$normalized][] = $shop;
|
|
}
|
|
|
|
return [$byName, $byNormalized];
|
|
}
|
|
|
|
private function matchShop(string $storeName, string $platform, array $byName, array $byNormalized): ?object
|
|
{
|
|
if (isset($byName[$storeName])) {
|
|
return $byName[$storeName];
|
|
}
|
|
|
|
$normalized = JushuitanShopBalanceRepository::normalizeStoreName($storeName);
|
|
$candidates = $byNormalized[$normalized] ?? [];
|
|
if (count($candidates) === 1) {
|
|
return $candidates[0];
|
|
}
|
|
|
|
$matches = array_values(array_filter(
|
|
$candidates,
|
|
fn (object $shop): bool => $this->platformCompatible($platform, (string) ($shop->platform ?? '')),
|
|
));
|
|
|
|
return count($matches) === 1 ? $matches[0] : null;
|
|
}
|
|
|
|
private function platformCompatible(string $source, string $target): bool
|
|
{
|
|
$source = mb_strtolower(trim($source));
|
|
$target = mb_strtolower(trim($target));
|
|
if ($source === '' || $target === '' || str_contains($source, $target) || str_contains($target, $source)) {
|
|
return true;
|
|
}
|
|
|
|
foreach ([
|
|
['京东', '京东自营', '京东供销'],
|
|
['京喜', '京喜企业店'],
|
|
['淘系支付宝', '天猫', '淘宝', '阿里巴巴', '淘工厂'],
|
|
['淘系微信', '天猫', '淘宝'],
|
|
['抖音', '视频号小店'],
|
|
['唯品会', '唯品会MP'],
|
|
] as $aliases) {
|
|
$sourceHit = array_filter($aliases, static fn (string $alias): bool => str_contains($source, mb_strtolower($alias)));
|
|
$targetHit = array_filter($aliases, static fn (string $alias): bool => str_contains($target, mb_strtolower($alias)));
|
|
if ($sourceHit !== [] && $targetHit !== []) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function financialPlatform(string $sourcePlatform, string $storeName): string
|
|
{
|
|
$sourcePlatform = trim($sourcePlatform);
|
|
$storeName = trim($storeName);
|
|
|
|
foreach ([
|
|
'天猫' => '天猫',
|
|
'淘宝' => '淘宝',
|
|
'1688' => '阿里巴巴',
|
|
'阿里巴巴' => '阿里巴巴',
|
|
'淘工厂' => '淘工厂',
|
|
'京东' => '京东',
|
|
'京喜' => '京喜企业店',
|
|
'拼多多' => '拼多多',
|
|
'抖音' => '抖音小店',
|
|
'头条放心购' => '抖音小店',
|
|
'唯品会MP' => '唯品会MP',
|
|
'得物' => '得物',
|
|
'小红书' => '小红书',
|
|
'快手' => '快手小店',
|
|
] as $needle => $platform) {
|
|
if (str_contains($storeName, $needle) || str_contains($sourcePlatform, $needle)) {
|
|
return $platform;
|
|
}
|
|
}
|
|
|
|
return $sourcePlatform !== '' ? $sourcePlatform : '未填写';
|
|
}
|
|
|
|
private function financialShopStatus(mixed $status): string
|
|
{
|
|
return match (trim((string) $status)) {
|
|
'正常', '在营' => '在营',
|
|
'关店中', '暂停' => '暂停',
|
|
'已关店', '已注销' => '已注销',
|
|
default => trim((string) $status) ?: '待确认',
|
|
};
|
|
}
|
|
|
|
private function validDate(?string $value): ?string
|
|
{
|
|
$value = trim((string) $value);
|
|
$date = \DateTimeImmutable::createFromFormat('!Y-m-d', $value);
|
|
|
|
return $date !== false && $date->format('Y-m-d') === $value ? $value : null;
|
|
}
|
|
}
|