Files
tms-erp-api/运单导入校验-最终实现总结.md
T
b2894lxlx bdafb83583 1、调整凭证
2、调整运单
2026-09-08 22:41:40 +08:00

413 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 运单导入校验功能 - 最终实现总结
## 完成时间
2026-09-08
## 实现方式
**两步校验机制**
1. **选择文件后** → 立即调用校验接口(`/import-batch/validate`),只校验不入库
2. **点击确认导入** → 调用确认接口(`/import-batch/confirm`),再次校验并入库
---
## 一、用户使用流程
### 1. 上传文件阶段
1. 用户点击"添加附件"按钮
2. 选择 Excel 文件
3. **前端自动调用校验接口** `POST /blade-transport/waybill-manage/import-batch/validate`
4. 后端返回结果:
- **校验通过**:返回 JSON,前端提示"数据校验通过"
- **校验失败**:返回 Excel 文件流,浏览器自动下载错误明细表,前端提示"数据校验失败,已自动下载错误明细表,请修正后重新上传"
### 2. 确认导入阶段
1. 用户查看预览数据,确认无误
2. 点击"确认导入"按钮
3. **前端调用确认接口** `POST /blade-transport/waybill-manage/import-batch/confirm`
4. 后端再次校验并入库:
- **校验通过**:入库成功,返回 JSON,前端提示"导入成功"
- **校验失败**:返回 Excel 文件流,浏览器自动下载错误明细表(防止数据在上传和确认之间被修改)
---
## 二、后端实现
### 1. 新增接口
#### 1.1 校验接口
**路径**: `POST /blade-transport/waybill-manage/import-batch/validate`
**功能**: 只校验数据,不入库
**返回**:
- 校验通过:JSON 成功响应
- 校验失败:Excel 文件流(包含错误信息)
**实现位置**:
- 控制器:`WaybillController.validateImportBatch()`
- 服务接口:`IWaybillImportBatchService.validate()`
- 服务实现:`WaybillImportBatchServiceImpl.validate()`
#### 1.2 确认导入接口(修改)
**路径**: `POST /blade-transport/waybill-manage/import-batch/confirm`
**功能**: 再次校验并入库
**返回**:
- 校验通过:入库成功,JSON 成功响应
- 校验失败:Excel 文件流(包含错误信息)
**实现位置**:
- 控制器:`WaybillController.confirmImportBatch()`
- 服务接口:`IWaybillImportBatchService.confirm()`
- 服务实现:`WaybillImportBatchServiceImpl.confirm()`
### 2. 修改的文件
#### 2.1 控制器
**文件**: `/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java`
```java
@PostMapping("/import-batch/validate")
@ApiOperationSupport(order = 6)
@Operation(summary = "校验运单批量导入数据")
public void validateImportBatch(@RequestBody WaybillImportBatchRequest request, HttpServletResponse response) {
waybillImportBatchService.validate(request, response);
}
@PostMapping("/import-batch/confirm")
@ApiOperationSupport(order = 7)
@Operation(summary = "确认运单批量导入")
public void confirmImportBatch(@RequestBody WaybillImportBatchRequest request, HttpServletResponse response) {
waybillImportBatchService.confirm(request, response);
}
```
#### 2.2 服务接口
**文件**: `/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IWaybillImportBatchService.java`
```java
public interface IWaybillImportBatchService extends BaseService<WaybillImportBatch> {
WaybillImportBatch saveDraft(WaybillImportBatchRequest request);
void validate(WaybillImportBatchRequest request, HttpServletResponse response);
void confirm(WaybillImportBatchRequest request, HttpServletResponse response);
IPage<WaybillImportBatchVO> page(IPage<WaybillImportBatch> page, WaybillImportBatchRequest request);
BusinessRemoveResultVO removeBatches(String ids);
}
```
#### 2.3 服务实现
**文件**: `/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java`
**新增方法**:
- `validate()` - 校验数据,不入库
- `mapToExcel()` - 将 Map 转换为 Excel 对象
**修改方法**:
- `confirm()` - 校验并入库
- `persist()` - 移除内部校验逻辑
#### 2.4 Excel 实体
**文件**: `/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillImportBatchExcel.java`
**新增字段**:
```java
/** 导入失败原因(不导出到模板,仅用于失败明细) */
private String errorMessage;
```
---
## 三、前端实现
### 1. 修改的文件
#### 1.1 API 文件
**文件**: `src/api/business/waybill-manage.js`
**新增接口**:
```javascript
export const validateImport = data => request({
url: `${baseUrl}/import-batch/validate`,
method: 'post',
data,
responseType: 'blob'
});
```
#### 1.2 组件文件
**文件**: `src/views/business/components/waybill-import-dialog.vue`
**新增方法**:
```javascript
const performValidation = async () => {
if (!rows.value.length) return;
try {
const response = await api.validateImport(buildImportPayload());
if (response instanceof Blob) {
// 校验失败,下载错误明细
const url = window.URL.createObjectURL(response);
const link = document.createElement('a');
link.href = url;
link.download = `运单导入失败明细_${dayjs().format('YYYYMMDDHHmmss')}.xlsx`;
link.click();
window.URL.revokeObjectURL(url);
ElMessage.warning('数据校验失败,已自动下载错误明细表,请修正后重新上传');
} else {
// 校验通过
ElMessage.success('数据校验通过');
}
} catch (error) {
console.error('校验失败:', error);
ElMessage.error('校验接口调用失败');
}
};
```
**修改方法**:
- `fileChange()` - 文件上传后调用 `performValidation()`
---
## 四、校验规则
### 已实现的校验规则
#### 4.1 格式校验
- ✅ 车牌号格式(公路运输:首位汉字 + 次位大写字母 + 7-8 位长度)
- ✅ 手机号格式(11 位数字)
- ✅ 运输方式枚举值
- ✅ 计量单位枚举值
- ✅ 正数校验(数量、里程)
- ✅ 日期时间格式(YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss
#### 4.2 逻辑校验
- ✅ 运费合计 = 运费 + 其他费用合计
- ✅ 日期关系(发货时间不能晚于完成时间)
- ✅ 配载标识号一致性(同一配载标识号的车牌号必须一致)
- ✅ 同一运单标识号一致性(同一运单标识号的车牌号必须一致)
#### 4.3 必填项校验
- ✅ 车牌号/航班号/船号/班列号
- ✅ 运输方式
- ✅ 发货地址
- ✅ 到货地址
- ✅ 货物名称
- ✅ 货物类型
- ✅ 数量
- ✅ 数量单位
- ✅ 实际发货时间(status=completed 时)
- ✅ 实际完成时间(status=completed 时)
### 错误明细 Excel 格式
- ✅ 在原始 Excel 最后一列追加"错误信息"列
- ✅ 错误信息以红色字体显示
- ✅ 校验通过的行显示空字符串
- ✅ 多个错误用 "; " 分隔
- ✅ 列宽自动调整
---
## 五、技术要点
### 5.1 两次校验的原因
1. **第一次校验(上传后)**
- 及早发现问题,用户可以立即修正
- 避免用户填写其他表单项后才发现数据有问题
2. **第二次校验(确认导入时)**
- 防止数据在上传和确认之间被修改
- 确保入库数据的准确性
### 5.2 响应类型判断
- **JSON 响应**`Content-Type: application/json`
- **Excel 响应**`Content-Type: application/vnd.ms-excel`
前端通过 `responseType: 'blob'` 接收响应,根据响应类型判断:
- `Blob` 类型 → 校验失败,下载文件
- 其他类型 → 校验通过,显示提示
### 5.3 事务处理
- `validate()` 方法**不开启事务**,只读操作
- `confirm()` 方法**开启事务**,校验失败时不入库,校验通过后才入库
### 5.4 性能优化
- 校验逻辑复用(`validateImportRows()` 方法)
- 使用 `TreeMap` 确保错误信息按行号排序
- 使用 `Map` 数据结构优化跨行校验性能
---
## 六、测试验证
### 6.1 编译验证
**后端编译成功** - `mvn clean compile -DskipTests`
**前端构建成功** - `pnpm run build`
### 6.2 待测试项
#### 功能测试
- [ ] 上传完全正确的数据
- 第一次校验提示"数据校验通过"
- 点击确认导入,提示"导入成功"
- [ ] 上传错误数据(如车牌号格式错误)
- 第一次校验自动下载错误明细
- 修正后重新上传,校验通过
- 点击确认导入,提示"导入成功"
- [ ] 上传混合数据(部分正确部分错误)
- 下载的错误明细最后一列显示错误信息(红色)
- 正确的行显示空字符串
#### 边界测试
- [ ] 上传空文件
- [ ] 上传非 Excel 文件
- [ ] 上传超大文件(>5000 行)
- [ ] 同一配载标识号车牌不一致
- [ ] 运费合计不匹配
#### 性能测试
- [ ] 上传 1000 行数据,校验响应时间
- [ ] 上传 5000 行数据,校验响应时间
- [ ] 并发上传测试
---
## 七、对比改造前后
### 改造前
- ❌ 前端校验,规则分散
- ❌ 前端生成错误 Excel
- ❌ 前后端规则不一致
- ❌ 只在确认导入时校验
### 改造后
- ✅ 后端校验,规则集中
- ✅ 后端生成错误 Excel
- ✅ 统一的校验规则
- ✅ 上传后立即校验 + 确认导入时再次校验
- ✅ 使用成熟的工具类(`ImportFailureExcelUtil`
---
## 八、后续工作
### 短期
1. **联调测试** - 启动前后端服务,完整流程测试
2. **Bug 修复** - 根据测试结果修复问题
3. **运输计划导入** - 实现类似的两步校验机制
### 长期
1. **校验规则配置化** - 将规则抽取为配置,便于维护
2. **地址库集成** - 实现真实的地址匹配校验
3. **性能优化** - 针对大文件导入进行优化
4. **国际化** - 支持多语言错误信息
---
## 九、相关文档
### 已创建的文档
1. **导入校验规则-后端实现文档.md**(前端项目)
2. **导入校验改造总结.md**(前端项目)
3. **运单导入校验-前后端对接完成总结.md**(后端项目)
4. **本文档**(最终实现总结)
### 参考实现
- **港口码头导入**: `/blade-system/port-terminal/import-port-terminal`
- **导入失败工具类**: `org.springblade.common.excel.ImportFailureExcelUtil`
---
## 十、API 接口文档
### 10.1 校验接口
#### 请求
```
POST /blade-transport/waybill-manage/import-batch/validate
Content-Type: application/json
{
"projectId": 123,
"contractId": 456,
"status": "completed",
"rows": [...]
}
```
#### 响应
**成功(校验通过)**:
```
HTTP/1.1 200 OK
Content-Type: application/json
{
"code": 200,
"success": true,
"msg": "校验通过"
}
```
**失败(校验不通过)**:
```
HTTP/1.1 200 OK
Content-Type: application/vnd.ms-excel
Content-Disposition: attachment; filename=运单导入失败明细20260908190730.xlsx
[Excel Binary Data]
```
### 10.2 确认导入接口
#### 请求
```
POST /blade-transport/waybill-manage/import-batch/confirm
Content-Type: application/json
{
"projectId": 123,
"contractId": 456,
"status": "completed",
"rows": [...]
}
```
#### 响应
**成功(导入成功)**:
```
HTTP/1.1 200 OK
Content-Type: application/json
{
"code": 200,
"success": true,
"msg": "操作成功"
}
```
**失败(校验不通过)**:
```
HTTP/1.1 200 OK
Content-Type: application/vnd.ms-excel
Content-Disposition: attachment; filename=运单导入失败明细20260908190730.xlsx
[Excel Binary Data]
```
---
**完成状态**: ✅ 前后端代码已完成,编译通过,等待联调测试
**实现方式**: 两步校验机制(上传后校验 + 确认导入时再次校验)
**编写人**: Claude Code
**版本**: 2.0
**最后更新**: 2026-09-08 19:10