1、调整凭证
2、调整运单
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
# 运单导入 & 运输计划导入校验功能 - 完整实现总结
|
||||
|
||||
## 完成时间
|
||||
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`
|
||||
|
||||
```javascript
|
||||
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`
|
||||
|
||||
```javascript
|
||||
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()`
|
||||
|
||||
**新增导入**:
|
||||
```javascript
|
||||
import dayjs from 'dayjs';
|
||||
```
|
||||
|
||||
### 2.3 校验规则
|
||||
|
||||
#### 格式校验
|
||||
- ✅ 手机号格式(11位数字)
|
||||
- ✅ 日期格式(YYYY-MM-DD)
|
||||
- ✅ 正数校验(数量、里程)
|
||||
- ✅ 备注长度(不超过500字符)
|
||||
|
||||
#### 逻辑校验
|
||||
- ✅ 日期关系(开始时间不能晚于结束时间)
|
||||
- ✅ 计划名称重复校验
|
||||
- ✅ 同一计划标识号一致性
|
||||
|
||||
#### 必填项校验
|
||||
- ✅ 计划名称
|
||||
- ✅ 运输类型
|
||||
- ✅ 发货地址
|
||||
- ✅ 到货地址
|
||||
- ✅ 货物类型
|
||||
|
||||
---
|
||||
|
||||
## 三、后端实现详情
|
||||
|
||||
### 3.1 运单导入
|
||||
|
||||
#### 控制器
|
||||
**文件**: `WaybillController.java`
|
||||
|
||||
```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`
|
||||
|
||||
```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`
|
||||
|
||||
```java
|
||||
private String errorMessage; // 导入失败原因
|
||||
```
|
||||
|
||||
### 3.2 运输计划导入
|
||||
|
||||
#### 控制器
|
||||
**文件**: `TransportPlanController.java`
|
||||
|
||||
```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`
|
||||
|
||||
```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`
|
||||
|
||||
```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
|
||||
Reference in New Issue
Block a user