886 lines
32 KiB
Markdown
886 lines
32 KiB
Markdown
# 设计文档:前后端分离 — Java API 后端与前端集成
|
||
|
||
## 概述
|
||
|
||
本设计文档描述将现有 Next.js 16 前端生产管理子系统从硬编码模拟数据迁移到真实 RESTful API 的完整技术方案。涵盖 Java Spring Boot 3.3.6 后端多模块项目架构、PostgreSQL 数据库设计、DDD 分层实现,以及前端 API 集成层的构建。
|
||
|
||
系统为地球物理仪器(高密度电法仪等)的全生命周期管理平台,包含设备管理、板卡管理、固件库、校准管理、校准文件管理、授权管理、配置文件管理、维修工单、报废回收等核心业务模块。
|
||
|
||
### 设计目标
|
||
|
||
- 搭建符合 DDD 分层架构的 Spring Boot 多模块 Maven 项目
|
||
- 设计 PostgreSQL `dev` schema 下的完整数据库表结构
|
||
- 为所有前端页面提供对应的 RESTful API 端点
|
||
- 在前端建立统一的 API 调用层,替换所有硬编码模拟数据
|
||
- 前后端采用统一的分页、筛选、错误响应规范
|
||
|
||
## 架构
|
||
|
||
### 整体架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph Frontend["前端 Next.js 16"]
|
||
Pages["页面组件<br/>devices / boards / firmware / ..."]
|
||
ApiLayer["API 集成层<br/>src/lib/api/"]
|
||
Pages --> ApiLayer
|
||
end
|
||
|
||
subgraph Backend["后端 Spring Boot 3.3.6"]
|
||
subgraph ApiAdmin["api-admin 模块"]
|
||
Controllers["REST Controllers<br/>/api/admin/*"]
|
||
end
|
||
subgraph ApiPortal["api-portal 模块"]
|
||
PortalControllers["REST Controllers<br/>/api/portal/*<br/>(无需鉴权)"]
|
||
end
|
||
subgraph Business["business 模块"]
|
||
subgraph Device["device 子模块"]
|
||
Interfaces["interfaces 层<br/>Controller / VO / Assembler"]
|
||
Application["application 层<br/>AppService / Command / Query"]
|
||
Domain["domain 层<br/>Entity / Repository接口"]
|
||
Infrastructure["infrastructure 层<br/>Mapper / DO / Repository实现"]
|
||
end
|
||
end
|
||
subgraph Common["common 模块"]
|
||
Response["统一响应体 R<T>"]
|
||
Exception["全局异常处理"]
|
||
Audit["审计字段自动填充"]
|
||
end
|
||
end
|
||
|
||
subgraph DB["PostgreSQL 12.14"]
|
||
DevSchema["dev schema<br/>devices / device_models / board_types / board_cards / calibration_files / ..."]
|
||
end
|
||
|
||
ApiLayer -->|HTTP REST| Controllers
|
||
ApiLayer -->|HTTP REST| PortalControllers
|
||
Controllers --> Interfaces
|
||
PortalControllers --> Interfaces
|
||
Interfaces --> Application
|
||
Application --> Domain
|
||
Domain --> Infrastructure
|
||
Infrastructure -->|MyBatis-Plus| DevSchema
|
||
```
|
||
|
||
### 后端项目结构
|
||
|
||
```
|
||
apps/geo-bps-api/
|
||
├── pom.xml # 父 POM
|
||
├── api-admin/
|
||
│ ├── pom.xml
|
||
│ └── src/main/java/com/geomative/bps/admin/
|
||
│ ├── ApiAdminApplication.java
|
||
│ └── config/
|
||
│ ├── CorsConfig.java # CORS 配置
|
||
│ └── WebMvcConfig.java
|
||
├── api-portal/
|
||
│ ├── pom.xml
|
||
│ └── src/main/java/com/geomative/bps/portal/
|
||
│ ├── ApiPortalApplication.java
|
||
│ └── config/
|
||
│ ├── CorsConfig.java # CORS 配置
|
||
│ └── WebMvcConfig.java # 文件上传大小限制等
|
||
├── business/
|
||
│ ├── pom.xml # pom packaging
|
||
│ └── device/
|
||
│ ├── pom.xml
|
||
│ └── src/main/java/com/geomative/bps/device/
|
||
│ ├── interfaces/ # Controller + VO
|
||
│ ├── application/ # AppService + Command + Query
|
||
│ ├── domain/ # Entity + Repository接口
|
||
│ └── infrastructure/ # Mapper + DO + Repository实现
|
||
└── common/
|
||
├── pom.xml
|
||
└── src/main/java/com/geomative/bps/common/
|
||
├── response/
|
||
│ └── R.java # 统一响应体
|
||
├── exception/
|
||
│ ├── BizException.java
|
||
│ └── GlobalExceptionHandler.java
|
||
├── mybatis/
|
||
│ └── AuditMetaObjectHandler.java
|
||
└── page/
|
||
└── PageResult.java # 分页响应
|
||
```
|
||
|
||
### 前端 API 集成层结构
|
||
|
||
```
|
||
src/
|
||
├── lib/
|
||
│ └── api/
|
||
│ ├── client.ts # Axios 实例,统一配置
|
||
│ ├── types.ts # 通用响应/分页类型
|
||
│ ├── deviceApi.ts # 设备管理 API
|
||
│ ├── modelApi.ts # 设备型号 API
|
||
│ ├── boardApi.ts # 板卡管理 API
|
||
│ ├── firmwareApi.ts # 固件库 API
|
||
│ ├── calibrationApi.ts # 校准管理 API
|
||
│ ├── calibrationFileApi.ts # 校准文件 API
|
||
│ ├── configFileApi.ts # 配置文件 API
|
||
│ ├── licenseApi.ts # 授权管理 API
|
||
│ ├── repairApi.ts # 维修工单 API
|
||
│ ├── scrapApi.ts # 报废管理 API
|
||
│ └── dashboardApi.ts # 首页统计 API
|
||
└── hooks/
|
||
└── useApi.ts # 通用数据获取 Hook
|
||
```
|
||
|
||
## 组件与接口
|
||
|
||
### 统一响应体
|
||
|
||
```java
|
||
public class R<T> {
|
||
private int code; // 业务状态码,0=成功
|
||
private String message; // 提示信息
|
||
private T data; // 响应数据
|
||
|
||
public static <T> R<T> ok(T data) { ... }
|
||
public static <T> R<T> fail(int code, String message) { ... }
|
||
}
|
||
```
|
||
|
||
### 分页响应体
|
||
|
||
```java
|
||
public class PageResult<T> {
|
||
private long total; // 总记录数
|
||
private int page; // 当前页码
|
||
private int pageSize; // 每页条数
|
||
private List<T> records; // 数据列表
|
||
}
|
||
```
|
||
|
||
### 统一分页请求参数
|
||
|
||
所有列表接口统一使用:
|
||
- `page`:页码,从 1 开始,默认 1
|
||
- `pageSize`:每页条数,默认 10
|
||
|
||
### API 端点清单
|
||
|
||
| 模块 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| 设备 | GET | `/api/admin/devices` | 分页设备列表(筛选:型号/状态/日期/SN/批次) |
|
||
| 设备 | GET | `/api/admin/devices/{sn}` | 设备详情(含授权、子设备、固件) |
|
||
| 设备 | POST | `/api/admin/devices` | 创建设备(设备登记) |
|
||
| 设备 | GET | `/api/admin/devices/batches` | 生产批次列表 |
|
||
| 型号 | GET | `/api/admin/device-models` | 设备型号列表 |
|
||
| 型号 | POST | `/api/admin/device-models` | 创建设备型号 |
|
||
| 型号 | GET | `/api/admin/checklist-templates` | Checklist 模板列表 |
|
||
| 型号 | POST | `/api/admin/checklist-templates` | 创建 Checklist 模板 |
|
||
| 板卡 | GET | `/api/admin/board-types` | 板卡型号列表(筛选:类型) |
|
||
| 板卡 | GET | `/api/admin/board-types/{id}` | 板卡详情 |
|
||
| 板卡 | POST | `/api/admin/board-types` | 创建板卡型号 |
|
||
| 固件 | GET | `/api/admin/firmware` | 固件版本列表(筛选:类型/板卡型号) |
|
||
| 固件 | POST | `/api/admin/firmware` | 创建固件版本 |
|
||
| 固件 | GET | `/api/admin/firmware/{id}/download` | 下载固件文件 |
|
||
| 校准 | GET | `/api/admin/calibrations` | 校准记录列表(筛选:SN/状态/人员) |
|
||
| 校准 | GET | `/api/admin/calibrations/{id}` | 校准详情 |
|
||
| 校准 | POST | `/api/admin/calibrations/import` | 批量导入校准记录 |
|
||
| 校准文件 | POST | `/api/portal/calibration-files/upload` | 上传校准文件(无需鉴权,上位机调用) |
|
||
| 校准文件 | GET | `/api/admin/board-cards/{id}/calibration-files` | 指定采集板的校准文件列表(按上传时间倒序) |
|
||
| 校准文件 | GET | `/api/admin/calibration-files/{id}/download` | 下载校准文件 |
|
||
| 配置 | GET | `/api/admin/config-files` | 配置文件列表(筛选:型号/版本/关键字) |
|
||
| 配置 | GET | `/api/admin/config-files/{id}` | 配置文件详情 |
|
||
| 配置 | POST | `/api/admin/config-files` | 创建配置文件 |
|
||
| 配置 | PUT | `/api/admin/config-files/{id}` | 更新配置文件 |
|
||
| 配置 | DELETE | `/api/admin/config-files/{id}` | 逻辑删除配置文件 |
|
||
| 授权 | GET | `/api/admin/licenses` | 授权列表(筛选:型号/状态) |
|
||
| 授权 | POST | `/api/admin/licenses` | 创建授权记录 |
|
||
| 授权 | PUT | `/api/admin/licenses/{id}` | 更新授权记录 |
|
||
| 授权 | PUT | `/api/admin/licenses/{id}/disable` | 停用授权 |
|
||
| 维修 | GET | `/api/admin/repair-orders` | 工单列表(筛选:状态/优先级/人员/日期/SN) |
|
||
| 维修 | GET | `/api/admin/repair-orders/{id}` | 工单详情 |
|
||
| 维修 | POST | `/api/admin/repair-orders` | 创建工单 |
|
||
| 维修 | PUT | `/api/admin/repair-orders/{id}/process` | 处理工单 |
|
||
| 维修 | PUT | `/api/admin/repair-orders/{id}/close` | 关闭工单 |
|
||
| 报废 | GET | `/api/admin/scrap-records` | 报废记录列表(筛选:SN/状态/日期) |
|
||
| 报废 | GET | `/api/admin/scrap-records/{id}` | 报废详情 |
|
||
| 报废 | PUT | `/api/admin/scrap-records/{id}/approve` | 审批通过 |
|
||
| 报废 | PUT | `/api/admin/scrap-records/{id}/reject` | 驳回 |
|
||
| 报废 | PUT | `/api/admin/scrap-records/{id}/recover` | 物料回收入库 |
|
||
| 报废 | GET | `/api/admin/scrap-records/stats` | 报废统计 |
|
||
| 首页 | GET | `/api/admin/dashboard/metrics` | 统计指标 |
|
||
| 首页 | GET | `/api/admin/dashboard/device-status` | 设备状态分布 |
|
||
| 首页 | GET | `/api/admin/dashboard/tasks` | 待处理任务 |
|
||
|
||
### 前端 API 客户端
|
||
|
||
```typescript
|
||
// src/lib/api/client.ts
|
||
import axios from 'axios';
|
||
|
||
const apiClient = axios.create({
|
||
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080',
|
||
timeout: 5000,
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
|
||
// 响应拦截器:统一错误处理
|
||
apiClient.interceptors.response.use(
|
||
(res) => res.data,
|
||
(error) => {
|
||
const message = error.response?.data?.message || '请求失败,请稍后重试';
|
||
return Promise.reject(new Error(message));
|
||
}
|
||
);
|
||
|
||
export default apiClient;
|
||
```
|
||
|
||
### 前端通用类型
|
||
|
||
```typescript
|
||
// src/lib/api/types.ts
|
||
export interface ApiResponse<T> {
|
||
code: number;
|
||
message: string;
|
||
data: T;
|
||
}
|
||
|
||
export interface PageResult<T> {
|
||
total: number;
|
||
page: number;
|
||
pageSize: number;
|
||
records: T[];
|
||
}
|
||
|
||
export interface PageParams {
|
||
page?: number;
|
||
pageSize?: number;
|
||
}
|
||
```
|
||
|
||
|
||
## 数据模型
|
||
|
||
### ER 关系图
|
||
|
||
```mermaid
|
||
erDiagram
|
||
device_models ||--o{ devices : "型号关联"
|
||
device_models ||--o{ checklist_templates : "模板关联"
|
||
device_models ||--o{ config_files : "配置关联"
|
||
device_models ||--o{ licenses : "授权关联"
|
||
checklist_templates ||--o{ checklist_items : "包含检查项"
|
||
devices ||--o{ device_boards : "装配板卡"
|
||
devices ||--o{ repair_orders : "维修工单"
|
||
devices ||--o{ scrap_records : "报废记录"
|
||
board_types ||--o{ device_boards : "板卡实例"
|
||
board_types ||--o{ firmware_versions : "固件版本"
|
||
board_types ||--o{ calibration_records : "校准记录"
|
||
board_cards ||--o{ calibration_files : "校准文件"
|
||
|
||
device_models {
|
||
varchar(64) id PK
|
||
varchar(100) name
|
||
varchar(50) code UK
|
||
varchar(20) status
|
||
varchar(500) description
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
devices {
|
||
varchar(64) id PK
|
||
varchar(100) sn UK
|
||
varchar(64) model_id FK
|
||
varchar(100) model_name
|
||
varchar(20) status
|
||
varchar(20) firmware_version
|
||
timestamp production_date
|
||
varchar(200) customer
|
||
varchar(20) batch
|
||
varchar(64) operator
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
board_types {
|
||
varchar(64) id PK
|
||
varchar(50) type
|
||
varchar(50) version UK
|
||
varchar(20) latest_firmware
|
||
date production_date
|
||
varchar(20) status
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
device_boards {
|
||
varchar(64) id PK
|
||
varchar(64) device_id FK
|
||
varchar(100) device_sn
|
||
varchar(64) board_type_id FK
|
||
varchar(100) board_sn
|
||
varchar(50) board_name
|
||
varchar(50) board_model
|
||
varchar(20) calibration_status
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
firmware_versions {
|
||
varchar(64) id PK
|
||
varchar(20) version
|
||
varchar(64) board_type_id FK
|
||
varchar(50) board_version
|
||
varchar(50) firmware_type
|
||
date release_date
|
||
varchar(20) status
|
||
varchar(20) file_size
|
||
int download_count
|
||
varchar(200) hw_range
|
||
varchar(20) upgrade_type
|
||
boolean signed
|
||
varchar(64) md5
|
||
varchar(128) sha256
|
||
text release_notes
|
||
varchar(500) file_path
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
calibration_records {
|
||
varchar(64) id PK
|
||
varchar(100) board_sn
|
||
varchar(64) board_type_id FK
|
||
varchar(50) board_version
|
||
date calibration_date
|
||
date expiry_date
|
||
varchar(100) calibrator
|
||
varchar(20) status
|
||
int channel_count
|
||
decimal overall_deviation
|
||
jsonb channel_results
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
config_files {
|
||
varchar(64) id PK
|
||
varchar(100) name
|
||
varchar(64) model_id FK
|
||
varchar(100) model_name
|
||
varchar(20) version
|
||
varchar(20) status
|
||
jsonb transmission_params
|
||
jsonb acquisition_params
|
||
jsonb protection_params
|
||
jsonb network_params
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
licenses {
|
||
varchar(64) id PK
|
||
varchar(64) model_id FK
|
||
varchar(50) model_code
|
||
jsonb modules
|
||
varchar(20) validity_type
|
||
date expiry_date
|
||
varchar(20) status
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
repair_orders {
|
||
varchar(64) id PK
|
||
varchar(50) order_no UK
|
||
varchar(100) device_sn
|
||
varchar(50) fault_type
|
||
varchar(20) status
|
||
varchar(10) priority
|
||
varchar(100) assignee
|
||
varchar(500) description
|
||
varchar(500) phenomenon
|
||
date expected_fix_date
|
||
text note
|
||
jsonb process_records
|
||
jsonb board_replacements
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
scrap_records {
|
||
varchar(64) id PK
|
||
varchar(100) device_sn
|
||
varchar(100) model_name
|
||
varchar(500) reason
|
||
varchar(100) applicant
|
||
varchar(20) status
|
||
varchar(50) order_id
|
||
decimal residual_value
|
||
jsonb materials
|
||
jsonb approval_timeline
|
||
text approval_note
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
checklist_templates {
|
||
varchar(64) id PK
|
||
varchar(64) model_id FK
|
||
varchar(50) model_code
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
checklist_items {
|
||
varchar(64) id PK
|
||
varchar(64) template_id FK
|
||
varchar(200) name
|
||
boolean required
|
||
int sort_order
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
board_cards {
|
||
varchar(64) id PK
|
||
varchar(100) sn UK
|
||
varchar(50) type
|
||
varchar(50) version
|
||
varchar(20) firmware_version
|
||
varchar(20) status
|
||
varchar(100) device_sn
|
||
date production_date
|
||
varchar(20) calib_status
|
||
date calib_date
|
||
varchar(500) remark
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
|
||
calibration_files {
|
||
varchar(64) id PK
|
||
varchar(100) board_sn
|
||
varchar(200) original_name
|
||
varchar(500) file_path
|
||
bigint file_size
|
||
varchar(64) md5
|
||
timestamp upload_time
|
||
timestamp created_at
|
||
varchar(64) created_by
|
||
timestamp updated_at
|
||
varchar(64) updated_by
|
||
smallint deleted
|
||
}
|
||
```
|
||
|
||
### 表结构详细说明
|
||
|
||
#### 公共审计字段(所有表必须包含)
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | VARCHAR(64) | PK | 应用层生成(雪花算法/UUID) |
|
||
| created_at | TIMESTAMP | NOT NULL | 创建时间,INSERT 自动填充 |
|
||
| created_by | VARCHAR(64) | NULL | 创建人 |
|
||
| updated_at | TIMESTAMP | NOT NULL | 修改时间,INSERT/UPDATE 自动填充 |
|
||
| updated_by | VARCHAR(64) | NULL | 修改人 |
|
||
| deleted | SMALLINT | NOT NULL DEFAULT 0 | 逻辑删除标记 |
|
||
|
||
#### dev.devices — 设备表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| sn | VARCHAR(100) | 设备SN号,唯一索引 |
|
||
| model_id | VARCHAR(64) | 关联设备型号 |
|
||
| model_name | VARCHAR(100) | 型号名称(冗余) |
|
||
| status | VARCHAR(20) | 装配中/已出厂/已激活/报废 |
|
||
| firmware_version | VARCHAR(20) | 当前固件版本 |
|
||
| production_date | TIMESTAMP | 生产日期 |
|
||
| customer | VARCHAR(200) | 客户名称 |
|
||
| batch | VARCHAR(20) | 生产批次(YYYY-WXX) |
|
||
| operator | VARCHAR(64) | 登记人 |
|
||
|
||
#### dev.device_models — 设备型号表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| name | VARCHAR(100) | 型号名称(如 GD-30 Supreme) |
|
||
| code | VARCHAR(50) | 型号代码(如 GD30),唯一索引 |
|
||
| status | VARCHAR(20) | 在产/停产 |
|
||
| description | VARCHAR(500) | 描述 |
|
||
|
||
#### dev.board_types — 板卡型号表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| type | VARCHAR(50) | 板卡类型(主协板/采集板/发射板/升压板) |
|
||
| version | VARCHAR(50) | 版本号(如 MB-V1.8),唯一索引 |
|
||
| latest_firmware | VARCHAR(20) | 最新固件版本 |
|
||
| production_date | DATE | 生产日期 |
|
||
| status | VARCHAR(20) | 在产/停产 |
|
||
|
||
#### dev.device_boards — 设备板卡关联表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| device_id | VARCHAR(64) | 关联设备 |
|
||
| device_sn | VARCHAR(100) | 设备SN(冗余) |
|
||
| board_type_id | VARCHAR(64) | 关联板卡型号 |
|
||
| board_sn | VARCHAR(100) | 板卡SN号 |
|
||
| board_name | VARCHAR(50) | 板卡名称(主协板/采集板等) |
|
||
| board_model | VARCHAR(50) | 板卡型号 |
|
||
| calibration_status | VARCHAR(20) | 校准状态 |
|
||
|
||
#### dev.firmware_versions — 固件版本表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| version | VARCHAR(20) | 版本号 |
|
||
| board_type_id | VARCHAR(64) | 关联板卡型号 |
|
||
| board_version | VARCHAR(50) | 板卡版本(冗余) |
|
||
| firmware_type | VARCHAR(50) | 固件类型(主协板/采集板/发射板/升压板/主机固件/计算单元固件) |
|
||
| release_date | DATE | 发布日期 |
|
||
| status | VARCHAR(20) | 已发布/草稿 |
|
||
| file_size | VARCHAR(20) | 文件大小 |
|
||
| download_count | INT | 下载次数,默认 0 |
|
||
| hw_range | VARCHAR(200) | 硬件版本范围 |
|
||
| upgrade_type | VARCHAR(20) | 可选/强制 |
|
||
| signed | BOOLEAN | 是否数字签名 |
|
||
| md5 | VARCHAR(64) | MD5 校验值 |
|
||
| sha256 | VARCHAR(128) | SHA256 校验值 |
|
||
| release_notes | TEXT | 发布说明(JSON 数组) |
|
||
| file_path | VARCHAR(500) | 文件存储路径 |
|
||
|
||
#### dev.calibration_records — 校准记录表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| board_sn | VARCHAR(100) | 采集板SN号 |
|
||
| board_type_id | VARCHAR(64) | 关联板卡型号 |
|
||
| board_version | VARCHAR(50) | 板卡版本(冗余) |
|
||
| calibration_date | DATE | 校准日期 |
|
||
| expiry_date | DATE | 到期日期 |
|
||
| calibrator | VARCHAR(100) | 校准人员 |
|
||
| status | VARCHAR(20) | 合格/不合格/待校准 |
|
||
| channel_count | INT | 通道数 |
|
||
| overall_deviation | DECIMAL(10,4) | 综合偏差 |
|
||
| channel_results | JSONB | 各通道校准结果 |
|
||
|
||
#### dev.config_files — 配置文件表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| name | VARCHAR(100) | 配置文件名(如 CFG-GD30-v1.3.0) |
|
||
| model_id | VARCHAR(64) | 关联设备型号 |
|
||
| model_name | VARCHAR(100) | 型号名称(冗余) |
|
||
| version | VARCHAR(20) | 配置版本 |
|
||
| status | VARCHAR(20) | 生效/已停用 |
|
||
| transmission_params | JSONB | 发射参数 |
|
||
| acquisition_params | JSONB | 采集参数 |
|
||
| protection_params | JSONB | 保护参数 |
|
||
| network_params | JSONB | 网络参数 |
|
||
|
||
#### dev.licenses — 授权表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| model_id | VARCHAR(64) | 关联设备型号 |
|
||
| model_code | VARCHAR(50) | 型号代码(冗余) |
|
||
| modules | JSONB | 授权模块列表 |
|
||
| validity_type | VARCHAR(20) | 永久/1年/2年/自定义 |
|
||
| expiry_date | DATE | 到期日期 |
|
||
| status | VARCHAR(20) | 生效/草稿/已停用 |
|
||
|
||
#### dev.repair_orders — 维修工单表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| order_no | VARCHAR(50) | 工单号(如 WO-2024-0001),唯一索引 |
|
||
| device_sn | VARCHAR(100) | 设备SN号 |
|
||
| fault_type | VARCHAR(50) | 故障类型 |
|
||
| status | VARCHAR(20) | 待处理/处理中/已处理 |
|
||
| priority | VARCHAR(10) | 高/中/低 |
|
||
| assignee | VARCHAR(100) | 负责人 |
|
||
| description | VARCHAR(500) | 故障描述 |
|
||
| phenomenon | VARCHAR(500) | 故障现象 |
|
||
| expected_fix_date | DATE | 预计修复日期 |
|
||
| note | TEXT | 备注 |
|
||
| process_records | JSONB | 处理记录时间线 |
|
||
| board_replacements | JSONB | 板卡更换记录 |
|
||
|
||
#### dev.scrap_records — 报废记录表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| device_sn | VARCHAR(100) | 设备SN号 |
|
||
| model_name | VARCHAR(100) | 设备型号 |
|
||
| reason | VARCHAR(500) | 报废原因 |
|
||
| applicant | VARCHAR(100) | 申请人 |
|
||
| status | VARCHAR(20) | 待审批/审批中/已审批/已驳回/回收中/已回收 |
|
||
| order_id | VARCHAR(50) | 来源工单号 |
|
||
| residual_value | DECIMAL(12,2) | 残值评估 |
|
||
| materials | JSONB | 可回收物料列表 |
|
||
| approval_timeline | JSONB | 审批记录时间线 |
|
||
| approval_note | TEXT | 审批意见 |
|
||
|
||
#### dev.checklist_templates — Checklist 模板表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| model_id | VARCHAR(64) | 关联设备型号 |
|
||
| model_code | VARCHAR(50) | 型号代码(冗余) |
|
||
|
||
#### dev.checklist_items — Checklist 检查项表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| template_id | VARCHAR(64) | 关联模板 |
|
||
| name | VARCHAR(200) | 检查项名称 |
|
||
| required | BOOLEAN | 是否必填 |
|
||
| sort_order | INT | 排序序号 |
|
||
|
||
#### dev.calibration_files — 校准文件表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| board_sn | VARCHAR(100) | 采集板SN号,关联 board_cards.sn |
|
||
| original_name | VARCHAR(200) | 原始文件名 |
|
||
| file_path | VARCHAR(500) | 服务器存储路径 |
|
||
| file_size | BIGINT | 文件大小(字节) |
|
||
| md5 | VARCHAR(64) | 文件MD5校验值 |
|
||
| upload_time | TIMESTAMP | 上传时间 |
|
||
|
||
|
||
## 正确性属性
|
||
|
||
*正确性属性是在系统所有有效执行中都应成立的特征或行为——本质上是对系统应做什么的形式化陈述。属性是人类可读规范与机器可验证正确性保证之间的桥梁。*
|
||
|
||
### Property 1: 统一响应格式一致性
|
||
|
||
*For any* API 请求(无论成功或失败),响应 JSON 都应包含且仅包含 `code`(整数)、`message`(字符串)、`data`(泛型)三个顶层字段。成功时 code=0,业务异常和参数校验异常均应返回 code≠0 的统一格式响应。
|
||
|
||
**Validates: Requirements 1.3, 1.4**
|
||
|
||
### Property 2: 审计字段自动填充
|
||
|
||
*For any* 通过 MyBatis-Plus 插入的实体记录,`created_at` 和 `updated_at` 字段应非空且为合理的时间戳;*For any* 更新操作,`updated_at` 应大于等于更新前的值。*For any* `dev` schema 下的业务表,都应包含 `id`、`created_at`、`created_by`、`updated_at`、`updated_by`、`deleted` 六个审计字段。
|
||
|
||
**Validates: Requirements 1.5, 2.12**
|
||
|
||
### Property 3: CRUD 数据往返一致性
|
||
|
||
*For any* 有效的创建命令(设备、型号、板卡、固件、配置文件、授权、工单、Checklist 模板),通过 POST 创建后再通过 GET 查询,返回的数据应与创建时提交的数据一致(在服务端生成的字段如 id、审计字段除外)。
|
||
|
||
**Validates: Requirements 3.2, 3.3, 4.2, 4.4, 5.3, 6.2, 8.3, 8.4, 9.2, 9.3, 10.3**
|
||
|
||
### Property 4: 唯一性约束与冲突检测
|
||
|
||
*For any* 已存在的唯一标识(设备SN号、型号代码、同一板卡型号下的固件版本号),尝试创建具有相同唯一标识的记录时,API 应返回 HTTP 409 冲突错误码和描述性错误消息,且数据库中不应产生重复记录。
|
||
|
||
**Validates: Requirements 3.5, 4.5, 6.4**
|
||
|
||
### Property 5: 筛选条件正确性
|
||
|
||
*For any* 列表查询接口和任意有效的筛选参数组合,返回的所有记录都应满足指定的筛选条件。例如:按型号筛选时,所有返回的设备型号应与筛选值匹配;按状态筛选时,所有返回记录的状态应与筛选值匹配。
|
||
|
||
**Validates: Requirements 3.1, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1, 11.1**
|
||
|
||
### Property 6: 状态转换正确性
|
||
|
||
*For any* 支持状态转换的实体(授权停用、工单关闭、报废审批/驳回/回收),执行状态转换操作后,实体的状态应更新为目标状态,且相关附加信息(如驳回意见、回收物料清单)应被正确记录。
|
||
|
||
**Validates: Requirements 9.4, 10.5, 11.3, 11.4, 11.5**
|
||
|
||
### Property 7: 逻辑删除排除性
|
||
|
||
*For any* 被逻辑删除的记录(deleted=1),该记录不应出现在任何列表查询的结果中。
|
||
|
||
**Validates: Requirements 8.5**
|
||
|
||
### Property 8: 分页一致性
|
||
|
||
*For any* 列表查询接口,响应应包含 `total`、`page`、`pageSize`、`records` 四个字段;`records` 的长度应不超过 `pageSize`;当不传筛选条件或筛选条件为空时,应返回不带过滤的完整数据集(受分页限制)。
|
||
|
||
**Validates: Requirements 14.1, 14.2, 14.4**
|
||
|
||
### Property 9: 聚合统计一致性
|
||
|
||
*For any* 统计接口返回的数据,各分类计数之和应等于总数。例如:设备状态分布中各状态数量之和应等于设备总数;报废统计中各状态数量之和应等于报废总数。
|
||
|
||
**Validates: Requirements 11.6, 12.2**
|
||
|
||
### Property 10: 参数校验防御性
|
||
|
||
*For any* 无效的筛选参数(如非法的状态值、格式错误的日期),API 应返回 HTTP 400 错误码和描述性错误消息,而非返回错误数据或抛出未处理异常。
|
||
|
||
**Validates: Requirements 14.3**
|
||
|
||
### Property 11: 校准文件上传-下载往返一致性
|
||
|
||
*For any* 有效的校准文件和已存在的采集板SN号,通过 POST `/api/portal/calibration-files/upload` 上传后,再通过 GET `/api/admin/calibration-files/{id}/download` 下载,下载的文件内容应与上传的原始文件完全一致,且 `dev.calibration_files` 表中的记录应正确关联该采集板SN号,文件大小和MD5校验值应与原始文件匹配。
|
||
|
||
**Validates: Requirements 17.2, 17.3, 17.7**
|
||
|
||
### Property 12: 校准文件列表完整性与排序
|
||
|
||
*For any* 采集板,上传 N 个校准文件后,通过 GET `/api/admin/board-cards/{id}/calibration-files` 查询应返回恰好 N 条记录,每条记录包含 id、fileName、fileSize、md5、uploadTime 字段,且结果按 uploadTime 倒序排列。
|
||
|
||
**Validates: Requirements 17.6, 17.10**
|
||
|
||
## 错误处理
|
||
|
||
### 后端错误处理策略
|
||
|
||
| 错误类型 | HTTP 状态码 | 业务码 | 处理方式 |
|
||
|----------|------------|--------|----------|
|
||
| 参数校验失败 | 400 | 40001 | `GlobalExceptionHandler` 捕获 `MethodArgumentNotValidException`,返回字段级错误信息 |
|
||
| 资源不存在 | 404 | 40401 | Service 层抛出 `BizException`,Handler 统一处理 |
|
||
| 文件不存在 | 404 | 40402 | 校准文件在存储目录中不存在时,返回描述性错误消息 |
|
||
| 唯一性冲突 | 409 | 40901 | Service 层检测重复后抛出 `BizException` |
|
||
| 文件上传失败 | 500 | 50002 | 文件写入磁盘失败时,记录日志并返回错误消息 |
|
||
| 服务器内部错误 | 500 | 50001 | `GlobalExceptionHandler` 兜底捕获,记录完整堆栈日志 |
|
||
|
||
### 自定义异常体系
|
||
|
||
```java
|
||
public class BizException extends RuntimeException {
|
||
private final int code;
|
||
private final String message;
|
||
|
||
public BizException(int code, String message) {
|
||
super(message);
|
||
this.code = code;
|
||
this.message = message;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 全局异常处理器
|
||
|
||
```java
|
||
@RestControllerAdvice
|
||
public class GlobalExceptionHandler {
|
||
|
||
@ExceptionHandler(BizException.class)
|
||
public R<Void> handleBizException(BizException e) {
|
||
return R.fail(e.getCode(), e.getMessage());
|
||
}
|
||
|
||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||
public R<Void> handleValidation(MethodArgumentNotValidException e) {
|
||
String msg = e.getBindingResult().getFieldErrors().stream()
|
||
.map(f -> f.getField() + ": " + f.getDefaultMessage())
|
||
.collect(Collectors.joining("; "));
|
||
return R.fail(40001, msg);
|
||
}
|
||
|
||
@ExceptionHandler(Exception.class)
|
||
public R<Void> handleException(Exception e) {
|
||
log.error("Unhandled exception", e);
|
||
return R.fail(50001, "服务器内部错误");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 前端错误处理策略
|
||
|
||
| 场景 | 处理方式 |
|
||
|------|----------|
|
||
| 网络超时(5秒) | 显示"网络请求超时,请检查网络连接" |
|
||
| HTTP 400 | 显示后端返回的具体校验错误信息 |
|
||
| HTTP 404 | 显示"请求的资源不存在" |
|
||
| HTTP 409 | 显示后端返回的冲突描述(如"SN号已存在") |
|
||
| HTTP 500 | 显示"服务器异常,请稍后重试" |
|
||
| 请求进行中 | 页面显示 loading 骨架屏或 spinner |
|
||
|
||
## 测试策略
|
||
|
||
### 后端测试
|
||
|
||
#### 单元测试
|
||
- 框架:JUnit 5 + Mockito
|
||
- 覆盖范围:
|
||
- Application Service 层的业务逻辑
|
||
- Domain Entity 的状态转换逻辑
|
||
- 参数校验逻辑
|
||
- 异常处理逻辑
|
||
|
||
#### 集成测试
|
||
- 框架:Spring Boot Test + TestContainers(PostgreSQL)
|
||
- 覆盖范围:
|
||
- Controller 层 API 端点的请求/响应格式
|
||
- MyBatis-Plus Mapper 的 CRUD 操作
|
||
- 审计字段自动填充
|
||
- 逻辑删除行为
|
||
- 分页查询
|
||
|
||
#### 属性测试
|
||
- 框架:jqwik(Java 属性测试库)
|
||
- 最少 100 次迭代
|
||
- 每个属性测试需引用设计文档中的属性编号
|
||
- 标签格式:`Feature: frontend-backend-separation, Property {number}: {property_text}`
|
||
- 覆盖范围:
|
||
- Property 1: 统一响应格式(生成随机数据类型,验证序列化格式)
|
||
- Property 3: CRUD 往返一致性(生成随机实体数据,验证创建-查询一致性)
|
||
- Property 4: 唯一性约束(生成随机唯一标识,验证重复创建返回 409)
|
||
- Property 5: 筛选正确性(生成随机筛选参数,验证返回结果匹配)
|
||
- Property 8: 分页一致性(生成随机 page/pageSize,验证响应格式)
|
||
- Property 10: 参数校验(生成随机无效参数,验证返回 400)
|
||
- Property 11: 校准文件上传-下载往返(生成随机文件内容和SN号,验证上传后下载内容一致、元数据正确)
|
||
- Property 12: 校准文件列表完整性(生成随机数量的校准文件上传,验证列表返回数量和排序正确)
|
||
|
||
### 前端测试
|
||
|
||
#### 单元测试
|
||
- 框架:Vitest + React Testing Library
|
||
- 覆盖范围:
|
||
- API 客户端的请求拦截和错误处理
|
||
- 各 API 服务文件的请求参数构造
|
||
- 页面组件的加载状态和错误状态渲染
|
||
- 板卡详情抽屉中校准文件列表的渲染和下载按钮交互
|
||
|
||
#### 集成测试
|
||
- 框架:Vitest + MSW(Mock Service Worker)
|
||
- 覆盖范围:
|
||
- 页面组件与 API 的完整交互流程
|
||
- 筛选、分页操作的数据刷新
|
||
- 表单提交与 API 调用
|
||
|