diff --git a/promotion/banyaro_zuechterolle_ausarbeitung.md b/promotion/banyaro_zuechterolle_ausarbeitung.md new file mode 100644 index 0000000..bb6b76a --- /dev/null +++ b/promotion/banyaro_zuechterolle_ausarbeitung.md @@ -0,0 +1,489 @@ +# 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