banyaro/promotion/banyaro_zuechterolle_ausarbeitung.md
rene 4ac1c27b75 Dateien nach „promotion“ hochladen
Vorbereitung für Züchterfeatures
2026-04-28 08:16:26 +02:00

489 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:** 35 Tage mit Claude Code