# Züchter-Rolle — Banyaro.app ## Vollständige Ausarbeitung zur Umsetzung --- ## 1. Übersicht ### Rollen-Modell | Rolle | Beschreibung | |---|---| | `user` | Normaler Hundebesitzer (bestehend) | | `breeder` | Verifizierter Züchter (neu) | | `admin` | René — Plattform-Administration (bestehend) | Die Rolle `breeder` erweitert `user` — ein Züchter hat alle normalen Nutzerrechte plus Züchter-spezifische Features. --- ## 2. Datenbankstruktur ### Tabelle: `users` (Erweiterung) ```sql ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'user'; -- Werte: 'user', 'breeder', 'admin' ALTER TABLE users ADD COLUMN breeder_status VARCHAR(20) DEFAULT NULL; -- Werte: NULL (kein Antrag), 'pending', 'approved', 'rejected' ``` --- ### Tabelle: `breeder_profiles` ```sql CREATE TABLE breeder_profiles ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL UNIQUE, kennel_name VARCHAR(100) NOT NULL, -- Zwingername breed_id INTEGER NOT NULL, -- Nur eine Rasse (FK auf breeds) vdh_member BOOLEAN DEFAULT FALSE, association VARCHAR(100), -- Zuchtverein (z.B. KFZ, VDH) description TEXT, -- Freitext Vorstellung website VARCHAR(255), location_lat FLOAT, location_lng FLOAT, location_city VARCHAR(100), show_on_map BOOLEAN DEFAULT TRUE, -- Auf Karte sichtbar verified_at DATETIME DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (breed_id) REFERENCES breeds(id) ); ``` --- ### Tabelle: `breeder_documents` ```sql CREATE TABLE breeder_documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, document_type VARCHAR(50) NOT NULL, -- Werte: 'vdh_membership', 'breeding_permit' file_path VARCHAR(255) NOT NULL, uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); ``` --- ### Tabelle: `litters` (Würfe) ```sql CREATE TABLE litters ( id INTEGER PRIMARY KEY AUTOINCREMENT, breeder_id INTEGER NOT NULL, -- FK auf breeder_profiles breed_id INTEGER NOT NULL, father_name VARCHAR(100), mother_name VARCHAR(100), birth_date DATE, expected_date DATE, -- Bei geplantem Wurf total_puppies INTEGER, available_count INTEGER, description TEXT, health_tests TEXT, -- HD, ED, Augen etc. als JSON price_range VARCHAR(50), -- Optional, z.B. "1200-1500 €" status VARCHAR(20) DEFAULT 'planned', -- Werte: 'planned', 'born', 'available', 'closed' visible BOOLEAN DEFAULT TRUE, -- Züchter steuert Sichtbarkeit visible_until DATE DEFAULT NULL, -- Optional: Ablaufdatum created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (breeder_id) REFERENCES breeder_profiles(id), FOREIGN KEY (breed_id) REFERENCES breeds(id) ); ``` --- ### Tabelle: `puppies` (Einzelne Welpen — optional pro Wurf) ```sql CREATE TABLE puppies ( id INTEGER PRIMARY KEY AUTOINCREMENT, litter_id INTEGER NOT NULL, name VARCHAR(100), -- Optional gender VARCHAR(10), -- 'male', 'female' color VARCHAR(50), chip_number VARCHAR(50), birth_weight FLOAT, -- Gramm status VARCHAR(20) DEFAULT 'available', -- Werte: 'available', 'reserved', 'adopted' show_status BOOLEAN DEFAULT TRUE, -- Züchter entscheidet ob Status sichtbar notes TEXT, -- Interne Notiz des Züchters created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (litter_id) REFERENCES litters(id) ); ``` --- ### Tabelle: `puppy_weights` (Gewichtsverlauf Welpen) ```sql CREATE TABLE puppy_weights ( id INTEGER PRIMARY KEY AUTOINCREMENT, puppy_id INTEGER NOT NULL, weight_g FLOAT NOT NULL, measured_at DATE NOT NULL, FOREIGN KEY (puppy_id) REFERENCES puppies(id) ); ``` --- ### Tabelle: `breeder_inquiries` (Interessenten) ```sql CREATE TABLE breeder_inquiries ( id INTEGER PRIMARY KEY AUTOINCREMENT, litter_id INTEGER NOT NULL, sender_id INTEGER NOT NULL, -- User der anfrägt message TEXT NOT NULL, status VARCHAR(20) DEFAULT 'new', -- Werte: 'new', 'replied', 'reserved', 'closed' created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (litter_id) REFERENCES litters(id), FOREIGN KEY (sender_id) REFERENCES users(id) ); ``` --- ## 3. Registrierungsprozess ### Schritt 1 — Antrag stellen (Frontend) Der User navigiert zu "Mein Profil → Züchter werden". **Formular:** ``` Zwingername * Zuchtverein / Organisation * Rasse * (Dropdown aus breeds-Tabelle) VDH-Mitglied? (Ja / Nein) Stadt / Region * Website (optional) Kurze Vorstellung (Freitext) Dokumente hochladen: [ ] VDH-Mitgliedsausweis (PDF oder Bild) [ ] Zuchtzulassung des Vereins (PDF oder Bild) → Mindestens eines der beiden Dokumente erforderlich [ ] Ich bestätige, dass meine Angaben korrekt sind [ ] Ich habe die Nutzungsbedingungen für Züchter gelesen [Antrag absenden] ``` --- ### Schritt 2 — Backend-Verarbeitung ```python # POST /api/breeder/apply @router.post("/breeder/apply") async def apply_for_breeder( current_user: User = Depends(get_current_user), form_data: BreederApplicationForm, documents: List[UploadFile] ): # Validierung if current_user.breeder_status == 'pending': raise HTTPException(400, "Antrag bereits gestellt") if current_user.role == 'breeder': raise HTTPException(400, "Bereits verifizierter Züchter") if len(documents) == 0: raise HTTPException(400, "Mindestens ein Dokument erforderlich") # Dokumente speichern (nicht öffentlich erreichbar) for doc in documents: save_path = f"/private/breeder_docs/{current_user.id}/{doc.filename}" save_document(doc, save_path) db.add(BreederDocument( user_id=current_user.id, document_type=detect_doc_type(doc.filename), file_path=save_path )) # Profil-Entwurf anlegen db.add(BreederProfile( user_id=current_user.id, kennel_name=form_data.kennel_name, breed_id=form_data.breed_id, ... )) # Status setzen current_user.breeder_status = 'pending' db.commit() # Admin benachrichtigen await notify_admin_new_breeder_application(current_user, form_data) return {"message": "Antrag eingereicht. Du wirst per E-Mail benachrichtigt."} ``` --- ### Schritt 3 — Admin-Benachrichtigung **E-Mail an René:** ``` Betreff: [Banyaro] Neuer Züchter-Antrag — {Zwingername} Neuer Antrag von: {Username} ({E-Mail}) Zwingername: {Zwingername} Rasse: {Rasse} Verein: {Verein} VDH: Ja/Nein Dokumente: {Anzahl} hochgeladen → Im Admin-Bereich prüfen: banyaro.app/admin/breeders ``` --- ### Schritt 4 — Admin-Bereich (Prüfung) **Route:** `/admin/breeders` **Ansicht je Antrag:** - Antragsdaten vollständig anzeigen - Dokumente inline anzeigen (PDF-Viewer oder Bild) - Buttons: **[Freischalten]** | **[Ablehnen]** - Freitextfeld für Ablehnungsgrund (wird per E-Mail gesendet) ```python # POST /api/admin/breeder/{user_id}/approve @router.post("/admin/breeder/{user_id}/approve") async def approve_breeder( user_id: int, admin: User = Depends(require_admin) ): user = db.get(User, user_id) user.role = 'breeder' user.breeder_status = 'approved' profile = db.query(BreederProfile).filter_by(user_id=user_id).first() profile.verified_at = datetime.utcnow() db.commit() await send_breeder_approval_email(user) return {"message": "Züchter freigeschaltet"} # POST /api/admin/breeder/{user_id}/reject @router.post("/admin/breeder/{user_id}/reject") async def reject_breeder( user_id: int, reason: str, admin: User = Depends(require_admin) ): user = db.get(User, user_id) user.breeder_status = 'rejected' db.commit() await send_breeder_rejection_email(user, reason) return {"message": "Antrag abgelehnt"} ``` --- ### Schritt 5 — User-Benachrichtigung **Bei Freischaltung:** ``` Betreff: Willkommen als Züchter bei Banyaro! 🐾 Hallo {Username}, dein Züchter-Profil wurde erfolgreich verifiziert. Ab sofort hast du Zugang zu allen Züchter-Features: ✅ Züchter-Profil auf der Karte ✅ Wurf-Verwaltung ✅ Wurfankündigungen ✅ Interessenten-Nachrichten → Jetzt Profil vervollständigen: banyaro.app/breeder/profile Viel Erfolg mit deiner Zucht! Das Banyaro-Team ``` **Bei Ablehnung:** ``` Betreff: Dein Züchter-Antrag bei Banyaro Hallo {Username}, leider konnten wir deinen Antrag aktuell nicht bestätigen. Grund: {Ablehnungsgrund} Du kannst einen neuen Antrag stellen sobald du die fehlenden Dokumente vorliegen hast. Bei Fragen: mail@motocamp.de ``` --- ## 4. Züchter-Features im Detail ### 4.1 Öffentliches Züchter-Profil **Route:** `/breeder/{kennel_name}` **Anzeige:** - Zwingername + verifiziertes Badge ✓ - Rasse (verlinkt auf Wiki-Eintrag) - Zuchtverein - Stadt / Region - Beschreibung - Website-Link - Aktive Wurfankündigungen - Kontakt-Button → öffnet internes Chat **Auf der Karte:** - Eigener Marker-Typ "Züchter" (unterschiedliches Icon) - Popup mit Zwingername, Rasse, Link zum Profil --- ### 4.2 Wurf-Verwaltung **Route:** `/breeder/litters` **Funktionen:** - Neuen Wurf anlegen (geplant oder bereits geboren) - Einzelne Welpen anlegen (optional) - Gewichtsverlauf pro Welpe erfassen - Status pro Welpe: verfügbar / reserviert / abgegeben - Züchter steuert selbst was öffentlich sichtbar ist - Wurf archivieren (bleibt als Referenz erhalten) --- ### 4.3 Wurfankündigung (öffentlich) **Route:** `/litters` (öffentliche Übersicht für alle User) **Filter:** - Nach Rasse - Nach PLZ / Umkreis - Nach Status (geplant / verfügbar) **Detailseite pro Wurf:** - Elterntiere (Name, Foto optional) - Geburtsdatum / erwarteter Termin - Anzahl Welpen gesamt / verfügbar - Gesundheitsuntersuchungen der Eltern - Preisrahmen (optional) - Fotos - [Nachricht senden]-Button --- ### 4.4 Interessenten-Verwaltung Eingehende Anfragen erscheinen im internen Chat-System. Züchter kann Status der Anfrage setzen: neu / beantwortet / reserviert / abgeschlossen. --- ### 4.5 Läufigkeits-Tracker (Züchter-Erweiterung) Der bestehende Läufigkeits-Tracker wird für Züchter erweitert: - Deckdatum erfassen - Automatische Berechnung des erwarteten Wurftermins (63 Tage Trächtigkeitsdauer) - Erinnerung: "Wurftermin in 7 Tagen" - Direkte Verknüpfung: Läufigkeit → neuen Wurf anlegen --- ## 5. API-Endpunkte Übersicht ``` # Registrierung POST /api/breeder/apply Antrag stellen GET /api/breeder/status Antragsstatus abfragen # Admin GET /api/admin/breeders/pending Offene Anträge POST /api/admin/breeder/{id}/approve Freischalten POST /api/admin/breeder/{id}/reject Ablehnen # Profil GET /api/breeder/{kennel_name} Öffentliches Profil PUT /api/breeder/profile Profil bearbeiten # Würfe GET /api/litters Öffentliche Übersicht GET /api/litters/{id} Wurf-Detail POST /api/breeder/litters Neuen Wurf anlegen PUT /api/breeder/litters/{id} Wurf bearbeiten DELETE /api/breeder/litters/{id} Wurf archivieren # Welpen POST /api/breeder/litters/{id}/puppies Welpe anlegen PUT /api/breeder/puppies/{id} Welpe bearbeiten POST /api/breeder/puppies/{id}/weight Gewicht erfassen # Karte GET /api/map/breeders Züchter für Karte ``` --- ## 6. Berechtigungsprüfung (Middleware) ```python def require_breeder(current_user: User = Depends(get_current_user)): if current_user.role not in ('breeder', 'admin'): raise HTTPException( status_code=403, detail="Diese Funktion ist nur für verifizierte Züchter verfügbar" ) return current_user ``` Verwendung: ```python @router.post("/breeder/litters") async def create_litter( breeder: User = Depends(require_breeder), ... ): ``` --- ## 7. Sicherheit & Datenschutz - Hochgeladene Dokumente werden **außerhalb des öffentlichen Webroot** gespeichert - Nur Admin kann Dokumente abrufen (authentifizierter Endpunkt) - Dokumente werden nach Prüfung nicht dauerhaft benötigt — optional nach 90 Tagen löschen - Standort des Züchters: nur Stadt/Region öffentlich, keine genaue Adresse - Datenschutzerklärung um Züchter-Daten ergänzen --- ## 8. Umsetzungsreihenfolge (empfohlen) | Schritt | Was | Aufwand | |---|---|---| | 1 | DB-Migration (users, breeder_profiles, breeder_documents) | Klein | | 2 | Antrag-Formular Frontend + Dokument-Upload | Mittel | | 3 | Admin-Bereich: Anträge prüfen + freischalten | Mittel | | 4 | E-Mail-Benachrichtigungen (Antrag, Freischaltung, Ablehnung) | Klein | | 5 | Öffentliches Züchter-Profil + Karten-Marker | Mittel | | 6 | Wurf-Verwaltung (CRUD) | Mittel | | 7 | Öffentliche Wurfankündigung + Filtersuche | Mittel | | 8 | Welpen-Verwaltung + Gewichtsverlauf | Klein | | 9 | Läufigkeits-Tracker Erweiterung | Klein | | 10 | Interessenten-Chat-Integration | Klein | **Gesamtaufwand geschätzt:** 3–5 Tage mit Claude Code