bdafb83583
2、调整运单
10 KiB
10 KiB
运单与运输计划导入校验功能 - 最终完成报告
完成时间
2026-09-08 19:45
实现方式
两步校验机制:
- 选择文件后 → 立即调用校验接口(只校验不入库)
- 点击确认导入 → 调用确认接口(再次校验并入库)
一、已完成的工作
1.1 运单导入 (/business/waybill-import/form)
后端
- ✅ 添加校验接口
POST /import-batch/validate(只校验不入库) - ✅ 修改确认接口
POST /import-batch/confirm(校验并入库) - ✅ 实现完整的校验规则(格式、逻辑、必填项)
- ✅ 校验失败时导出错误明细 Excel
前端
- ✅ 添加
validateImportAPI - ✅ 文件上传后自动调用校验接口
- ✅ 根据响应 MIME 类型正确判断是否下载错误明细
- ✅ 校验失败自动下载错误明细表
1.2 运输计划导入 (/business/transport-plan/import)
后端
- ✅ 添加校验接口
POST /validate-transport-plan(只校验不入库) - ✅ 保留确认接口
POST /import-transport-plan(校验并入库) - ✅ 实现完整的校验规则
- ✅ 校验失败时导出错误明细 Excel
前端
- ✅ 添加
validateTransportPlanAPI - ✅ 文件上传后自动调用校验接口
- ✅ 根据响应 MIME 类型正确判断是否下载错误明细
- ✅ 校验失败自动下载错误明细表
二、关键问题修复
2.1 前端响应类型判断问题
问题描述
当 API 设置 responseType: 'blob' 时,axios 会将所有响应(包括 JSON)都当作 Blob 处理,导致无法正确判断校验结果。
解决方案
根据 Blob 的 MIME 类型判断实际内容:
const blob = response.data || response;
if (blob instanceof Blob) {
// Excel 文件
if (blob.type.includes('application/vnd.ms-excel') ||
blob.type.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
// 下载错误明细
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `运单导入失败明细_${dayjs().format('YYYYMMDDHHmmss')}.xlsx`;
link.click();
window.URL.revokeObjectURL(url);
ElMessage.warning('数据校验失败,已自动下载错误明细表');
}
// JSON 响应
else if (blob.type.includes('application/json')) {
const text = await blob.text();
const json = JSON.parse(text);
if (json.success) {
ElMessage.success('数据校验通过');
} else {
ElMessage.error(json.msg || '校验失败');
}
}
}
修改的文件
- ✅
src/views/business/components/waybill-import-dialog.vue - ✅
src/views/business/transport-plan-import.vue
2.2 后端导入语句顺序问题
问题描述
TransportPlanServiceImpl.java 中的导入语句顺序混乱,导致编译错误。
解决方案
重新整理导入语句,按照标准顺序:
- Java 标准库
- 第三方库
- 项目内部包
修改的文件
- ✅
TransportPlanServiceImpl.java- 重新整理了所有导入语句
三、用户使用流程
3.1 上传文件阶段
- 用户点击"添加附件"按钮
- 选择 Excel 文件
- 前端解析 Excel 并自动调用后端校验接口
- 后端校验结果:
- 校验通过:返回 JSON (
application/json),前端提示"数据校验通过" - 校验失败:返回 Excel 文件流 (
application/vnd.ms-excel),浏览器自动下载错误明细表
- 校验通过:返回 JSON (
3.2 确认导入阶段
- 用户查看预览数据,确认无误
- 点击"确认导入"按钮
- 前端调用确认接口
- 后端再次校验并入库:
- 校验通过:入库成功,返回 JSON,前端提示"导入成功"
- 校验失败:返回 Excel 文件流,浏览器自动下载错误明细表
3.3 错误明细 Excel 格式
- ✅ 在原始 Excel 最后一列追加"错误信息"列
- ✅ 错误信息以红色字体显示
- ✅ 校验通过的行显示空字符串
- ✅ 多个错误用 "; " 分隔
- ✅ 列宽自动调整
四、技术实现细节
4.1 后端接口
运单导入
// 校验接口
@PostMapping("/import-batch/validate")
public void validateImportBatch(@RequestBody WaybillImportBatchRequest request,
HttpServletResponse response) {
waybillImportBatchService.validate(request, response);
}
// 确认导入接口
@PostMapping("/import-batch/confirm")
public void confirmImportBatch(@RequestBody WaybillImportBatchRequest request,
HttpServletResponse response) {
waybillImportBatchService.confirm(request, response);
}
运输计划导入
// 校验接口
@PostMapping("/validate-transport-plan")
public R validateTransportPlan(MultipartFile file, @RequestParam Long projectId, ...,
HttpServletResponse response) {
List<TransportPlanImportExcel> failureList =
transportPlanService.validateTransportPlan(...);
if (Func.isNotEmpty(failureList)) {
ImportFailureExcelUtil.export(response, "运输计划导入失败明细" +
DateUtil.time(), "导入失败明细", failureList,
TransportPlanImportExcel.class);
return null;
}
return R.success("校验通过");
}
// 确认导入接口(已有方法,保持不变)
@PostMapping("/import-transport-plan")
public R importTransportPlan(MultipartFile file, @RequestParam Long projectId, ...,
HttpServletResponse response) {
List<TransportPlanImportExcel> failureList =
transportPlanService.importTransportPlan(...);
if (Func.isNotEmpty(failureList)) {
ImportFailureExcelUtil.export(response, "运输计划导入失败明细" +
DateUtil.time(), "导入失败明细", failureList,
TransportPlanImportExcel.class);
return null;
}
return R.success("导入数据成功");
}
4.2 前端 API
运单导入
export const validateImport = data => request({
url: `${baseUrl}/import-batch/validate`,
method: 'post',
data,
responseType: 'blob'
});
export const confirmImport = data => request({
url: `${baseUrl}/import-batch/confirm`,
method: 'post',
data
});
运输计划导入
export const validateTransportPlan = ({ file, projectId, ... }) => {
const data = new FormData();
data.append('file', file);
data.append('projectId', projectId || '');
// ...
return request({
url: `${baseUrl}/validate-transport-plan`,
method: 'post',
data,
responseType: 'blob',
timeout: 60000,
});
};
export const importTransportPlan = ({ file, projectId, ... }) => {
// 类似结构
};
4.3 响应类型判断
关键点:
- 设置
responseType: 'blob'后,所有响应都会被当作 Blob - 必须通过
blob.type(MIME 类型)来判断实际内容 - Excel 文件:
application/vnd.ms-excel或application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - JSON 响应:
application/json
五、编译验证
5.1 后端编译
✅ 编译成功 - mvn compile -DskipTests
问题修复:
- 修复了
TransportPlanServiceImpl.java的导入语句顺序问题
5.2 前端构建
✅ 构建成功 - pnpm run build
六、测试清单
运单导入测试
- 上传正确数据 → 校验通过 → 确认导入成功
- 上传错误数据 → 自动下载错误明细(红字显示错误)
- 修正后重新上传 → 校验通过 → 确认导入成功
- 车牌号格式错误 → 错误明细显示"车牌号格式错误"
- 必填项缺失 → 错误明细显示"XXX不能为空"
- 运费合计不匹配 → 错误明细显示"运费合计不匹配"
运输计划导入测试
- 上传正确数据 → 校验通过 → 确认导入成功
- 上传错误数据 → 自动下载错误明细(红字显示错误)
- 必填项缺失 → 错误明细显示相应错误
- 日期格式错误 → 错误明细显示相应错误
七、相关文档
已创建的文档
- 导入校验规则-后端实现文档.md(前端项目)
- 导入校验改造总结.md(前端项目)
- 运单导入校验-前后端对接完成总结.md(后端项目)
- 运单导入校验-最终实现总结.md(后端项目)
- 运单与运输计划导入校验-完整实现总结.md(后端项目)
- 本文档(最终完成报告)
八、改造前后对比
改造前
- ❌ 前端校验,规则可能不完整
- ❌ 只在确认导入时校验
- ❌ 校验失败时提示不够详细
- ❌ 前后端规则不一致
改造后
- ✅ 后端统一校验,规则完整
- ✅ 上传后立即校验 + 确认导入时再次校验
- ✅ 校验失败自动下载详细的错误明细表
- ✅ 前后端规则统一,易于维护
- ✅ 双重校验确保数据准确性
九、核心优势
- 及早发现问题 - 上传后立即校验,用户可以立即修正
- 详细的错误明细 - Excel 格式,红字显示错误,一目了然
- 双重校验 - 确保数据准确性
- 用户体验好 - 自动下载错误明细,无需手动操作
- 易于维护 - 校验规则集中在后端,前后端规则统一
十、总结
完成状态
✅ 全部完成
- 运单导入:前后端代码完成,编译通过
- 运输计划导入:前后端代码完成,编译通过
- 问题修复:响应类型判断、导入语句顺序
- 文档完善:创建了 6 份详细文档
核心改进
- 两步校验机制 - 上传后校验 + 确认导入时再次校验
- 智能响应判断 - 根据 MIME 类型判断是下载文件还是显示提示
- 详细错误明细 - Excel 格式,红字显示,易于修正
下一步
- 启动前后端服务进行联调测试
- 验证各种错误场景
- 收集用户反馈并优化
编写人: Claude Code
版本: 4.0(最终版)
最后更新: 2026-09-08 19:45
状态: ✅ 全部完成,编译通过,等待测试