Files
zsp-project/ARCHITECTURE.md
jxw 2ff3002b19 feat: 添加项目基础设施、测试框架和路由对接文档
- 新增 .gitignore、ARCHITECTURE.md 项目基础设施文件
- 新增前后端路由对接文档,完整映射前端页面到后端 API 端点
- 配置前端 Vitest 测试框架,添加 API/Store/Utils/Components 单元测试
- 添加后端 UserService 单元测试
- 新增统一测试运行脚本 scripts/run-tests.sh
- 清理旧文档和过期覆盖率报告文件

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 08:53:36 +08:00

414 lines
16 KiB
Markdown

# ARCHITECTURE.md
## Project Overview
企业供应链合同管理系统 (中尚鹏 / ZSP) — a monorepo enterprise supply chain contract management system. It manages contracts, deliveries, inventory, finance, purchasing, and reporting for a bulk commodity supply chain business.
### 5 Business Modules (21 Sub-menus)
| Module | Sub-menus |
|---|---|
| 合同管理 (Contract) | 业务合同管理, 中尚鹏合同管理, 公司信息管理 |
| 提货管理 (Delivery) | 我要提货, 提货明细, 库存信息, 货权转移, 其他出库 |
| 财务管理 (Finance) | 货代费用, 杂费管理, 服务费管理, 发票管理, 结算管理, 对账单, 利润明细 |
| 采购管理 (Purchase) | 采购报单, 采购运踪明细, 国外供应商, 采购预算 |
| 查询&报表中心 (Reports) | 合同执行情况, 营业明细表, 其他明细表, 商品基础资料, 其他报表 |
---
## Directory Structure
```
zsp-project/
├── frontend/ # Vue 3 SPA (pure-admin-thin)
│ ├── src/
│ │ ├── api/ # Axios API modules (one per domain)
│ │ ├── assets/ # Static assets (icons, images, SVG)
│ │ ├── components/ # Reusable global components
│ │ ├── config/ # Runtime platform configuration
│ │ ├── directives/ # Custom Vue directives (auth, perms, copy, etc.)
│ │ ├── layout/ # App shell (vertical/horizontal/mix layouts)
│ │ ├── plugins/ # Plugin installers (Element Plus, ECharts)
│ │ ├── router/ # Vue Router + auto-imported route modules
│ │ ├── store/ # Pinia stores (user, app, permission, settings, etc.)
│ │ ├── style/ # SCSS/CSS (theme, dark mode, transitions, Tailwind)
│ │ ├── utils/ # HTTP client, auth, tree, message, print, etc.
│ │ └── views/ # Page components by feature domain
│ └── public/ # Static public assets
├── backend/ # Go + Gin REST API
│ ├── cmd/api/ # Entry point (main.go)
│ ├── configs/ # YAML configs (dev, prod)
│ ├── internal/
│ │ ├── dto/ # Request/response DTOs
│ │ ├── handler/ # HTTP handlers (Gin controllers)
│ │ ├── middleware/ # JWT auth middleware
│ │ ├── model/ # GORM database models (27 tables)
│ │ ├── repository/ # Data access layer (GORM queries)
│ │ ├── server/ # Router setup and route definitions
│ │ └── service/ # Business logic layer
│ ├── pkg/ # Shared packages (config, database, utils)
│ ├── test/ # Handler integration tests
│ ├── scripts/ # SQL init scripts
│ └── data/ # Uploaded files (runtime)
├── docs/ # Project documentation
│ ├── api/ # API docs and frontend route docs
│ ├── deploy/ # Deployment guides, nginx config
│ ├── dev/ # Requirements, gap analysis, meeting notes
│ └── superpowers/ # Implementation plans and specs
├── scripts/ # Test runner, deployment scripts
├── .github/workflows/ # CI pipeline (Go + Vue tests)
└── CLAUDE.md # Claude Code project guide
```
---
## Technology Stack
### Frontend
| Technology | Version | Purpose |
|---|---|---|
| Vue | 3.5 | UI framework (Composition API) |
| TypeScript | 5.8 | Type safety |
| Vite | 6.3 | Build tool & dev server |
| Pinia | 3.0 | State management |
| Vue Router | 4.5 | Client-side routing |
| Element Plus | 2.9 | UI component library |
| Axios | latest | HTTP client |
| Tailwind CSS | 4.1 | Utility-first CSS |
| ECharts | latest | Data visualization |
| Vitest | latest | Test framework |
### Backend
| Technology | Version | Purpose |
|---|---|---|
| Go | 1.25 | Runtime |
| Gin | 1.10 | HTTP framework |
| GORM | 1.31 | ORM |
| MySQL | 8.0 | Relational database |
| JWT (golang-jwt) | 4.5 | Authentication tokens |
| Viper | 1.21 | Configuration management |
| Excelize | 2.9 | Excel import/export |
| bcrypt | latest | Password hashing |
---
## Backend Architecture
### Layered Design
```
HTTP Request
┌─────────────────┐
│ Gin Router │ internal/server/route.go — single source of truth for all routes
├─────────────────┤
│ Middleware │ internal/middleware/auth.go — JWT Bearer token validation
├─────────────────┤
│ Handler │ internal/handler/ — request parsing, response writing (thin)
├─────────────────┤
│ Service │ internal/service/ — business logic, orchestration
├─────────────────┤
│ Repository │ internal/repository/ — GORM data access
├─────────────────┤
│ Model │ internal/model/ — GORM structs with table tags
└─────────────────┘
MySQL 8.0
```
**Key pattern**: Manual dependency injection in `cmd/api/main.go` — all repositories, services, and handlers are wired together at startup. Every service and most repositories define a Go interface, enabling mock-based unit testing.
### Startup Sequence (main.go)
1. Load YAML config via Viper (`CONFIG_PATH` env var)
2. Ensure MySQL database exists (create/drop per config)
3. Open GORM DB connection pool
4. `AutoMigrate` all 27 model tables
5. Instantiate repositories → services → handlers (manual DI)
6. Create JWT auth middleware
7. Setup Gin router (`server.SetupRouter`)
8. Start HTTP server (goroutine)
9. Graceful shutdown on SIGINT/SIGTERM
### API Route Groups
All routes are under `/api/` prefix with JWT middleware applied globally except for auth endpoints and static file serving.
| Route Group | Prefix | Handler | Key Endpoints |
|---|---|---|---|
| Auth (public) | `/api/auth` | UserHandler | `POST /register`, `POST /login` |
| Users | `/api/users` | UserHandler | `GET /:id` |
| Contract | `/api/contract` | ContractHandler | CRUD, file upload, folders, batches |
| ZSP Contract | `/api/zsp-contract` | ZSPContractHandler | CRUD, details, Excel import/export |
| Company | `/api/company` | CompanyHandler | Folders, file upload/CRUD |
| Delivery | `/api/apply-delivery` | DeliveryHandler | Delivery applications |
| Outbound | `/api/delivery-details` | DeliveryHandler | Outbound orders |
| Inventory | `/api/inventory` | DeliveryHandler | Warehouses, inbound, summary |
| Ownership | `/api/ownership-transfer` | DeliveryHandler | Ownership transfer CRUD + files |
| Finance | `/api/zsp-finances` | ZSPFinancesHandler | Freight, misc, service fees, costs, profits |
### Data Models (27 GORM Tables)
| Domain | Tables |
|---|---|
| User | `users` |
| Contract | `contracts`, `contract_files`, `contract_folders`, `contract_batches`, `contract_batch_files` |
| ZSP Contract | `zsp_contract_folders`, `zsp_contract_files`, `zsp_contract_details` |
| Company | `company_folders`, `company_files` |
| Delivery | `delivery_applies`, `delivery_outbounds`, `warehouses`, `inventory_inbounds`, `ownership_transfers`, `ownership_transfer_bls`, `ownership_transfer_files` |
| Finance | `zsp_freight_charges`, `zsp_misc_charges`, `zsp_service_fees`, `zsp_cost_breakdowns`, `zsp_profit_records` |
| Invoice | `zsp_upstream_invoices`, `zsp_downstream_invoices`, `zsp_freight_misc_invoices` |
| Settlement | `zsp_settlements`, `zsp_reconciliations` |
Note: Invoice and Settlement models are defined and auto-migrated but not yet wired into handlers/services.
### File Storage
Uploaded files are stored on the local filesystem under `backend/data/`:
- `./data/contract/` — contract files
- `./data/contract/batch/` — batch contract files
- `./data/zsp-contract/files/` — ZSP contract files
- `./data/company/files/` — company info files
Files are served statically via Gin (`/api/contract-files`, `/api/zsp-contract-files`, `/api/company-files`).
---
## Frontend Architecture
### Component Tree
```
App.vue
└── <el-config-provider>
├── <router-view> # Page content (or login page)
└── <ReDialog /> # Global dialog manager
Layout (after login)
└── Layout (index.vue) # Three modes: vertical / horizontal / mix
├── Navbar # Top bar (user info, search, fullscreen)
├── Sidebar # Menu (vertical/horizontal/mix variants)
├── MultiTags # Tab-based page navigation
├── <router-view> # Keep-alive cached page views
│ └── Page View # Domain-specific business pages
└── Settings Panel # Theme/layout preferences drawer
```
### Page Composition Pattern
Complex pages follow a consistent decomposition:
```
views/system/contract/business-contract/
├── index.vue # Page assembly (layout, orchestrates sub-components)
├── components/ # Page-specific sub-components
│ ├── ContractTable.vue
│ ├── EditContractDialog.vue
│ ├── FolderPanel.vue
│ ├── SearchPanel.vue
│ └── UploadDialog.vue
├── hooks/ # Composition API hooks (business logic)
│ └── useContract.ts
└── types.ts # Page-specific TypeScript types
```
### Routing
Routes use `import.meta.glob` to auto-import all modules from `src/router/modules/`. Two-tier routing:
- **Static routes**: Built-in pages (login, error pages, welcome, home) — defined in router modules
- **Async routes**: Business pages fetched from backend `/get-async-routes` after login, merged with static routes, filtered by user roles
Route structure mirrors the 5 business modules:
```
/ → Home
/login → Login
/system/contract/* → Contract management (3 pages)
/system/delivery/* → Delivery management (5 pages)
/system/finance/* → Finance management (7 pages)
/system/purchase/* → Purchase management (4 pages)
/system/query-report/* → Query & reports (5 pages)
```
### State Management (Pinia)
| Store | Key State |
|---|---|
| `user` | avatar, username, roles, permissions, tokens |
| `app` | sidebar state, layout mode, device type |
| `permission` | constant/whole menus, flattened routes, keep-alive cache list |
| `settings` | title, fixed header, sidebar visibility |
| `multiTags` | open tab list, tab cache toggle |
| `epTheme` | theme color, light/dark mode |
### API Layer (`src/api/`)
Each API module exports typed functions returning `BaseResponse<T>`:
```typescript
// Pattern: src/api/contract.ts
import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";
export const getContractList = (params?: object) => {
return http.request<BaseResponse<Contract[]>>("get", baseUrlApi("contract"), { params });
};
```
The HTTP client (`src/utils/http/index.ts`) handles:
- Base URL resolution (dev proxy vs production)
- JWT token attachment via request interceptor
- Automatic token refresh on 401 with request queueing
- NProgress loading bar integration
### Authentication Flow
```
Login → POST /api/auth/login
→ Receive JWT token
→ Store in cookies + localStorage
→ Fetch user info + async routes
→ Build dynamic menu from roles
→ Navigate to home
Token refresh:
→ 401 response intercepted
→ Queue subsequent requests
→ POST /refresh-token
→ Replay queued requests with new token
```
### Permission System
Two levels:
- **Route-level**: `meta.roles` in route config filtered against user roles
- **Button-level**: `v-auth` / `v-perms` directives and `<Auth>` / `<Perms>` components
---
## Data Flow
```
User Action (UI)
→ Pinia Store action (optional, for shared state)
→ API function call (src/api/domain.ts)
→ Axios request (auto-attaches JWT)
→ Vite dev proxy (/api → localhost:8080) or nginx proxy (production)
→ Gin router → JWT middleware → Handler
→ Service (business logic)
→ Repository (GORM queries)
→ MySQL 8.0
← JSON response { success, data, message }
← Axios interceptor (deserialize, handle 401)
← Component re-render
```
---
## Authentication & Authorization
- **Scheme**: JWT Bearer tokens (HS256, 24-hour expiry)
- **Password storage**: bcrypt hashed
- **Public endpoints**: `POST /api/auth/register`, `POST /api/auth/login`, static file routes
- **Middleware**: Extracts user ID from JWT claims into Gin context; all other routes require valid token
- **CORS**: All origins allowed with Authorization header (dev mode)
---
## Testing Strategy
### Backend Tests
| Location | Type | Coverage |
|---|---|---|
| `backend/test/` | Handler integration tests | User, contract, ZSP contract, company, delivery, finance handlers |
| `backend/internal/service/` | Service unit tests | User service (with mock repository) |
Pattern: Mock service/repository interfaces, use `httptest.NewRecorder` with Gin test mode.
### Frontend Tests
| Location | Type | Coverage |
|---|---|---|
| `src/api/__tests__/` | API module tests | All 12 API modules |
| `src/components/business/__tests__/` | Component tests | ConfirmDialog |
| `src/store/modules/__tests__/` | Store tests | User store |
| `src/utils/__tests__/` | Utility tests | Tree utilities |
Framework: Vitest + jsdom + @vue/test-utils. Test helpers in `src/api/test-utils.ts`.
### Test Runner
`scripts/run-tests.sh` supports: `backend`, `frontend`, `coverage`, `ci`, `all`.
---
## Deployment
### Development
```bash
# Backend (port 8080)
cd backend && go run cmd/api/main.go
# or with hot-reload
cd backend && air -c .air.toml
# Frontend (port 5173, proxies /api → 8080)
cd frontend && pnpm dev
# Docker Compose (backend + MySQL + Air)
cd backend && docker-compose -f docker-compose.dev.yml up --build
```
### Production
```bash
# Frontend build
cd frontend && pnpm build # → dist/
# Backend build (Docker)
cd backend && docker build -t zsp-backend .
# Full stack
docker-compose --env-file .env.prod up -d
```
Nginx serves the frontend SPA and proxies `/api/` to the Go backend. Configuration at `docs/deploy/nginx.conf`.
---
## CI/CD
`.github/workflows/test.yml` — Three-job pipeline triggered on push/PR to `master`/`main`/`dev`:
1. **Backend**: Go 1.25 + MySQL 8 service → `go test ./... -race`
2. **Frontend**: Node 22 + pnpm 9 → `vitest run` + `typecheck` + `eslint`
3. **Summary**: Gate job, fails if either backend or frontend fails
---
## Key Design Decisions
1. **Manual DI over framework**: All wiring in `main.go` keeps dependencies explicit and avoids magic
2. **Interface-based services**: Every service defines an interface for testability via mocks
3. **DTOs at API boundary**: Request/response types in `internal/dto/` decouple API contract from database schema
4. **AutoMigrate for schema**: All 27 models migrated on startup; additive-only schema changes
5. **File storage on local FS**: Uploaded files go to `data/` directory, served statically by Gin
6. **Glob-based route auto-import**: Vite's `import.meta.glob` loads all router modules without manual registration
7. **Token refresh with request queueing**: Axios interceptor queues failed requests during token refresh, replays them after
8. **Dual contract models**: Legacy `Contract` coexists with `ZSPContractFile`/`ZSPContractDetail` for backward compatibility
## Known Issues
1. **Dual contract models** — Legacy `Contract` and new `ZSPContractFile`/`ZSPContractDetail` models coexist with overlapping concerns
2. **API URL inconsistency** — Some routes use `/list`, `/create`, `/update/:id` suffixes while others use direct REST patterns
3. **Incomplete module coverage** — Invoice and Settlement models are in AutoMigrate but lack handlers, services, and repositories
4. **Delivery handler complexity**`InventorySummary` bypasses the service/repository layer and accesses `*gorm.DB` directly for complex aggregate queries
5. **Hardcoded UI text** — All UI text is hardcoded in Chinese; no i18n infrastructure
6. **Handler-level inconsistency** — Some handlers use proper DTOs, others use inline anonymous structs for request binding
7. **Frontend finance API inconsistency** — Finance API modules use hardcoded `/zsp-finances/` prefix while other modules use `/api/` prefix via `baseUrlApi()`