This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
当当日记 (DangDang Diary) — a pet diary mobile app. Phase 1 is an MVP; Phase 2 adds AI and social features.
- Frontend: Flutter 3.x (Dart), state management via Riverpod
- Backend: Python 3.11+ FastAPI
- Infra: PostgreSQL 16, Redis 7, MinIO (S3-compatible), Nginx reverse proxy, Docker Compose
The repo is currently in the planning/documentation phase. Implementation follows the step-by-step guides in docs/.
Each step doc (docs/step1-*.md through docs/step8-*.md) is the authoritative spec for that step. Always read the relevant step doc before implementing. General order:
- Read step doc, propose a plan
- Implement backend API first (test with FastAPI Swagger UI at
/docs) - Implement Flutter frontend
- Front-end/backend integration test on real device
- Git commit
The backend (FastAPI) runs inside docker compose (service fastapi), with the
source bind-mounted and --reload on — editing backend code hot-reloads, no rebuild.
See docs/deploy-ops.md for the full ops runbook.
# Start the whole stack (FastAPI + PG + Redis + MinIO + Nginx)
docker compose up -d
# Backend: run migrations (must run INSIDE the container — .env uses
# compose service names like `postgres`, unresolvable from the host)
docker compose exec fastapi alembic upgrade head
# Backend: restart after editing .env / rebuild after editing requirements.txt
docker compose restart fastapi
docker compose build fastapi && docker compose up -d fastapi
# Backend: logs
docker compose logs -f fastapi
# Frontend: run on device
cd frontend && flutter run
# Frontend: build release APK (inject the real domain at build time)
cd frontend && flutter build apk --release --dart-define=BASE_URL=https://dangdangdiary.orgPhone → Nginx (:80/:443)
├── /api/... → FastAPI (:8000)
└── /media/... → MinIO (:9000)
The phone client only talks to Nginx. Internal service addresses (minio:9000, fastapi:8000) must never be returned in API responses.
main.py— FastAPI entry point, registers routers and exception handlersconfig.py— all config from.envdatabase.py— SQLAlchemy sessionmodels/— SQLAlchemy ORM modelsschemas/— Pydantic request/response modelsapi/— route handlers (auth.py,pets.py,photos.py,health.py)services/— business logicutils/— helpers
main.dart— entry, route setup, themeconfig/— constants, theme, base URL configmodels/— data modelsservices/— API callsproviders/— Riverpod providersscreens/— pages:auth/,record/,health/,timeline/,profile/widgets/— reusable components
- Fields:
snake_case - Pagination:
page+page_size; list response uses semantic keys (pets,photos,weights) - List order: newest first
- Create/update → return the full updated object
- Delete →
204 No Content - Empty list →
200+ empty array - Timestamps stored as UTC; date-only fields use
datetype
{ "code": "...", "message": "...", "details": {...} }- Invalid input →
400 - Permission denied →
403 - Register a FastAPI
RequestValidationErrorhandler to convert Pydantic errors to this format.
- SMS via Aliyun Dypnsapi
SendSmsVerifyCode; code stored in Redis (5 min TTL, 60 s resend cooldown) - JWT: Access Token (2 h) + Refresh Token (30 d)
POST /api/v1/auth/logoutinvalidates the current device's refresh token only- First login auto-creates the user account
- Frontend converts HEIC/HEIF → JPEG before upload
- Backend accepts JPG / PNG / WEBP
- On upload, backend calls Aliyun
RecognizeSceneto verify the image contains a cat or dog; reject with a user-friendly message if not - Store original + thumbnail in MinIO; return thumbnail URL in list responses, original URL on detail view
- EXIF date (
DateTimeOriginal) is extracted on the Flutter side using theexifpackage
- Backend reads all config from
.env;.env.examplecontains only placeholders - Frontend uses a single config entry point for
base_url - Never hardcode real keys, passwords, or server addresses in code or docs
| File | Purpose |
|---|---|
docs/00-global-rules.md |
Cross-step conventions (canonical) |
docs/DangDangDiary-technical-plan.plan.md |
Full architecture, DB schema, API overview |
docs/step0-env-setup-manual.md |
Manual environment setup reference |
docs/step1-environment-setup.md |
Docker Compose + project skeletons |
docs/deploy-ops.md |
Deploy/ops runbook: start-stop, restart, migrations, domain switch, the MINIO_ENDPOINT↔nginx Host signing gotcha |
docs/step2-auth-module.md |
SMS auth + JWT |
docs/step3-pet-profile.md |
Pet CRUD + profile UI |
docs/step4-photo-record.md |
Photo upload + validation + EXIF |
docs/step5-health-management.md |
Weight / routine / deworming / vaccination |
docs/step6-timeline.md |
Paginated timeline |
docs/step7-push-notification.md |
Local push notifications |
docs/step8-integration-polish.md |
Source hardening & automated testing (UI polish deferred to end of Phase 2) |
docs/phase2-step1-pet-share.md |
Phase 2: pet profile sharing (OWNER / EDITOR / VIEWER roles, share codes) |
docs/phase2-step2-voice-intake.md |
Phase 2: long-press voice → STT + Tongyi LLM intent extract → record drafts |
docs/phase2-step3-photo-auto-assign.md |
Phase 2: auto-classify uploaded photos to a pet via DashScope multimodal embedding + pgvector |
docs/phase2-step4-logo-splash.md |
Phase 2: brand Logo assets, Splash screen animation, AppBar / loading brand reuse |
docs/future-async-task-queue.md |
Future optimization: offload _backfill_embedding (and other slow work) to an async task queue (arq/Celery). Not scheduled. |
docs/future-voice-frontend-streaming.md |
Future optimization: Flutter streams PCM directly over WebSocket so STT runs while the user is still talking. Designed but not scheduled. |
docs/API_docs/ |
Third-party API reference (Aliyun SMS, scene recognition) |
If a step doc conflicts with 00-global-rules.md, the step doc takes precedence only when it is more specific and does not contradict a global rule.