3e39cf0170
Switch from direct WeChat Pay / Alipay integrations to the unified
宇之然 pay-api gateway (HMAC-SHA256 auth). Removes wechat_pay.py,
keeps PaymentGateway abstraction, adds UnifiedPayService. Simplifies
payment.py create_order to {plan, pay_type} params. Single webhook
endpoint replaces separate WeChat/Alipay notify handlers.
100 lines
5.7 KiB
Markdown
100 lines
5.7 KiB
Markdown
# TradeMate (外贸小助手) — Agent Guide
|
|
|
|
## Architecture
|
|
|
|
- **Backend**: `backend/` — FastAPI + SQLAlchemy 1.4 async + asyncpg, single `app.main:app`
|
|
- **Frontends**: `uni-app/` (mobile H5/mini-program), `admin-frontend/` (PC admin), `user-frontend/` (PC workspace)
|
|
- **Config**: `backend/app/config.py` reads from `/.env` (project root) via pydantic BaseSettings
|
|
- **Auth**: JWT (python-jose). Default dep `get_current_user_id` in `backend/app/api/v1/deps.py`
|
|
- **AI Router**: `backend/app/ai/router.py` — singleton `AIRouter`, DB-driven providers. Primary = sensenova, fallbacks = alibaba-mt / opencode_go / nvidia / spark
|
|
- **Database**: PostgreSQL via `asyncpg`, pool_size=20
|
|
|
|
## AI Providers
|
|
|
|
- **Active**: Sensenova (商汤), OpencodeGo, NVIDIA, 讯飞 Spark, 阿里机器翻译 (alibaba-mt)
|
|
- **Removed (dead code)**: Claude (`claude.py`), DeepL (`deepl.py`), Local (`local.py`) — git rm'd, not yet committed
|
|
- **DB-driven**: `AIProvider` model + `admin_ai.py` API — manage providers at runtime. `router.seed_from_env()` loads from `.env` on startup
|
|
- **Provider type mapping** in `router.py._build_provider()`: sensenova, opencode_go, nvidia, spark, alibaba-mt
|
|
|
|
## Security
|
|
|
|
- **CORS**: `middleware.py` — whitelist origins, restricted methods/headers
|
|
- **Rate Limit**: endpoint-specific — login 5/min, register 3/h, password 3/5min, payment 20/min, admin 30/min
|
|
- **CSRF**: `core/csrf.py` — double-submit cookie pattern. Required on auth/payment/profile. Webhooks skipped.
|
|
- **Login**: JSON `LoginRequest` model, not `OAuth2PasswordRequestForm`
|
|
|
|
## Customer Discovery
|
|
|
|
- `discovery.py` + `discovery_record.py` — Google Custom Search integration
|
|
- Contact extraction from company websites (email/phone/WhatsApp/WeChat)
|
|
|
|
## Dev Commands
|
|
|
|
```bash
|
|
# Backend (from project root — .env is there)
|
|
cd backend && source venv/bin/activate && uvicorn app.main:app --reload --port 8000
|
|
|
|
# Mobile H5
|
|
cd uni-app && npm run dev:h5
|
|
|
|
# Admin frontend (PC management)
|
|
cd admin-frontend && npm run dev # port 5173, base: /admin/
|
|
|
|
# User workspace (PC workbench)
|
|
cd user-frontend && npm run dev # port 5174, base: /workspace/
|
|
|
|
# Tests (backend — needs PostgreSQL running with foreign_trade_test DB)
|
|
cd backend && venv/bin/pytest # all
|
|
venv/bin/pytest tests/test_auth_api.py # single file
|
|
venv/bin/pytest tests/ -k "test_login" # keyword filter
|
|
|
|
# Builds
|
|
cd uni-app && npm run build:h5 # uni-app (mobile H5)
|
|
cd admin-frontend && npm run build # admin => /www/wwwroot/trade.yuzhiran.com/admin/
|
|
cd user-frontend && npm run build # workspace => /www/wwwroot/trade.yuzhiran.com/workspace/
|
|
|
|
# Alembic migrations
|
|
cd backend && alembic upgrade head
|
|
alembic revision --autogenerate -m "desc"
|
|
```
|
|
|
|
## Deployment
|
|
|
|
- **Landing page**: `trade.yuzhiran.com/` — static marketing HTML
|
|
- **SPA**: `trade.yuzhiran.com/app/` — uni-app build (mobile)
|
|
- **Admin**: `trade.yuzhiran.com/admin/` — Vue 3 + Element Plus (standalone)
|
|
- **Workspace**: `trade.yuzhiran.com/workspace/` — Vue 3 + Element Plus (standalone)
|
|
- **Nginx**: SPA fallbacks for `/app/`, `/admin/`, `/workspace/`
|
|
- **vite config**: each project has its own `base` path and dev port
|
|
- **API**: proxied via nginx `location /api/` to `127.0.0.1:8002`
|
|
|
|
## Critical Quirks
|
|
|
|
- **Route ordering**: FastAPI matches top-down. Specific routes (`/{customer_id}/health`) must be registered **before** wildcard `/{customer_id}`.
|
|
- **AI `extract_info`**: Some models (e.g. deepseek-v4-flash) don't support `response_format={"type": "json_object"}`. `openai.py` catches the failure and retries without it.
|
|
- **Manual auth on some endpoints**: `keywords` and `competitor-analysis` endpoints use `authorization: str = Header(None)` instead of `Depends(get_current_user_id)`.
|
|
- **MarketingService fallback**: When no AI providers initialized, returns template content instead of crashing.
|
|
- **Onboarding service**: calls `mkt.generate(product_info={"name": ..., ...})`, not keyword args. Check `onboarding.py` for the exact dict shape.
|
|
- **CustomerHealthService**: `get_health_overview` endpoint must use `CustomerHealthService(db)` not `CustomerService(db)`.
|
|
- **CSRF**: Sensitive endpoints (auth/payment/profile) require `X-CSRF-Token` header. Token available via `csrf_token` cookie / `X-CSRF-Token` response header.
|
|
- **AI Router reload**: After modifying AI providers via admin API, call `POST /api/v1/admin/ai/reload` to refresh in-memory providers.
|
|
- **Payment**: Uses unified `pay-api` gateway (`UnifiedPayService`). NOT direct WeChat/Alipay integration. Credentials: `PAY_API_KEY`/`PAY_API_SECRET` from `.env`. HMAC-SHA256 auth. Webhook at `POST /api/v1/payment/webhook` skips CSRF.
|
|
- **Payment gateway config**: `PAY_API_KEY`, `PAY_API_SECRET`, `PAY_API_BASE_URL`, `PAY_WEBHOOK_URL` in `config.py`. `pay_type` param: `"alipay"` (returns `pay_url`) or `"wechat"` (returns `code_url`). `UnifiedPayService` normalizes legacy `native`/`jsapi`/`pc` to `wechat`/`alipay`.
|
|
|
|
## Project Conventions
|
|
|
|
- **No README.md** — key context is in `PROGRESS.md` and `docs/`
|
|
- **Chinese UI** — mobile-first, for foreign-trade SOHOs/small teams
|
|
- **No comments in code** unless explicitly asked
|
|
- **Commit messages** focus on "why" not "what", in English
|
|
- **Services** instantiate `MarketingService()` (no db needed). For customer health: `CustomerHealthService(db)`
|
|
- **AI providers** in `backend/app/ai/providers/` — inherit from `OpenAIProvider` if compatible with OpenAI API format
|
|
- **Static assets** go in `uni-app/src/static/`
|
|
- **Test DB**: `foreign_trade_test` (uses credentials from `conftest.py`, not `.env`)
|
|
|
|
## Remember
|
|
|
|
- Write tests for new features
|
|
- Run `cd backend && venv/bin/pytest` before committing
|
|
- Keep context compact — avoid bloating the session with unnecessary file reads
|