3.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
CityWalk 图章收集系统 — 移动端 H5 + 管理后台。游客在城市点位扫描二维码收集图章,集满可兑换奖品,兑换后图章清空,支持重复收集。
Commands
# Development (需要同时运行两个服务)
pnpm dev:server # Express API on :3000
pnpm dev:web # Vite dev server on :5173
# Database
pnpm db:generate # Generate Prisma client after schema changes
pnpm db:migrate # Create and apply migrations (prisma migrate dev)
pnpm db:push # Push schema directly (dev only, no migration file)
pnpm db:seed # Seed sample data (9 stamps + 4 redemption rules)
# Build
pnpm build # Build all packages
Architecture
pnpm monorepo with 3 packages sharing tsconfig.base.json (target ES2022, moduleResolution bundler):
packages/shared— Prisma client singleton + shared TypeScript types (ApiResponse<T>,StampWithStatus, etc.)packages/server(port 3000) — Express 5 + Zod validation + JWT auth. Routes: auth, stamps, collection, redemption, admin. File uploads via multer topackages/server/uploads/.packages/web(port 5173) — React 19 + Vite 8 + Tailwind CSS 4. Dual interface: mobile H5 (/, /album) + PC admin (/admin/*). Vite proxies/apiand/uploadsto server.
API Response Format
All endpoints return: { success: boolean, data?: T, error?: { code: string, message: string } }
Authentication
- Users: JWT (7-day expiry, single token in localStorage
stamp_token). Middleware:requireAuth,optionalAuth. - Admin: API key via
X-Admin-Keyheader (matched againstADMIN_API_KEYenv var). Middleware:requireAdmin.
Frontend Routing
/ → LandingPage (also handles /?stamp={id} for collection popup)
/album → AlbumPage (stamp grid + redemption)
/collect/:stampId → Redirects to /?stamp={stampId} (QR code entry point)
/admin → AdminLogin
/admin/stamps → Stamp CRUD + QR code generation
/admin/rules → Redemption rule CRUD
/admin/redemptions → Redemption history + stats
Collection Flow
QR codes encode /collect/{stampId} → redirects to /?stamp={stampId} → LandingPage shows collection overlay. All interactions (register, collect, close) happen as modals on top of the landing page. The stamp ID is stored in sessionStorage during registration to resume collection after auth.
Redemption Transaction
Atomic: prisma.$transaction creates Redemption record + deletes all user Collections. The @@unique([userId, stampId]) constraint resets after deletion, allowing re-collection.
Critical: Tailwind CSS v4 Layer Architecture
Custom CSS in packages/web/src/index.css must use @layer to avoid overriding Tailwind utilities:
@import "tailwindcss";
@layer base { /* Reset, CSS variables, body/heading fonts */}
@keyframes ... { } /* Keyframes stay OUTSIDE layers */
@layer components { /* .animate-*, .stagger-children, .paper-texture, etc. */}
Why: In Tailwind CSS v4, unlayered styles always beat @layer utilities. If base resets like * { padding: 0 } are unlayered, they override Tailwind's px-4, py-3, etc., breaking all utility spacing across the app.
Environment
DATABASE_URL="file:./dev.db" # SQLite (relative to project root)
JWT_SECRET="..." # JWT signing key
ADMIN_API_KEY="..." # Admin panel access key
SERVER_PORT=3000
SITE_URL="http://localhost:5173" # Used in QR code URL generation