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

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)

  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>:

// 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

# 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:

  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 complexityInventorySummary 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()