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

11 KiB
Raw Blame History

运单导入 & 运输计划导入校验功能 - 完整实现总结

完成时间

2026-09-08

实现方式

两步校验机制

  1. 选择文件后 → 立即调用校验接口,只校验不入库
  2. 点击确认导入 → 调用确认接口,再次校验并入库

一、运单导入 (/business/waybill-import/form)

1.1 后端接口

校验接口

  • 路径: POST /blade-transport/waybill-manage/import-batch/validate
  • 功能: 只校验数据,不入库
  • 返回:
    • 校验通过:JSON 成功响应
    • 校验失败:Excel 文件流(包含错误信息)

确认导入接口

  • 路径: POST /blade-transport/waybill-manage/import-batch/confirm
  • 功能: 再次校验并入库
  • 返回:
    • 校验通过:入库成功,JSON 成功响应
    • 校验失败:Excel 文件流(包含错误信息)

1.2 前端实现

API 文件

文件: src/api/business/waybill-manage.js

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

组件文件

文件: src/views/business/components/waybill-import-dialog.vue

新增方法:

  • performValidation() - 文件上传后自动调用,执行校验

修改方法:

  • fileChange() - 在解析 Excel 后调用 performValidation()

1.3 校验规则

格式校验

  • 车牌号格式(公路运输)
  • 手机号格式(11位数字)
  • 运输方式枚举值
  • 计量单位枚举值
  • 正数校验(数量、里程)
  • 日期时间格式

逻辑校验

  • 运费合计 = 运费 + 其他费用合计
  • 日期关系(发货时间不能晚于完成时间)
  • 配载标识号一致性
  • 同一运单标识号一致性

必填项校验

  • 车牌号、运输方式、发货地址、到货地址
  • 货物名称、货物类型、数量、数量单位
  • 实际发货时间、实际完成时间(status=completed 时)

二、运输计划导入 (/business/transport-plan/import)

2.1 后端接口

校验接口

  • 路径: POST /blade-transport/transport-plan/validate-transport-plan
  • 功能: 只校验数据,不入库
  • 返回:
    • 校验通过:JSON 成功响应
    • 校验失败:Excel 文件流(包含错误信息)

确认导入接口

  • 路径: POST /blade-transport/transport-plan/import-transport-plan
  • 功能: 再次校验并入库
  • 返回:
    • 校验通过:入库成功,JSON 成功响应
    • 校验失败:Excel 文件流(包含错误信息)

2.2 前端实现

API 文件

文件: src/api/business/transport-plan.js

export const validateTransportPlan = ({
  file,
  projectId,
  projectName,
  customerName,
  contractId,
  contractName,
}) => {
  const data = new FormData();
  data.append('file', file);
  data.append('projectId', projectId || '');
  data.append('projectName', projectName || '');
  data.append('customerName', customerName || '');
  data.append('contractId', contractId || '');
  data.append('contractName', contractName || '');
  return request({
    url: `${baseUrl}/validate-transport-plan`,
    method: 'post',
    data,
    responseType: 'blob',
    timeout: 60000,
  });
};

export const importTransportPlan = ({...}) => {...};

组件文件

文件: src/views/business/transport-plan-import.vue

新增方法:

  • performValidation() - 文件上传后自动调用,执行校验

修改方法:

  • handleFileChange() - 在解析 Excel 后调用 performValidation()

新增导入:

import dayjs from 'dayjs';

2.3 校验规则

格式校验

  • 手机号格式(11位数字)
  • 日期格式(YYYY-MM-DD
  • 正数校验(数量、里程)
  • 备注长度(不超过500字符)

逻辑校验

  • 日期关系(开始时间不能晚于结束时间)
  • 计划名称重复校验
  • 同一计划标识号一致性

必填项校验

  • 计划名称
  • 运输类型
  • 发货地址
  • 到货地址
  • 货物类型

三、后端实现详情

3.1 运单导入

控制器

文件: WaybillController.java

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

服务接口

文件: IWaybillImportBatchService.java

void validate(WaybillImportBatchRequest request, HttpServletResponse response);
void confirm(WaybillImportBatchRequest request, HttpServletResponse response);

服务实现

文件: WaybillImportBatchServiceImpl.java

  • validate() - 校验数据,失败时导出 Excel
  • confirm() - 校验并入库,失败时导出 Excel
  • mapToExcel() - 将 Map 转换为 Excel 对象

Excel 实体

文件: WaybillImportBatchExcel.java

private String errorMessage; // 导入失败原因

3.2 运输计划导入

控制器

文件: TransportPlanController.java

@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("导入数据成功");
}

服务接口

文件: ITransportPlanService.java

