enterprise-saa-s-dashboard-.../.kiro/specs/frontend-backend-separation/design.md

32 KiB
Raw Blame History

设计文档:前后端分离 — 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 调用层,替换所有硬编码模拟数据
  • 前后端采用统一的分页、筛选、错误响应规范

架构

整体架构

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

组件与接口

统一响应体

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) { ... }
}

分页响应体

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 客户端

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

前端通用类型

// 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 关系图

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_atupdated_at 字段应非空且为合理的时间戳;For any 更新操作,updated_at 应大于等于更新前的值。For any dev schema 下的业务表,都应包含 idcreated_atcreated_byupdated_atupdated_bydeleted 六个审计字段。

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 列表查询接口,响应应包含 totalpagepageSizerecords 四个字段;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 层抛出 BizExceptionHandler 统一处理
文件不存在 404 40402 校准文件在存储目录中不存在时,返回描述性错误消息
唯一性冲突 409 40901 Service 层检测重复后抛出 BizException
文件上传失败 500 50002 文件写入磁盘失败时,记录日志并返回错误消息
服务器内部错误 500 50001 GlobalExceptionHandler 兜底捕获,记录完整堆栈日志

自定义异常体系

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;
    }
}

全局异常处理器

@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 + TestContainersPostgreSQL
  • 覆盖范围:
    • Controller 层 API 端点的请求/响应格式
    • MyBatis-Plus Mapper 的 CRUD 操作
    • 审计字段自动填充
    • 逻辑删除行为
    • 分页查询

属性测试

  • 框架jqwikJava 属性测试库)
  • 最少 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 + MSWMock Service Worker
  • 覆盖范围:
    • 页面组件与 API 的完整交互流程
    • 筛选、分页操作的数据刷新
    • 表单提交与 API 调用