СущностиHotelier — юрлицо, users[], statusHotel — name, address, geo, dna_attributes[], honest_minus*, checkin/checkout, moderation_statusRoomType — hotel, name, capacity, base_price, attributes[] (quiet, balcony, desk, wifi_mbps), total, in_auction, allowed_perks[]Availability — room_type, date, free, price_overrideAgentPolicy — hotel, strategy, floor_pct, occupancy_rules, horizon_rules, perks[], auto_rebid, max_rebids, min_match, daily_auction_limitLot — user, city, dates, guests, budget, dna_json, status, expires_atOffer — lot, hotel, room_type, price, perks[], match_pct, expires_at, rank, source (agent | manual), statusBidEvent — lot, offer, type, reason, ts (лог торгов)Booking — offer, guest, total, status, pms_ref
REST (FastAPI) · /api/v1GET/POST /hotels · PATCH /hotels/{id}GET/POST /hotels/{id}/room-typesPUT /room-types/{id}/availabilityGET/PUT /hotels/{id}/agent-policyPOST /hotels/{id}/agent-policy/simulateGET /lots?match_min=70&status=liveGET /lots/{id} · GET /lots/{id}/eventsPOST /lots/{id}/offers (manual)DELETE /offers/{id} (снять)GET /bookings— админ —GET /admin/lots · /admin/statsPOST /admin/hotels/{id}/moderateGET/PUT /admin/settingsGET/POST /admin/users · rolesWS /ws/hotelier/{id} → offer.new, offer.outbid, lot.closed, booking.created
Агент · воркер (Celery/arq)1. on lot.created — для каждого отеля в радиусе: match = cos-similarity(dna_lot, attrs_room_type) × доступность. Если match < min_match — BidEvent «skipped».2. first_offer — цена = base × max(floor_pct, rule(occupancy, horizon)); перки — из allowed_perks по ценности для ДНК; апгрейд — если free(upper) ≥ 3.3. on offer.outbid — если auto_rebid и rebids < max: снизить к полу шагом 3–5% или добавить перку; иначе BidEvent «hold».4. guard-rails площадки — скидка > max_discount → заморозка и очередь модерации; offers_per_lot ≤ 10; TTL оффера 10 мин.5. on lot.accepted — Booking → PMS webhook, остальные офферы → closed.