补充 Svelte 5 runes、SvelteKit 代理架构、Go LocalTime 类型、 软删除约束、Adapter 模式、账单去重策略等细节, 扩充重要文件列表,完善测试命令说明。
11 KiB
AGENTS.md - AI Coding Agent Guidelines
Guidelines for AI coding agents working on BillAI - a microservices bill analysis system.
Architecture
web/- SvelteKit 5 + TailwindCSS 4 + TypeScript (Frontend Proxy & UI, port 3000)server/- Go 1.21 + Gin + MongoDB (Main API & Data Storage, port 8080)analyzer/- Python 3.12 + FastAPI (Data Cleaning & Analysis Service, port 8001)
The SvelteKit frontend acts as a proxy: all /api/* browser requests are forwarded by
web/src/routes/api/[...path]/+server.ts to the Go backend. The browser never contacts Go
directly. API_URL env var controls the target (http://server:8080 in Docker,
http://localhost:8080 in local dev).
Build/Lint/Test Commands
Frontend (web/)
Working Directory: /Users/clz/Projects/BillAI/web
npm run dev # Start Vite dev server
npm run build # Production build (adapter-node)
npm run preview # Preview production build
npm run check # TypeScript check (svelte-check)
npm run lint # Prettier --check + ESLint
npm run format # Format with Prettier
npm run test # Run all unit tests once (CI mode)
npm run test:unit # Run unit tests in watch mode
npx vitest run src/routes/+page.spec.ts # Run single test file
npx vitest run -t "test name pattern" # Run tests by name pattern
Backend (server/)
Working Directory: /Users/clz/Projects/BillAI/server
go run . # Start server
go build -o server . # Build binary
go mod tidy # Clean dependencies
go test ./... # Run all tests
go test ./handler/... # Run handler package tests
go test -run TestName ./... # Run single test function
go test -v ./handler/... # Verbose test output
Analyzer (analyzer/)
Working Directory: /Users/clz/Projects/BillAI/analyzer
python server.py # Start FastAPI server directly
uvicorn server:app --reload # Start with hot reload
pytest # Run all tests
pytest test_jd_cleaner.py # Run single test file
pytest -k "test_name" # Run test by name pattern
pip install -r requirements.txt # Install dependencies
Docker
Working Directory: /Users/clz/Projects/BillAI
docker-compose up -d --build # Start/rebuild all services
docker-compose logs -f server # Follow service logs
docker-compose down # Stop all services
Code Style
General
- Comments: Existing comments often use Chinese for business logic explanations. Maintain this style where appropriate; English is also acceptable for technical explanations.
- Conventions: Follow existing patterns strictly. Do not introduce new frameworks or libraries
without checking
package.json/go.mod/requirements.txt.
TypeScript/Svelte (web/)
- Formatting: Prettier — tabs, single quotes, no trailing commas, printWidth 100,
prettier-plugin-svelte. - Naming:
PascalCasefor types/interfaces/components,camelCasefor variables/functions. - Imports: Use
$libalias for internal imports and$app/*for SvelteKit builtins. Never use relative paths for lib-level modules.import { browser } from '$app/environment' import { goto } from '$app/navigation' import { auth } from '$lib/stores/auth' import type { UIBill } from '$lib/models/bill' import Upload from '@lucide/svelte/icons/upload' - Svelte 5 runes: Use the new runes API —
$state,$derived,$effect,$props. Event handlers useonclick={fn}syntax (not legacyon:click). - Types: Define
export interfacefor all data models. Frontend models usecamelCasefields (UIBill); API responses usesnake_case(CleanedBill). Provide explicit converter functions (e.g.,cleanedBillToUIBill,uiBillToUpdateBillRequest) inweb/src/lib/models/bill.ts. - Error Handling: Check
response.ok; thrownew Error(\HTTP ${response.status}`)for the UI to catch. On 401, callauth.logout()and redirect to/login`. - Auth pattern:
createAuthStore()factory in$lib/stores/auth.ts. Token stored inlocalStorageunder keyauth. All API calls go throughapiFetch()in$lib/api.ts, which injectsAuthorization: Bearer <token>and handles 401 centrally. - Testing: Vitest +
vitest-browser-svelte+ Playwright. Test files co-located with routes as*.spec.ts. Usedescribe/it/expectfrom vitest,renderfromvitest-browser-svelte.
Go Backend (server/)
- Layer structure:
handler(HTTP) →service(logic) →adapter(external Python service) andrepository(DB) →model(structs). Handlers must not contain business logic. - Struct tags: JSON uses
snake_case.omitemptyon optional response fields. Useformtags for query/form binding. Use pointer fields (*string) for optional patch request fields. Sensitive fields getjson:"-".type CleanedBill struct { ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` BillType string `bson:"bill_type" json:"bill_type"` } type UpdateBillRequest struct { Category *string `json:"category,omitempty"` } - Error Handling: Return
500for DB/internal errors,400for bad requests,404for not found. Wrap errors with context usingfmt.Errorf("context: %w", err). Checkerr == repository.ErrNotFoundfor 404 disambiguation. UseResult bool(notSuccess) in response envelopes.if err != nil { c.JSON(http.StatusInternalServerError, Response{Result: false, Message: err.Error()}) return } - Response envelope: Most endpoints:
Result bool,Message string,Data *T. Auth endpoints usesuccess bool,error string,data interface{}. - Interfaces: Use
adapter.Cleanerandrepository.BillRepositoryinterfaces. Access global singletons viaadapter.GetCleaner()andrepository.GetRepository(). - Time: Use the custom
LocalTimetype (wrapstime.Time) for all timestamp fields. It serializes as"2006-01-02 15:04:05"in both JSON and BSON, preserving local time. - Soft delete: Bills are never hard-deleted. All queries must filter
is_deleted: false.
Python Analyzer (analyzer/)
- Style: PEP 8.
snake_casefor variables, functions, and filenames.UPPER_CASEfor module-level constants. Prefix private module globals with_. - Type Hints: Mandatory for all function arguments and return types. Use
Optional[str]fromtypingorstr | None(Python 3.10+ union syntax). - Models: Use
pydantic.BaseModelfor all API request/response schemas.class CleanRequest(BaseModel): input_path: str output_path: str year: Optional[str] = None bill_type: Optional[str] = "auto" - FastAPI patterns: Use
HTTPException(status_code=400, detail=message)for user errors. Manage temporary files withtempfile.NamedTemporaryFile+os.unlinkinfinallyblocks. - Cleaner classes: Extend
BaseCleaner(ABC)fromcleaners/base.py. Implementclean()and optionallyreclassify(). Category inference reads rules fromconfig/category.yamlviayaml.safe_load. - Docstrings: Triple-quoted. Chinese descriptions are common for API endpoint docs.
Key Patterns
API Flow
Browser → SvelteKit proxy (/api/[...path]/+server.ts)
→ Go server (Gin, AuthRequired middleware)
→ handler → service → adapter.GetCleaner() → HTTP POST to Python FastAPI
→ repository.GetRepository() → MongoDB
Authentication
- JWT (HS256). Token in
localStorageunder keyauth. - Header:
Authorization: Bearer <token>. middleware.AuthRequired()wraps all/api/*routes except/api/auth/*.- Passwords in
config.yamlsupport plaintext or SHA-256 hashed values. - 401 anywhere →
auth.logout()+ redirect to/login.
File Processing
Upload flow: Upload (ZIP/XLSX) → Extract → Convert to UTF-8 CSV (Python /convert) →
Auto-detect bill type → Deduplicate against DB → Clean/normalize (Python /clean/upload) →
Save raw + cleaned bills to MongoDB.
Deduplication: raw bills check transaction_id; cleaned bills check
transaction_id + merchant_order_no. JD bills trigger soft-deletion of overlapping records in
other sources to prevent double-counting.
Adapter (Go ↔ Python)
adapter.Cleaner interface has two implementations: HTTP-based (adapter/http, default) and
subprocess-based (adapter/python, legacy). Controlled by ANALYZER_MODE env var.
Important Files
| File | Role |
|---|---|
web/src/lib/api.ts |
Central API client; apiFetch() injects auth and handles 401 |
web/src/lib/stores/auth.ts |
Auth state; JWT in localStorage; login/logout/validate |
web/src/lib/models/bill.ts |
UIBill model + converters to/from API CleanedBill shape |
web/src/routes/api/[...path]/+server.ts |
SvelteKit proxy to Go backend |
server/main.go |
Entry point; wires config, adapters, repository, router |
server/config/config.go |
YAML + env config; priority: defaults → config.yaml → env vars |
server/router/router.go |
All route definitions and middleware assignment |
server/middleware/auth.go |
JWT validation + user context injection |
server/handler/upload.go |
Full upload pipeline (extract → convert → clean → store) |
server/handler/bills.go |
List/filter bills with pagination and monthly stats |
server/model/bill.go |
RawBill, CleanedBill, MonthlyStat; custom LocalTime type |
server/adapter/adapter.go |
Cleaner interface definition |
server/repository/repository.go |
BillRepository interface (14 persistence methods) |
server/repository/mongo/repository.go |
MongoDB implementation with aggregation pipelines |
analyzer/server.py |
FastAPI entry point; /health, /clean, /convert, /detect routes |
analyzer/cleaners/base.py |
BaseCleaner ABC; shared filtering and output logic |
analyzer/cleaners/alipay.py |
Alipay-specific normalization |
analyzer/cleaners/wechat.py |
WeChat-specific normalization |
analyzer/cleaners/jd.py |
JD (京东) normalization and 3-level review scoring |
analyzer/category.py |
infer_category() using YAML keyword rules |
analyzer/converter.py |
xlsx→csv (openpyxl), GBK→UTF-8 re-encoding, type detection |
server/config.yaml |
Server port, MongoDB URI, JWT settings, user list |
docker-compose.yaml |
5 services: web, server, analyzer, mongodb, mongo-express |