connection = $database->connection( (string) config('upstream_mirrors.connection', 'business_mirror'), ); } /** * @param iterable $stores * @return array */ public function matchesForStores(iterable $stores, string $balanceDate): array { $stores = is_array($stores) ? $stores : iterator_to_array($stores); $result = []; foreach ($stores as $store) { $result[(int) $store->id] = $this->emptyMatch('not_ready'); } if (! $this->hasTable()) { return $result; } $sourceRows = $this->connection->table(self::TABLE) ->where('balance_date', $balanceDate) ->orderBy('id') ->get([ 'id', 'source_platform', 'store_name', 'normalized_store_name', 'platform_account_id', 'balance', ]); if ($sourceRows->isEmpty()) { return $result; } foreach ($stores as $store) { $names = array_values(array_unique(array_filter([ self::normalizeStoreName((string) ($store->shop_name ?? '')), self::normalizeStoreName((string) ($store->erp_store_name ?? '')), ]))); $matches = $sourceRows->filter( static fn (object $row): bool => in_array((string) $row->normalized_store_name, $names, true), )->values(); if ($matches->isEmpty()) { $result[(int) $store->id] = $this->emptyMatch('unmatched'); continue; } $targetPlatform = self::platformKey((string) ($store->store_platform ?? '')); if ($targetPlatform !== null) { $matches = $matches->filter( static function (object $row) use ($targetPlatform): bool { $sourcePlatform = self::platformKey((string) ($row->source_platform ?? '')) ?? self::platformKey((string) self::sourcePlatform((string) ($row->store_name ?? ''))); return $sourcePlatform !== null && $sourcePlatform === $targetPlatform; }, )->values(); if ($matches->isEmpty()) { $result[(int) $store->id] = $this->emptyMatch('unmatched'); continue; } } if ($matches->count() !== 1) { $result[(int) $store->id] = $this->emptyMatch('ambiguous'); continue; } $match = $matches->first(); $result[(int) $store->id] = [ 'status' => 'matched', 'balance' => (float) $match->balance, 'source_store_name' => (string) $match->store_name, 'source_platform' => $match->source_platform === null ? null : (string) $match->source_platform, ]; } return $result; } public function countForDate(string $balanceDate): int { if (! $this->hasTable()) { return 0; } return $this->connection->table(self::TABLE) ->where('balance_date', $balanceDate) ->count(); } public static function normalizeStoreName(string $value): string { $value = trim(str_replace(['-', '—', '–'], '-', $value)); $value = preg_replace( '/^(支付宝|天猫|淘宝|阿里巴巴|1688|京东商城|京东|京喜小店|京喜|拼多多|抖音|快手|小红书|唯品会|得物|淘工厂)[\s\-_::]+/u', '', $value, ) ?? $value; $value = preg_replace('/\s+/u', '', $value) ?? $value; return mb_substr(mb_strtolower($value), 0, 255); } public static function sourcePlatform(string $storeName): ?string { if (preg_match( '/^(支付宝|天猫|淘宝|阿里巴巴|1688|京东商城|京东|京喜小店|京喜|拼多多|抖音|快手|小红书|唯品会|得物|淘工厂)[\s\-—–_::]+/u', trim($storeName), $matches, ) === 1) { return $matches[1]; } return str_ends_with(mb_strtolower(trim($storeName)), '1688') ? '1688' : null; } private static function platformKey(string $platform): ?string { $platform = mb_strtolower(trim($platform)); foreach ([ 'tmall' => ['天猫'], 'taobao' => ['淘宝'], '1688' => ['1688', '阿里巴巴'], 'jd' => ['京东'], 'jingxi' => ['京喜'], 'pinduoduo' => ['拼多多'], 'douyin' => ['抖音', '头条放心购'], 'kuaishou' => ['快手'], 'xiaohongshu' => ['小红书'], 'vipshop' => ['唯品会'], 'dewu' => ['得物'], 'taogongchang' => ['淘工厂'], ] as $key => $aliases) { foreach ($aliases as $alias) { if (str_contains($platform, mb_strtolower($alias))) { return $key; } } } return null; } /** @return array{status:string,balance:null,source_store_name:null,source_platform:null} */ private function emptyMatch(string $status): array { return [ 'status' => $status, 'balance' => null, 'source_store_name' => null, 'source_platform' => null, ]; } private function hasTable(): bool { return Schema::connection($this->connection->getName())->hasTable(self::TABLE); } }