>,total:int} */ public function paginate(int $page, int $perPage, array $filters = []): array { $dataset = $this->datasets->get(self::DATASET); if (! $this->mirror->ready($dataset)) { throw new RuntimeException('store_account_mirror_not_ready'); } $page = max($page, 1); $perPage = min(max($perPage, 1), 100); $query = $this->mirror->connection()->table($dataset->targetTable()); $columns = [ 'store_name' => 'storeName', 'operation_owner' => 'operationOwner', 'operation_department_head' => 'operationDepartmentHead', 'operation_director' => 'operationDirector', 'profit_center' => 'profitCenter', 'account' => 'account', 'bound_phone' => 'boundPhone', 'verification_contact' => 'verificationContact', 'account_group_name' => 'accountGroupName', ]; foreach ($columns as $key => $column) { $value = mb_substr(trim((string) ($filters[$key] ?? '')), 0, 191); if ($value !== '') $query->where($column, 'like', '%' . addcslashes($value, '%_\\') . '%'); } $accountType = trim((string) ($filters['account_type'] ?? '')); if ($accountType !== '') $query->where('accountType', '=', $accountType === '主账号' ? 'MAIN' : ($accountType === '子账号' ? 'SUB' : $accountType)); $passwordState = trim((string) ($filters['password_configured'] ?? '')); if (in_array($passwordState, ['已设置', '未设置', '状态未同步'], true)) { if ($passwordState === '状态未同步') $query->whereNull('passwordConfigured'); else $query->where('passwordConfigured', $passwordState === '已设置'); } foreach (['created_at' => 'createdAt', 'updated_at' => 'updatedAt'] as $key => $column) { foreach (['_from' => '>=', '_to' => '<='] as $suffix => $operator) { $date = trim((string) ($filters[$key . $suffix] ?? '')); if (preg_match('/^\d{4}-\d{2}-\d{2}$/D', $date)) $query->whereDate($column, $operator, $date); } } $total = (clone $query)->count(); $sortColumns = [ 'store_name' => 'storeName', 'operation_owner' => 'operationOwner', 'operation_department_head' => 'operationDepartmentHead', 'operation_director' => 'operationDirector', 'profit_center' => 'profitCenter', 'account_type' => 'accountType', 'account' => 'account', 'password_configured' => 'passwordConfigured', 'bound_phone' => 'boundPhone', 'verification_contact' => 'verificationContact', 'account_group_name' => 'accountGroupName', 'created_at' => 'createdAt', 'updated_at' => 'updatedAt', ]; $sortColumn = $sortColumns[$filters['_sort'] ?? ''] ?? null; $direction = ($filters['_direction'] ?? 'asc') === 'desc' ? 'desc' : 'asc'; if ($sortColumn !== null) { $query->orderBy($sortColumn, $direction)->orderByDesc('id'); } else { $query->orderByDesc('id'); } $rows = $query ->forPage($page, $perPage) ->get() ->map(fn (object $row): array => [ 'id' => $row->id, 'store_name' => $row->storeName, 'operation_owner' => $row->operationOwner, 'operation_department_head' => $row->operationDepartmentHead, 'operation_director' => $row->operationDirector, 'profit_center' => $row->profitCenter, 'account_type' => $this->accountType($row->accountType), 'account' => $row->account, // Only the safe boolean may enter the mirror, never a password. 'password_configured' => isset($row->passwordConfigured) ? (bool) $row->passwordConfigured : null, 'bound_phone' => $row->boundPhone, 'verification_contact' => $row->verificationContact, 'account_group_name' => $row->accountGroupName, 'created_at' => $row->createdAt, 'updated_at' => $row->updatedAt, ]) ->all(); return ['data' => $rows, 'total' => $total]; } public function containsAccount(string $id): bool { $dataset = $this->datasets->get(self::DATASET); if (! $this->mirror->ready($dataset)) { throw new RuntimeException('store_account_mirror_not_ready'); } return $this->mirror->connection()->table($dataset->targetTable())->where('id', $id)->exists(); } public function containsAccounts(array $ids): bool { $ids = array_values(array_unique($ids)); if ($ids === []) { return false; } $dataset = $this->datasets->get(self::DATASET); if (! $this->mirror->ready($dataset)) { throw new RuntimeException('store_account_mirror_not_ready'); } return $this->mirror->connection()->table($dataset->targetTable()) ->whereIn('id', $ids)->distinct()->count('id') === count($ids); } /** * 店铺维度报表的归属字段覆盖表,以店铺名称(storeName)为键。 * * @return array */ public function ownershipByStoreName(): array { $dataset = $this->datasets->get(self::DATASET); if (! $this->mirror->ready($dataset)) { throw new RuntimeException('store_account_mirror_not_ready'); } $map = []; foreach ($this->mirror->connection()->table($dataset->targetTable()) ->select('storeName', 'operationOwner', 'operationDirector', 'profitCenter') ->get() as $row) { $name = trim((string) $row->storeName); if ($name === '') { continue; } $map[$name] = [ 'operation_owner' => trim((string) $row->operationOwner), 'operation_director' => trim((string) $row->operationDirector), 'profit_center' => trim((string) $row->profitCenter), ]; } return $map; } private function accountType(mixed $value): ?string { $type = trim((string) $value); return match (strtoupper($type)) { 'MAIN' => '主账号', 'SUB' => '子账号', '', '/' => null, default => $type, }; } }