151 lines
5.4 KiB
PHP
151 lines
5.4 KiB
PHP
<?php
|
||
|
||
namespace App\Business;
|
||
|
||
use App\Models\Business\StoreReceivingAccount;
|
||
use Modules\EcommerceFinance\Repositories\BusinessInformationMirrorRepository;
|
||
|
||
class StoreReceivingAccountSynchronizer
|
||
{
|
||
public function __construct(
|
||
private readonly BusinessInformationMirrorRepository $mirror,
|
||
) {
|
||
}
|
||
|
||
/**
|
||
* Seed account rows from the read-only store-information mirror while
|
||
* preserving any credentials already entered by the user.
|
||
*
|
||
* @return array{created:int,updated:int,skipped:int,by_platform:array<string,int>}
|
||
*/
|
||
public function sync(?int $companyId = null): array
|
||
{
|
||
$companyId ??= (int) (company_id() ?: 1);
|
||
$created = 0;
|
||
$updated = 0;
|
||
$skipped = 0;
|
||
$byPlatform = [];
|
||
|
||
// Load the local directory once; avoid one SQL lookup per upstream
|
||
// shop every time a tab is opened.
|
||
$existing = StoreReceivingAccount::withoutGlobalScopes()
|
||
->withTrashed()
|
||
->where('company_id', $companyId)
|
||
->get();
|
||
$byWallet = [];
|
||
$byPlatformAndName = [];
|
||
$byName = [];
|
||
|
||
foreach ($existing as $account) {
|
||
$platform = (string) $account->store_platform;
|
||
$wallet = trim((string) $account->wallet_number);
|
||
$name = trim((string) $account->store_name);
|
||
if ($wallet !== '') {
|
||
$byWallet[$platform . "\0" . $wallet] ??= $account;
|
||
}
|
||
if ($name !== '') {
|
||
$byPlatformAndName[$platform . "\0" . $name] ??= $account;
|
||
$byName[$name] ??= $account;
|
||
}
|
||
}
|
||
|
||
foreach ($this->mirror->receivingAccountStores() as $row) {
|
||
$erpStoreName = trim((string) ($row['erp_store_name'] ?? ''));
|
||
$platform = $this->classify($erpStoreName, (string) ($row['source_platform'] ?? ''));
|
||
|
||
if ($erpStoreName === '' || $platform === null) {
|
||
$skipped++;
|
||
continue;
|
||
}
|
||
|
||
$storeId = trim((string) ($row['store_id'] ?? $row['id'] ?? ''));
|
||
$account = $storeId !== ''
|
||
? ($byWallet[$platform . "\0" . $storeId] ?? null)
|
||
: null;
|
||
|
||
// Older rows created before wallet_number was populated may have
|
||
// been classified by the upstream platform. Reuse them by ERP
|
||
// name so a corrected prefix classification does not duplicate a
|
||
// store row.
|
||
$account ??= $byPlatformAndName[$platform . "\0" . $erpStoreName]
|
||
?? $byName[$erpStoreName]
|
||
?? null;
|
||
|
||
$attributes = [
|
||
'company_id' => $companyId,
|
||
'store_platform' => $platform,
|
||
'store_name' => $erpStoreName,
|
||
'store_entity_name' => trim((string) ($row['company_name'] ?? '')),
|
||
'shop_name' => trim((string) ($row['shop_name'] ?? '')) ?: $erpStoreName,
|
||
'store_status' => $this->status($row['store_status'] ?? null),
|
||
'wallet_number' => $storeId,
|
||
'store_id' => $storeId,
|
||
'source_platform' => trim((string) ($row['source_platform'] ?? '')),
|
||
];
|
||
|
||
if ($account === null) {
|
||
$account = new StoreReceivingAccount();
|
||
$account->fill($attributes);
|
||
$account->save();
|
||
$created++;
|
||
if ($storeId !== '') {
|
||
$byWallet[$platform . "\0" . $storeId] = $account;
|
||
}
|
||
$byPlatformAndName[$platform . "\0" . $erpStoreName] = $account;
|
||
$byName[$erpStoreName] ??= $account;
|
||
} else {
|
||
if ($account->trashed()) {
|
||
$account->restore();
|
||
}
|
||
$account->fill($attributes);
|
||
if ($account->isDirty()) {
|
||
$account->save();
|
||
$updated++;
|
||
}
|
||
if ($storeId !== '') {
|
||
$byWallet[$platform . "\0" . $storeId] = $account;
|
||
}
|
||
$byPlatformAndName[$platform . "\0" . $erpStoreName] = $account;
|
||
$byName[$erpStoreName] ??= $account;
|
||
}
|
||
|
||
$byPlatform[$platform] = ($byPlatform[$platform] ?? 0) + 1;
|
||
}
|
||
|
||
return compact('created', 'updated', 'skipped', 'byPlatform');
|
||
}
|
||
|
||
public function classify(string $erpStoreName, string $sourcePlatform = ''): ?string
|
||
{
|
||
$prefix = preg_split('/[-—_-|丨\/\\()()【】\[\]·::]+/u', trim($erpStoreName), 2)[0] ?? '';
|
||
$prefix = trim((string) $prefix);
|
||
$sourcePlatform = trim($sourcePlatform);
|
||
|
||
$platforms = ['淘宝分销', '阿里巴巴', '天猫', '唯品会', '淘宝', '京喜', '京东', '抖音', '拼多多'];
|
||
|
||
foreach ($platforms as $platform) {
|
||
if (str_starts_with($prefix, $platform)) {
|
||
return $platform;
|
||
}
|
||
}
|
||
|
||
foreach ($platforms as $platform) {
|
||
if (str_starts_with($sourcePlatform, $platform)) {
|
||
return $platform;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private function status(mixed $value): ?string
|
||
{
|
||
return match ((string) $value) {
|
||
'0', '正常' => '正常',
|
||
'3', '关闭中', '关店中' => '关店中',
|
||
'6', '已关闭', '已关店' => '已关店',
|
||
default => trim((string) $value) ?: null,
|
||
};
|
||
}
|
||
}
|