>, tiktok: array>} */ public static function parseStorage(?string $raw): array { $empty = [ self::PLATFORM_FACEBOOK => [], self::PLATFORM_TIKTOK => [], ]; if ($raw === null || trim($raw) === '') { return $empty; } $decoded = json_decode($raw, true); if (!is_array($decoded)) { return $empty; } return [ self::PLATFORM_FACEBOOK => self::normalizePlatformList( $decoded[self::PLATFORM_FACEBOOK] ?? [], self::PLATFORM_FACEBOOK ), self::PLATFORM_TIKTOK => self::normalizePlatformList( $decoded[self::PLATFORM_TIKTOK] ?? [], self::PLATFORM_TIKTOK ), ]; } /** * 合并 POST 数据与已有配置(Token / 测试码留空保留原值) * * @param array $incoming * @param array $existing * @return array{facebook: array>, tiktok: array>} */ public static function mergeForSave(array $incoming, array $existing): array { $existingMap = self::indexById($existing); $result = [ self::PLATFORM_FACEBOOK => [], self::PLATFORM_TIKTOK => [], ]; foreach ([self::PLATFORM_FACEBOOK, self::PLATFORM_TIKTOK] as $platform) { $rows = is_array($incoming[$platform] ?? null) ? $incoming[$platform] : []; if (count($rows) > self::MAX_ITEMS_PER_PLATFORM) { throw new \InvalidArgumentException('像素配置数量超出上限'); } foreach ($rows as $row) { if (!is_array($row)) { continue; } $normalized = self::normalizeRow($row, $platform); $id = (string) ($normalized['id'] ?? ''); if ($id !== '' && isset($existingMap[$id])) { $old = $existingMap[$id]; if (trim((string) ($normalized['access_token'] ?? '')) === '' || strpos((string) ($normalized['access_token'] ?? ''), '***') !== false) { $normalized['access_token'] = (string) ($old['access_token'] ?? ''); } if (trim((string) ($normalized['test_code'] ?? '')) === '') { $normalized['test_code'] = (string) ($old['test_code'] ?? ''); } } if (trim((string) ($normalized['pixel_id'] ?? '')) === '') { continue; } $result[$platform][] = $normalized; } usort($result[$platform], static function (array $a, array $b): int { return ((int) ($a['sort'] ?? 0)) <=> ((int) ($b['sort'] ?? 0)); }); } return $result; } /** * @param array{facebook: array>, tiktok: array>} $config */ public static function encodeStorage(array $config): string { $payload = [ self::PLATFORM_FACEBOOK => $config[self::PLATFORM_FACEBOOK] ?? [], self::PLATFORM_TIKTOK => $config[self::PLATFORM_TIKTOK] ?? [], ]; return json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'; } /** * 管理端 GET:脱敏 access_token * * @param array{facebook: array>, tiktok: array>} $config * @return array{facebook: array>, tiktok: array>} */ public static function maskForAdmin(array $config): array { $mask = static function (array $rows): array { $out = []; foreach ($rows as $row) { $item = $row; $token = (string) ($item['access_token'] ?? ''); $item['access_token'] = $token === '' ? '' : self::maskToken($token); $item['has_access_token'] = $token !== ''; unset($item['access_token_raw']); $out[] = $item; } return $out; }; return [ self::PLATFORM_FACEBOOK => $mask($config[self::PLATFORM_FACEBOOK] ?? []), self::PLATFORM_TIKTOK => $mask($config[self::PLATFORM_TIKTOK] ?? []), ]; } /** * 获取已启用且按 sort 排序的条目(中转页 / 服务端回传) * * @param array{facebook: array>, tiktok: array>} $config * @return array> */ public static function getEnabledSorted(array $config, string $platform): array { $rows = $config[$platform] ?? []; $enabled = []; foreach ($rows as $row) { if ((int) ($row['enabled'] ?? 0) === 1) { $enabled[] = $row; } } usort($enabled, static function (array $a, array $b): int { return ((int) ($a['sort'] ?? 0)) <=> ((int) ($b['sort'] ?? 0)); }); return $enabled; } public static function maskToken(string $token): string { $len = strlen($token); if ($len <= 8) { return '***'; } return substr($token, 0, 3) . '***' . substr($token, -3); } /** * @param array> $rows * @return array> */ private static function normalizePlatformList(array $rows, string $platform): array { $result = []; foreach ($rows as $row) { if (!is_array($row)) { continue; } $normalized = self::normalizeRow($row, $platform); if (trim((string) ($normalized['pixel_id'] ?? '')) === '') { continue; } $result[] = $normalized; } usort($result, static function (array $a, array $b): int { return ((int) ($a['sort'] ?? 0)) <=> ((int) ($b['sort'] ?? 0)); }); return $result; } /** * @param array $row * @return array */ private static function normalizeRow(array $row, string $platform): array { $event = (string) ($row['event'] ?? 'PageView'); if (!in_array($event, self::EVENT_OPTIONS, true)) { $event = 'PageView'; } $id = trim((string) ($row['id'] ?? '')); if ($id === '') { $id = $platform . '_' . uniqid('', true); } return [ 'id' => $id, 'enabled' => (int) ($row['enabled'] ?? 0) === 1 ? 1 : 0, 'server_postback' => (int) ($row['server_postback'] ?? 0) === 1 ? 1 : 0, 'pixel_id' => trim((string) ($row['pixel_id'] ?? '')), 'access_token' => trim((string) ($row['access_token'] ?? '')), 'test_code' => trim((string) ($row['test_code'] ?? '')), 'event' => $event, 'sort' => max(0, (int) ($row['sort'] ?? 0)), ]; } /** * @param array{facebook: array>, tiktok: array>} $config * @return array> */ private static function indexById(array $config): array { $map = []; foreach ([self::PLATFORM_FACEBOOK, self::PLATFORM_TIKTOK] as $platform) { foreach ($config[$platform] ?? [] as $row) { $id = (string) ($row['id'] ?? ''); if ($id !== '') { $map[$id] = $row; } } } return $map; } }