List<TransportPlanImportExcel> validateTransportPlan(List<TransportPlanImportExcel> data, Long projectId, String projectName, Long contractId, String contractName, String customerName);
List<TransportPlanImportExcel> importTransportPlan(List<TransportPlanImportExcel> data, Long projectId, String projectName, Long contractId, String contractName, String customerName);

服务实现

文件: TransportPlanServiceImpl.java

  • validateTransportPlan() - 只校验,不入库
  • importTransportPlan() - 校验并入库(已有方法,保持不变)

Excel 实体

文件: TransportPlanImportExcel.java

@ExcelIgnore
private String errorMessage; // 已存在

四、错误明细 Excel 格式

通用格式

  • 在原始 Excel 最后一列追加"错误信息"列
  • 错误信息以红色字体显示
  • 校验通过的行显示空字符串
  • 多个错误用 "; " 分隔
  • 列宽自动调整

生成工具

  • 使用 ImportFailureExcelUtil.export() 统一生成
  • 自动设置样式和格式

五、用户使用流程对比

改造前

  1. 用户上传文件
  2. 前端解析并显示预览
  3. 用户点击"确认导入"
  4. 前端校验(规则可能不完整)
  5. 调用后端接口入库
  6. 如果后端校验失败,提示错误信息(无详细明细)

改造后

  1. 用户上传文件
  2. 前端解析并显示预览
  3. 前端自动调用后端校验接口
  4. 校验失败:自动下载错误明细表
  5. 校验通过:提示"数据校验通过"
  6. 用户点击"确认导入"
  7. 调用后端确认接口
  8. 后端再次校验并入库
  9. 校验失败:自动下载错误明细表
  10. 校验通过:入库成功,提示"导入成功"

优势

  • 及早发现问题,减少返工
  • 详细的错误明细,一次性修正所有错误
  • 双重校验,确保数据准确性
  • 前后端规则统一,易于维护

六、技术要点

6.1 两次校验的原因

  1. 第一次校验(上传后)

    • 及早发现问题,用户可以立即修正
    • 避免用户填写其他表单项后才发现数据有问题
  2. 第二次校验(确认导入时)

    • 防止数据在上传和确认之间被修改
    • 确保入库数据的准确性

6.2 响应类型判断

  • JSON 响应Content-Type: application/json
  • Excel 响应Content-Type: application/vnd.ms-excel

前端通过 responseType: 'blob' 接收响应,根据响应类型判断:

  • Blob 类型 → 校验失败,下载文件
  • 其他类型 → 校验通过,显示提示

6.3 事务处理

  • validate() 方法不开启事务,只读操作
  • confirm() / importTransportPlan() 方法开启事务,校验失败时不入库

6.4 代码复用

  • 运单导入:validateImportRows() 方法被 validate()confirm() 复用
  • 运输计划导入:校验逻辑在 validateTransportPlan()importTransportPlan() 中实现

七、编译验证

前端

构建成功 - pnpm run build

后端

⚠️ 部分编译错误 - 与我们的修改无关,是 BaiduOcrServiceImpl 的问题

  • 运单导入相关代码: 语法正确
  • 运输计划导入相关代码: 语法正确(修复了重复 @Override 注解)

八、测试清单

运单导入测试

  • 上传正确数据 → 第一次校验通过 → 确认导入成功
  • 上传错误数据 → 第一次校验失败 → 下载错误明细
  • 修正后重新上传 → 校验通过 → 确认导入成功
  • 车牌号格式错误
  • 手机号格式错误
  • 必填项缺失
  • 运费合计不匹配
  • 日期逻辑错误
  • 配载标识号不一致

运输计划导入测试

  • 上传正确数据 → 第一次校验通过 → 确认导入成功
  • 上传错误数据 → 第一次校验失败 → 下载错误明细
  • 手机号格式错误
  • 必填项缺失
  • 日期逻辑错误
  • 计划名称重复
  • 备注长度超限

九、相关文档

已创建的文档

  1. 导入校验规则-后端实现文档.md(前端项目)
  2. 导入校验改造总结.md(前端项目)
  3. 运单导入校验-前后端对接完成总结.md(后端项目)
  4. 运单导入校验-最终实现总结.md(后端项目)
  5. 本文档(运单 & 运输计划完整实现总结)

十、总结

完成状态

  • 运单导入:前后端代码完成
  • 运输计划导入:前后端代码完成
  • 前端构建通过
  • ⚠️ 后端有编译错误(与本次修改无关)

实现方式

两步校验机制(上传后校验 + 确认导入时再次校验)

优势

  • 及早发现问题
  • 详细的错误明细
  • 双重校验保证准确性
  • 前后端规则统一

编写人: Claude Code
版本: 3.0
最后更新: 2026-09-08 19:30