'operations_replica.rpt.fys_companyInfo', 'store-information.read' => 'operations_replica.rpt.fys_shopInfo', 'trademark-information.read' => 'operations_replica.rpt.fys_TrademarkInfo', ]; private readonly ConnectionInterface $connection; public function __construct( private readonly BusinessMirrorDatasetRegistry $datasets, private readonly GenericBusinessMirrorRepository $mirror, ) { $this->connection = $mirror->connection(); } public function ready(string $scope): bool { return $this->mirror->ready($this->dataset($scope)); } /** @return array{data: array>, total: int} */ public function paginate(string $scope, int $page, int $perPage): array { if (! $this->ready($scope)) { throw new RuntimeException('business_mirror_dataset_not_ready'); } $page = max($page, 1); $perPage = min(max($perPage, 1), 100); return match ($scope) { 'company-information.read' => $this->companies($page, $perPage), 'store-information.read' => $this->stores($page, $perPage), 'trademark-information.read' => $this->trademarks($page, $perPage), default => throw new InvalidArgumentException('未知的业务资料镜像范围:' . $scope), }; } /** @param array $filters @return array> */ public function allStores(array $filters = []): array { if (! $this->ready('store-information.read')) { throw new RuntimeException('business_mirror_dataset_not_ready'); } $query = $this->storeQuery()->orderByDesc('s.Id'); $this->applyStoreFilters($query, $filters); return $query ->get() ->map(static fn (object $row): array => (array) $row) ->all(); } /** * Return the stable store-information rows used to seed the receiving * account pages. The query stays on the shared business mirror and only * reads the approved shop/company fields. * * @return list> */ public function receivingAccountStores(): array { if (! $this->ready('store-information.read')) { throw new RuntimeException('business_mirror_dataset_not_ready'); } $latestCompanies = $this->connection->table('ops_fys_company_info') ->selectRaw('unifiedSocialCreditCode, MAX(Id) AS latest_id') ->whereNotNull('unifiedSocialCreditCode') ->where('unifiedSocialCreditCode', '<>', '') ->groupBy('unifiedSocialCreditCode'); return $this->connection->table('ops_fys_shop_info AS s') ->leftJoinSub($latestCompanies, 'company_keys', function ($join): void { $join->on('company_keys.unifiedSocialCreditCode', '=', 's.unifiedSocialCreditCode'); }) ->leftJoin('ops_fys_company_info AS c', 'c.Id', '=', 'company_keys.latest_id') ->selectRaw(<<<'SQL' s.Id AS id, s.sotreId AS store_id, s.shopName AS shop_name, s.erpShopName AS erp_store_name, s.unifiedSocialCreditCode AS unified_social_credit_code, c.businessLicenseName AS company_name, s.platform AS source_platform, s.shopState AS store_status, s.shopType AS store_type SQL) ->orderBy('s.Id') ->get() ->map(static fn (object $row): array => (array) $row) ->all(); } /** 店铺维度报表的店铺字段查询条件;日期左闭右开 [from, to)。 */ private function applyStoreFilters($query, array $filters): void { foreach ([ 'erp_store_name' => 's.erpShopName', 'store_name' => 's.shopName', 'store_id' => 's.sotreId', 'profit_center' => 's.responsibleDepartment', 'store_manager' => 's.shopHead', 'department_manager' => 's.customerDeptManager', ] as $key => $column) { $value = trim((string) ($filters[$key] ?? '')); if ($value !== '') { $query->where($column, '=', $value); } } if (trim((string) ($filters['company_type'] ?? '')) !== '') { $query->where('c.businessLicenseType', '=', trim((string) $filters['company_type'])); } if (trim((string) ($filters['status'] ?? '')) !== '') { $query->where('s.shopState', '=', (string) $filters['status']); } if (trim((string) ($filters['opened_at_from'] ?? '')) !== '') { $query->where('s.openTime', '>=', $filters['opened_at_from']); } if (trim((string) ($filters['opened_at_to'] ?? '')) !== '') { $query->where('s.openTime', '<', $filters['opened_at_to']); } } /** @return array>> */ public function companiesByNames(array $names): array { if (! $this->ready('company-information.read')) { throw new RuntimeException('business_mirror_dataset_not_ready'); } $names = array_values(array_unique(array_filter(array_map('trim', $names)))); if ($names === []) { return []; } return $this->connection->table('ops_fys_company_info') ->whereIn('businessLicenseName', $names) ->orderByDesc('Id') ->get() ->map(static fn (object $row): array => (array) $row) ->groupBy(static fn (array $row): string => (string) $row['businessLicenseName']) ->map(static fn ($rows): array => $rows->values()->all()) ->all(); } /** @return array{data: array>, total: int} */ private function companies(int $page, int $perPage): array { $query = $this->connection->table('ops_fys_company_info') ->selectRaw(<<<'SQL' Id AS id, unifiedSocialCreditCode AS unified_social_credit_code, businessLicenseName AS company_name, registeredCapital AS subscribed_capital, businessLicenseType AS company_type, IncorporationDate AS establishment_date, legalRepresentative AS legal_representative, registeredAddress AS registered_address, businessScope AS business_scope, accountSerialNumber AS basic_deposit_account_number, registeredCapitalVaildTime AS subscribed_at, companyStatus AS company_status, financialManager AS financial_officer, supervisor AS supervisor, taxCategoryAndType AS taxpayer_category, NULL AS shareholders SQL); return $this->page($query, $page, $perPage, 'Id'); } /** @return array{data: array>, total: int} */ private function stores(int $page, int $perPage): array { return $this->page($this->storeQuery(), $page, $perPage, 's.Id'); } private function storeQuery() { $latestCompanies = $this->connection->table('ops_fys_company_info') ->selectRaw('unifiedSocialCreditCode, MAX(Id) AS latest_id') ->whereNotNull('unifiedSocialCreditCode') ->where('unifiedSocialCreditCode', '<>', '') ->groupBy('unifiedSocialCreditCode'); $query = $this->connection->table('ops_fys_shop_info AS s') ->leftJoinSub($latestCompanies, 'company_keys', function ($join): void { $join->on('company_keys.unifiedSocialCreditCode', '=', 's.unifiedSocialCreditCode'); }) ->leftJoin('ops_fys_company_info AS c', 'c.Id', '=', 'company_keys.latest_id') ->leftJoinSub( $this->connection->table('ops_fys_trademark_info AS trademark_keys') ->selectRaw('trademark_keys.trademarkNumber, MAX(trademark_keys.Id) AS latest_id') ->where('trademark_keys.isDelete', 0) ->whereNotNull('trademark_keys.trademarkNumber') ->where('trademark_keys.trademarkNumber', '<>', '') ->groupBy('trademark_keys.trademarkNumber'), 'trademark_keys', function ($join): void { $join->on('trademark_keys.trademarkNumber', '=', 's.trademarkNumber'); }, ) ->leftJoin('ops_fys_trademark_info AS t', 't.Id', '=', 'trademark_keys.latest_id') ->selectRaw(<<<'SQL' s.Id AS id, s.sotreId AS store_id, s.openTime AS opened_at, s.renewalTime AS renewed_at, s.erpShopName AS erp_store_name, s.shopName AS store_name, s.unifiedSocialCreditCode AS unified_social_credit_code, c.businessLicenseName AS company_name, c.businessLicenseType AS company_type, s.trademarkNumber AS trademark_number, t.trademarkName AS trademark_name, s.platform AS platform, s.responsibleDepartment AS profit_center, s.shopHead AS store_manager, s.customerDeptManager AS department_manager, s.shopState AS store_status, s.shopBankAccount AS withdrawal_account, s.shopType AS store_type, s.channelType AS channel_mode SQL); return $query; } /** @return array{data: array>, total: int} */ private function trademarks(int $page, int $perPage): array { $query = $this->connection->table('ops_fys_trademark_info') ->where('isDelete', 0) ->selectRaw(<<<'SQL' Id AS id, trademarkNumber AS trademark_number, trademarkName AS trademark_name, internationalClassification AS trademark_type, trademarkStatus AS trademark_status, registrationDate AS registration_date, termOfValidity AS expiry_date, approvedCommodities AS approved_goods, transferenceUser AS final_holder, registrationAddress AS registered_address, custodyDepartment AS custody_department, remark AS notes SQL); return $this->page($query, $page, $perPage, 'Id'); } /** @return array{data: array>, total: int} */ private function page($query, int $page, int $perPage, string $orderColumn): array { $total = (clone $query)->count(); $rows = $query->orderByDesc($orderColumn) ->forPage($page, $perPage) ->get() ->map(static fn (object $row): array => (array) $row) ->all(); return ['data' => $rows, 'total' => $total]; } private function dataset(string $scope): BusinessMirrorDataset { $key = self::DATASETS[$scope] ?? null; if ($key === null) { throw new InvalidArgumentException('未知的业务资料镜像范围:' . $scope); } return $this->datasets->get($key); } }