- 新增 .gitignore、ARCHITECTURE.md 项目基础设施文件 - 新增前后端路由对接文档,完整映射前端页面到后端 API 端点 - 配置前端 Vitest 测试框架,添加 API/Store/Utils/Components 单元测试 - 添加后端 UserService 单元测试 - 新增统一测试运行脚本 scripts/run-tests.sh - 清理旧文档和过期覆盖率报告文件 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
16 KiB
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)
- Load YAML config via Viper (
CONFIG_PATHenv var) - Ensure MySQL database exists (create/drop per config)
- Open GORM DB connection pool
AutoMigrateall 27 model tables- Instantiate repositories → services → handlers (manual DI)
- Create JWT auth middleware
- Setup Gin router (
server.SetupRouter) - Start HTTP server (goroutine)
- 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-routesafter 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>:
// 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.rolesin route config filtered against user roles - Button-level:
v-auth/v-permsdirectives 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
# 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
# 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:
- Backend: Go 1.25 + MySQL 8 service →
go test ./... -race - Frontend: Node 22 + pnpm 9 →
vitest run+typecheck+eslint - Summary: Gate job, fails if either backend or frontend fails
Key Design Decisions
- Manual DI over framework: All wiring in
main.gokeeps dependencies explicit and avoids magic - Interface-based services: Every service defines an interface for testability via mocks
- DTOs at API boundary: Request/response types in
internal/dto/decouple API contract from database schema - AutoMigrate for schema: All 27 models migrated on startup; additive-only schema changes
- File storage on local FS: Uploaded files go to
data/directory, served statically by Gin - Glob-based route auto-import: Vite's
import.meta.globloads all router modules without manual registration - Token refresh with request queueing: Axios interceptor queues failed requests during token refresh, replays them after
- Dual contract models: Legacy
Contractcoexists withZSPContractFile/ZSPContractDetailfor backward compatibility
Known Issues
- Dual contract models — Legacy
Contractand newZSPContractFile/ZSPContractDetailmodels coexist with overlapping concerns - API URL inconsistency — Some routes use
/list,/create,/update/:idsuffixes while others use direct REST patterns - Incomplete module coverage — Invoice and Settlement models are in AutoMigrate but lack handlers, services, and repositories
- Delivery handler complexity —
InventorySummarybypasses the service/repository layer and accesses*gorm.DBdirectly for complex aggregate queries - Hardcoded UI text — All UI text is hardcoded in Chinese; no i18n infrastructure
- Handler-level inconsistency — Some handlers use proper DTOs, others use inline anonymous structs for request binding
- Frontend finance API inconsistency — Finance API modules use hardcoded
/zsp-finances/prefix while other modules use/api/prefix viabaseUrlApi()