(string) $row['id'], 'name' => (string) $row['title'], 'company' => ['name' => (string) $row['company_name']], ]; } return ['items' => $items]; } /** * Detail jedné nabídky. Vrací data i pro zavřené nabídky — kampaň je potřebuje * i poté, co se nabídka zavřela. * TODO: SELECT jedné nabídky podle ID. */ function offer(string $externalId): ?array { $row = db_offer($externalId); if ($row === null) { return null; } return [ 'external_id' => (string) $row['id'], // Otisk obsahu: musí se změnit při každé změně obsahu a nesmí se měnit jinak. 'version' => hash('sha256', $row['title'] . '|' . $row['description'] . '|' . $row['salary_min']), 'name' => (string) $row['title'], 'description' => (string) $row['description'], 'language_code' => (string) $row['language'], // volitelné 'status' => (string) $row['status'], // published | pending | closed 'company' => [ 'name' => (string) $row['company_name'], 'introduction' => (string) $row['company_about'], // volitelné 'logo_url' => (string) $row['company_logo'], // volitelné, veřejné HTTPS 'logo_approved' => (bool) $row['company_logo_ok'], // volitelné ], 'salaries' => $row['salary_min'] === null ? [] : [[ 'minimum' => (int) $row['salary_min'], 'maximum' => $row['salary_max'] === null ? null : (int) $row['salary_max'], 'currency' => (string) $row['salary_currency'], 'measure' => 'month', ]], 'area_id' => (string) $row['category_id'], // bez něj: start bez odkladu, cílení bez zájmů 'area_root_id' => (string) $row['category_root_id'], // bez něj: výchozí stránka // Pole posílej i prázdné — prázdné znamená „remote", chybějící znamená „neumíme". 'locations' => array_map(static fn($city) => ['city' => (string) $city], (array) $row['cities']), 'updated_at' => date(DATE_ATOM, (int) $row['updated_ts']), ]; } /** * Změnový výpis: jen identita a verze, ne obsah. * TODO: SELECT nabídek s updated_at >= $updatedSince, stabilně seřazených. */ function changes(string $updatedSince, mixed $page): array { $rows = db_offers_changed_since($updatedSince, $page); $items = []; foreach ($rows['rows'] as $row) { $items[] = [ 'external_id' => (string) $row['id'], 'version' => hash('sha256', $row['title'] . '|' . $row['description'] . '|' . $row['salary_min']), 'status' => (string) $row['status'], 'updated_at' => date(DATE_ATOM, (int) $row['updated_ts']), ]; } return ['items' => $items, 'next_page' => $rows['next_page']]; } /** * Kandidáti na kampaň = tvoje byznysové rozhodnutí, že se za nabídku má platit. * Zrušení vyjádři jako status "cancelled" u téže dvojice (source_type, external_key), * nikdy vynecháním položky z výpisu. * TODO: SELECT zakoupených produktů viditelnosti / PPC. */ function candidates(?string $updatedSince, mixed $page): array { $rows = db_purchases($updatedSince, $page); $items = []; foreach ($rows['rows'] as $row) { $items[] = [ 'source_type' => (string) $row['kind'], // visibility | ppc 'external_key' => (string) $row['id'], 'offer_external_id' => (string) $row['offer_id'], 'product' => (string) $row['product'], // klíč dohodnutý s UpWolfem 'window' => [ 'since' => date(DATE_ATOM, (int) $row['since_ts']), 'until' => $row['until_ts'] === null ? null : date(DATE_ATOM, (int) $row['until_ts']), ], 'status' => $row['cancelled'] ? 'cancelled' : 'active', 'budget_override' => null, // volitelné, v haléřích 'delay_override_days' => null, // volitelné 'first_campaign_boost' => false, // volitelné 'updated_at' => date(DATE_ATOM, (int) $row['updated_ts']), ]; } return ['items' => $items, 'next_page' => $rows['next_page']]; } /** * Počet reakcí od zadaného okamžiku. `count` jsou kvalitní reakce (definice je na tobě), * `total_count` všechny. Podle `count` se kampaň ukončí, až nabídka nasbírá dost. * TODO: COUNT(*) přihlášek k nabídce od $from. */ function applications(string $externalId, string $from): ?array { if (db_offer($externalId) === null) { return null; } return [ 'external_id' => $externalId, 'from' => $from, 'count' => (int) db_quality_applications($externalId, $from), // musí být integer, ne string 'total_count' => (int) db_all_applications($externalId, $from), ]; } // --------------------------------------------------------------------------- // 5) Pomocné funkce // --------------------------------------------------------------------------- function respond(array $payload): never { echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } function fail(int $status, string $code, string $message): never { http_response_code($status); echo json_encode(['error' => ['code' => $code, 'message' => $message]], JSON_UNESCAPED_UNICODE); exit; } // --------------------------------------------------------------------------- // 6) Zástupné funkce nad databází — nahraď svými dotazy // --------------------------------------------------------------------------- function db_active_offers(): array { return []; } function db_offer(string $id): ?array { return null; } function db_offers_changed_since(string $since, mixed $page): array { return ['rows' => [], 'next_page' => null]; } function db_purchases(?string $since, mixed $page): array { return ['rows' => [], 'next_page' => null]; } function db_quality_applications(string $id, string $from): int { return 0; } function db_all_applications(string $id, string $from): int { return 0; }