Files
workspace/code/app/CLAUDE.md
T
2026-06-26 17:35:29 +08:00

7.8 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Allapp is a family/space mini-program platform — a main shell with sub-apps inside it. The frontend owns most business logic; the backend is deliberately thin and generic (auth, file storage, ID generation, database access).

  • Backend: app-go/ — Go 1.25, Fiber v3.1, pgx (PostgreSQL), JWT, Viper, Zap
  • Frontend: app-rn/ — React Native 0.85, Expo 56, Expo Router, Zustand, Alova, HeroUI Native, Uniwind (Tailwind CSS)
  • Database: PostgreSQL 18.1, database allapp, schema public

Read PROJECT_CONTEXT.md for deeper context; this file focuses on what you need day-to-day.

Commands

Backend (app-go/)

cd app-go
go build ./cmd/app          # Build
go run ./cmd/app            # Run server
go test ./...               # Run all tests
go mod tidy                 # Tidy dependencies

Frontend (app-rn/)

cd app-rn
pnpm start                  # Expo dev server (port 8083)
pnpm android                # Android build
pnpm ios                    # iOS build
pnpm lint                   # oxlint
pnpm exec tsc --noEmit      # TypeScript type check
pnpm exec oxfmt             # Format code

WSL-specific: if node/pnpm are not found, run:

export PATH="$HOME/.local/share/fnm:$PATH"
eval "$(fnm env --shell bash)"

Architecture

Bootstrap Flow

Backend: config → logger → S3 → Postgres → db.Init → unique ID → JWT → WeChat → Fiber server
Frontend: /index → token check → auth/login or family/entry or /home

Backend Design

All CRUD goes through 5 generic data APIs. Never create dedicated REST endpoints for feature-specific operations:

Endpoint Purpose
POST /app/data/loadData Query by table/view name, conditions, columns, order, args
POST /app/data/loadDataPage Paginated query — same as loadData + page/page_size, returns {rows, total, page, page_size}
POST /app/data/loadDataBySql Raw SQL query
POST /app/data/saveData Batch insert/update/delete by table name and key field
POST /app/data/getUniqueId Get snowflake IDs (1–100)

Auth endpoints: /app/auth/login/{qq,wechat,test}, /app/bind/{qq,wechat}.

File endpoints:

Endpoint Purpose
POST /app/s3/upload Upload files (multipart form, field name files), stored under YYYY/MM/DD/{uuid}.ext, returns string[] keys
POST /app/s3/delete Delete files by keys, body {keys: string[]}, failure is logged but still returns success

On the frontend: s3UploadFileApi(files[]) wraps files into a FormData with key "files" and multipart/form-data content type.

Auth token is passed in a custom Token header. JWT middleware stores user_id in context. Audit fields (create_time, update_time, create_by, update_by) are auto-injected by pkg/db/meta_field.go for all tables except b_user and b_user_oauth.

Frontend Design

Expo Router file-based routing under src/app/:

  • (main)/(tabs)/ — main shell tabs (home, chat, my)
  • (main)/auth/, (main)/family/, (main)/tools/, (main)/activities/
  • finance/(tabs)/ — finance sub-app (independent from main)
  • Layouts: AppLayout for main shell, ModuleLayout for sub-app shells, PageLayout for simple pages

State: Zustand with persist middleware for user and family (AsyncStorage). Finance store is in-memory only.

API layer: single Alova instance (src/request/index.ts), API wrappers in src/request/api.ts. Key wrappers: loadDataApi, loadDataPageApi (paginated, returns PageData<T> with rows/total/page/page_size), loadDataBySqlApi, saveDataApi, getUniqueIdApi. Response codes: 1000 (success), 1001 (unauthorized → redirect to login), 2000 (business error), 3000 (system error).

Startup flow in src/app/index.tsx:

no token → /auth/login
token invalid → /auth/login
no family → /family/entry
has family → /home

Critical Conventions

1. No New Dedicated API Endpoints

Always reuse the 5 generic data APIs. If you think a new endpoint is needed, ask the user first. Even then, new endpoints must be generic helpers, not feature-specific.

2. Database Tables Must Include Audit Columns

All new business tables must include: id BIGINT PRIMARY KEY (snowflake), create_time TIMESTAMPTZ, update_time TIMESTAMPTZ, create_by BIGINT, update_by BIGINT. Do not add new tables to auditExcludeTables — only b_user and b_user_oauth are exempt.

3. Family/Space-Centric Data Model

All business data (finance, favorites, etc.) associates with family_id. Users link to families via b_family_member. Always filter queries by the current user's family_id.

4. Frontend Family-Scoped Data Access

On the frontend, keep using the generic data APIs in app-rn/src/request/api.ts. Pages are responsible for passing the right family/user scope in search_condition, args, and inserted rows. Use useFamilyStore((s) => s.getFamilyId()) for the current family id and useUserStore((s) => s.getUserId()) for the current user id.

5. Family Feed Recording

Certain actions must call recordFeed() from @/helpers/feed. Use {nickname} placeholder in content — do NOT hardcode nicknames. The loadFeeds() helper replaces it dynamically. Scenarios: creating/joining family (join), renaming (rename), recording bills (bill), favoriting apps (fav).

6. Frontend: Use Existing Libraries Only

Before writing any page/component, check app-rn/package.json for available dependencies. Use HeroUI Native for UI, @legendapp/list for long lists, lucide-react-native for icons, sonner-native for toasts. Use NativeWind className for styling, not inline style={}.

7. Loading State: Always Use useLoading + LoadingOverlay

Any async save/update/delete operation must use the useLoading hook + <LoadingOverlay> pattern — never roll per-field loading booleans. The hook provides startLoading(message) (shows toast.loading + page mask), stopLoading(options?) (dismisses toast, optionally shows success/error), and isLoading (boolean for disabling buttons). Place <LoadingOverlay visible={isLoading} /> near the bottom of the page JSX (before closing </PageLayout>). Reference: src/hooks/useLoading.ts, src/components/LoadingOverlay.tsx.

8. Finance Convention

type = 0 means expense, type = 1 means income. Category icon names match iconfont names (e.g., finance_jiaotong, finance_canyin).

Key Files

File Role
PROJECT_CONTEXT.md Full project documentation, risks, feature status
app-go/internal/bootstrap/app.go Backend initialization flow
app-go/internal/router/router.go Route definitions
app-go/pkg/db/meta_field.go Audit field auto-injection
app-rn/src/app/index.tsx Startup/auth routing logic
app-rn/src/request/index.ts Alova instance, auth interceptor
app-rn/src/request/api.ts API wrapper functions
app-rn/src/store/ Zustand stores (user, family, finance, app, favorite)
app-rn/src/layouts/ Shell layouts (AppLayout, ModuleLayout, PageLayout)
app-rn/src/configs/pages.ts Sub-app definitions
app-rn/src/hooks/useLoading.ts Loading state hook (toast + overlay)
app-rn/src/components/LoadingOverlay.tsx Full-page loading mask component
app-rn/src/helpers/feed.ts Family feed recording

Known Risks

  • Generic data APIs accept table names and raw SQL from the frontend — needs server-side whitelisting before production.
  • db.local.yml contains database credentials and should never be committed.
  • Finance category queries currently don't filter by family_id.
  • Main tab /space route is missing its page file.
  • GetUniqueIdReq.Count uses binding tag instead of validate tag — min/max rules may not run.