Files
workspace/code/app/PROJECT_CONTEXT.md
T
2026-06-22 21:44:33 +08:00

480 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.
# Allapp Project Context
This file is a compact project memory for future Codex chats. Read it first when working in
`D:\workspace\code\app`.
## Workspace
- Root: `D:\workspace\code\app`
- Git repository root: `D:\workspace`
- Backend: `app-go`
- Frontend: `app-rn`
- Local database memo: `db.local.yml`
- Treat config files as sensitive. Some local config files may contain real credentials.
## Product Mental Model
Allapp is shaped more like a mini-program platform than a single linear mobile app.
There is a main shell for the family/personal space, and feature modules behave like small apps
inside it. The frontend owns most product flow and business behavior. The backend is deliberately
thin and generic, acting mostly as auth, file storage, ID generation, and database access.
High-level model:
```text
Allapp main shell
├── Home / Chat / Tools / My
├── Auth and family/space management
└── Sub-apps
├── Finance
│ ├── Home
│ ├── Add record
│ └── Analysis
└── Clothes matching
└── Planned/listed, route not implemented yet
```
Important naming note:
- The product often uses "family" and "space" as the same concept.
- Database tables use `b_family` and `b_family_member`.
- Main tab config currently includes `/space`, but there is no matching page yet.
## Backend: app-go
Tech stack:
- Go
- Fiber v3
- pgx PostgreSQL client
- JWT
- Viper config
- Zap logging
- S3/RustFS file storage
- QQ/WeChat login and binding
- Snowflake-style unique ID generator
Important files:
- Entry: `app-go/cmd/app/main.go`
- Bootstrap: `app-go/internal/bootstrap/app.go`
- Server: `app-go/internal/bootstrap/server.go`
- Routes: `app-go/internal/router/router.go`
- Config type: `app-go/internal/config/config.go`
- Request binding/validation: `app-go/internal/httpx/request.go`
- Response shape: `app-go/internal/httpx/response.go`
- Auth middleware: `app-go/internal/middleware/auth.go`
- Generic DB access: `app-go/pkg/db/*`
Bootstrap flow:
```text
config -> logger -> S3 client -> Postgres pool -> db.Init
-> unique ID -> JWT -> WeChat client -> Fiber server
```
API base path comes from config and is currently:
```text
/app
```
Important endpoints:
```text
POST /app/auth/login/qq
POST /app/auth/login/wechat
POST /app/auth/login/test
POST /app/bind/qq
POST /app/bind/wechat
POST /app/data/loadData
POST /app/data/loadDataBySql
POST /app/data/saveData
POST /app/data/getUniqueId
POST /app/s3/upload
POST /app/s3/delete
```
Backend architecture notes:
- `loadData` accepts a table/view name, selected columns, condition string, order string, and args.
- `loadDataBySql` accepts raw SQL and args.
- `saveData` accepts batches of inserts, updates, and deletes by table name/key field.
- Insert/update helpers add audit fields for most tables via `applyMetaFields`.
- `Token` header is parsed by `middleware.Auth()` and stores `user_id` in context.
- There are no explicit database foreign keys in the inspected schema; relationships are enforced by code and naming conventions.
Security/architecture caution:
- The generic data API is very flexible but risky.
- Table names, columns, conditions, ordering, and raw SQL can come from the frontend.
- If this moves toward production, prioritize server-side table/field whitelists, ownership checks, and removing or restricting raw SQL.
Validation notes:
- `BindAndValidate` validates structs with `go-playground/validator`.
- It skips validation for slices.
- `GetUniqueIdReq.Count` currently uses a `binding` tag, not a `validate` tag, so its min/max rules may not run.
## Frontend: app-rn
Tech stack:
- Expo 55
- React Native 0.83
- React 19
- Expo Router
- Zustand
- Alova with Axios adapter
- HeroUI Native
- Uniwind / Tailwind-style classes
- Lucide React Native icons
- QQ/WeChat native login plugins
Important files:
- Package: `app-rn/package.json`
- App config: `app-rn/app.json`
- Root layout: `app-rn/src/app/_layout.tsx`
- Startup redirect page: `app-rn/src/app/index.tsx`
- Request wrapper: `app-rn/src/request/index.ts`
- API methods: `app-rn/src/request/api.ts`
- User store: `app-rn/src/store/user.ts`
- Family store: `app-rn/src/store/family.ts`
- Finance store: `app-rn/src/store/finance.ts`
- Main shell layout: `app-rn/src/layouts/AppLayout.tsx`
- Module shell layout: `app-rn/src/layouts/ModuleLayout.tsx`
- Shared tabbar: `app-rn/src/components/layout/tabbar.tsx`
- App/module list config: `app-rn/src/configs/pages.ts`
Frontend/backend contract:
- Frontend reads `EXPO_PUBLIC_API_URL`.
- Requests use Alova with Axios adapter.
- Auth token is sent in a custom `Token` header.
- Login APIs are whitelisted in the request wrapper.
- Backend response shape:
```ts
{
code: number;
message: string;
data: unknown;
track_id?: string;
}
```
Response codes:
```text
1000 success
1001 unauthorized/token expired
2000 business error
3000 system error
```
State:
- `user-storage`: persisted user token/id/nickname/avatar.
- `family-storage`: persisted current family id.
- Finance categories are in memory only and loaded by the finance module layout.
Startup flow:
```text
/ index
├── no local token -> /auth/login
├── token exists but user check fails -> /auth/login
├── token exists but no family -> /family/entry
└── token exists and family exists -> /(tabs)/home
```
## Frontend Route Structure
Expo Router tree:
```text
src/app
├── _layout.tsx
├── index.tsx
├── +not-found.tsx
├── auth
│ └── login.tsx
├── (tabs)
│ ├── _layout.tsx
│ ├── home/index.tsx
│ ├── chat/index.tsx
│ ├── my/index.tsx
│ └── star/index.tsx
├── tools
│ └── index.tsx
├── family
│ ├── entry.tsx
│ ├── settings.tsx
│ ├── user.tsx
│ └── member/[id].tsx
└── finance
├── (tabs)
│ ├── _layout.tsx
│ ├── home/index.tsx
│ └── analysis/index.tsx
├── add/index.tsx
└── sub/index.tsx
```
Effective routes:
```text
/ startup redirect
/auth/login login
/home main home
/chat chat/image picker demo
/my profile tab
/star placeholder page, not currently in tab config
/tools app/module list
/family/entry create or join family
/family/settings family settings
/family/user user profile settings
/family/member/:id family member detail
/finance/home finance module home
/finance/add add finance record
/finance/analysis finance analysis
/finance/sub test page
```
Main tab config in `src/app/(tabs)/_layout.tsx`:
```text
/home 首页
/space 空间, but route currently missing
/tools center app-list button
/chat 聊天
/my 我的
```
Finance module tab config in `src/app/finance/(tabs)/_layout.tsx`:
```text
/finance/home 记账
/finance/add center add button
/finance/analysis 分析
```
Layout behavior:
- Root `_layout.tsx` renders a global Stack with no headers.
- `(tabs)/_layout.tsx` uses `AppLayout`, which renders `Slot`, top `Navbar`, and bottom `Tabbar`.
- `finance/(tabs)/_layout.tsx` uses `ModuleLayout`, a module-level shell with its own top bar and tabbar.
- `ModuleLayout` close action returns to `/(tabs)/home`.
Current route mismatches:
- Main tab points to `/space`, but no `/space` file exists.
- `src/app/(tabs)/star/index.tsx` exists but is not in the tab config.
- `/tools` is outside `(tabs)`, so it is not wrapped by `AppLayout`; decide whether this is intentional.
- `/finance/add` is outside `finance/(tabs)`, but the finance tabbar points to it. This can be intentional for a modal/subpage style add screen.
## Feature Status
Implemented or mostly implemented:
- QQ login
- WeChat login
- Auth persistence
- Auth-expired redirect
- Family creation
- Family joining by invite code
- Family settings
- User profile
- Avatar upload to S3/RustFS
- QQ/WeChat account binding
- App/module list page
Partially implemented:
- Finance module shell
- Finance category loading
- Finance add-record form UI
- Finance analysis placeholder/demo page
Not yet complete:
- Saving a finance record from `/finance/add`
- Finance record list on `/finance/home`
- Finance analysis real data
- Clothes matching app route
- `/space` main-tab page
## Database: allapp
Observed database:
- PostgreSQL version observed previously: 18.1
- Database: `allapp`
- Schema: `public`
- Explicit foreign keys: none observed
Tables:
```text
b_user
b_user_oauth
b_family
b_family_member
b_finance
b_finance_category
b_finance_category_default
b_app_favorite
```
Views:
```text
v_family_member
```
Main entities:
- `b_user`: user profile, nickname, avatar, last login time, name, phone, birth date.
- `b_user_oauth`: third-party login bindings; includes `user_id`, `type`, `openid`.
- `b_family`: family/space; includes `name`, `invite_code`, `owner_id`.
- `b_family_member`: family membership; includes `family_id`, `user_id`, `role`, `nickname`.
- `b_finance`: finance records; includes `category_id`, `family_id`, `amount`, `remark`, `record_time`.
- `b_finance_category_default`: default finance category template.
- `b_finance_category`: actual finance categories for a family.
- `v_family_member`: joins `b_family_member` with `b_user`.
Finance conventions:
```text
type = 0 expense
type = 1 income
```
Category icon names match frontend iconfont names, for example:
```text
finance_jiaotong
finance_canyin
finance_shicai
finance_gouwu
finance_gongzi
finance_jianzhi
finance_shouru
```
## Local Environment Notes
The Codex shell runs inside WSL2 Ubuntu.
WSL has `fnm` installed at:
```text
/home/oneao/.local/share/fnm/fnm
```
Known WSL Node setup:
```text
fnm 1.39.0
v22.22.3
v24.16.0 default
```
In non-interactive Codex commands, `~/.bashrc` may not initialize fnm automatically. If `node` is not
found, temporarily enable it with:
```bash
export PATH="$HOME/.local/share/fnm:$PATH"
eval "$(fnm env --shell bash)"
```
After fnm init, expected tools:
```text
node v24.16.0
pnpm 11.5.2
```
Windows-side `nvm` paths may appear in PATH:
```text
/mnt/d/devtool/nvm
/mnt/d/devtool/nodejs
```
Those are not the right runtime for WSL. Prefer WSL `fnm`.
## Known Type/Build Issues
Frontend:
- `pnpm exec tsc --noEmit` currently fails.
- `tsconfig.json` includes `**/*.ts` and `**/*.tsx`, pulling in `example`.
- `tsconfig.json` has a suspicious extra include entry: `src/app/auth/login_2`.
- Generated iconfont components have `react-native-svg` prop type incompatibilities.
- `src/app/finance/(tabs)/analysis/index.tsx` has `Surface variant="2"` type mismatch.
Backend:
- `go` was not available in the earlier command environment, so `go test ./...` could not be rerun there.
- Prior context noted a possible unique ID package compile issue around `fmt.Println(message)` vs `fmt.Println(message...)`.
## Important Current Risks
- Generic backend data APIs need server-side permissions before production use.
- Finance category loading currently queries all `b_finance_category` rows without filtering by current `family_id`.
- Frontend business logic often trusts local/user-provided family and user IDs.
- Main tab route `/space` is missing.
- Some pages are placeholders or demos.
## Development Conventions
These rules MUST be followed. They exist to keep the codebase consistent and avoid unnecessary
duplication.
### API — No New Dedicated Endpoints
The project already has a set of generic data APIs. Do NOT create dedicated REST endpoints for
CRUD operations. Always use the existing generic APIs instead:
| Generic API | Usage |
|---|---|
| `POST /data/loadData` | Query with `view_name` (table/view), `search_condition`, `order_by`, `search_columns`, `args` |
| `POST /data/loadDataBySql` | Raw SQL query with `sql` and `args` |
| `POST /data/saveData` | Batch insert/update/delete by `table_name` and `key_field` |
| `POST /data/getUniqueId` | Get snowflake IDs, `count` param (1–100) |
If you genuinely believe a new endpoint is needed, **ask the user first**. Even then, new endpoints
should be generic (e.g. a new generic helper), not dedicated to a single feature.
### Database — Standard Audit Columns
All business tables MUST include these four standard columns:
```sql
create_time TIMESTAMPTZ -- auto-set by applyMetaFields on insert
update_time TIMESTAMPTZ -- auto-set by applyMetaFields on insert & update
create_by BIGINT -- auto-set by applyMetaFields on insert (from JWT user_id)
update_by BIGINT -- auto-set by applyMetaFields on update (from JWT user_id)
```
The `applyMetaFields` function in `pkg/db/meta_field.go` injects these automatically for all tables
except those in the `auditExcludeTables` list (`b_user`, `b_user_oauth`).
When creating a new table:
1. Include all four columns
2. Do NOT add the table to `auditExcludeTables` unless it is a system-level table (user/auth)
3. Primary key `id` should be a snowflake BIGINT, obtained via `getUniqueIdApi` on the frontend or
`uniqueid.NextId()` on the backend
### Frontend State — Reuse Existing Stores
Before creating a new Zustand store or API wrapper, check whether the data can be fetched via the
existing `loadDataApi` / `saveDataApi` from the `@/request/api` module. New API wrapper functions
should only wrap these generic endpoints — never introduce feature-specific RPC-style endpoints.