13 KiB
13 KiB
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)
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
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
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)
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)
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)
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)
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
# 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)
# 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)
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:
@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