86 lines
3.6 KiB
Markdown
86 lines
3.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
CityWalk 图章收集系统 — 移动端 H5 + 管理后台。游客在城市点位扫描二维码收集图章,集满可兑换奖品,兑换后图章清空,支持重复收集。
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
# 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 to `packages/server/uploads/`.
|
|
- **`packages/web`** (port 5173) — React 19 + Vite 8 + Tailwind CSS 4. Dual interface: mobile H5 (/, /album) + PC admin (/admin/*). Vite proxies `/api` and `/uploads` to 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-Key` header (matched against `ADMIN_API_KEY` env 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:
|
|
|
|
```css
|
|
@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
|
|
```
|