1、修复业务模块bug
2、完善凭证管理
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springblade.core.tenant.mp.TenantEntity;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证图片明细。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("blade_voucher_image")
|
||||||
|
@Schema(description = "凭证图片明细")
|
||||||
|
public class VoucherImage extends TenantEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long voucherId;
|
||||||
|
private String voucherBatchNo;
|
||||||
|
private Long waybillId;
|
||||||
|
private String waybillNo;
|
||||||
|
private String plateNo;
|
||||||
|
private String imageName;
|
||||||
|
private String objectKey;
|
||||||
|
private Integer matched;
|
||||||
|
}
|
||||||
+3
@@ -229,6 +229,9 @@ public class Waybill extends TenantEntity {
|
|||||||
@Schema(description = "过程节点")
|
@Schema(description = "过程节点")
|
||||||
private String processJson;
|
private String processJson;
|
||||||
|
|
||||||
|
@Schema(description = "路线信息")
|
||||||
|
private String routeJson;
|
||||||
|
|
||||||
@Schema(description = "费用信息")
|
@Schema(description = "费用信息")
|
||||||
private String freightJson;
|
private String freightJson;
|
||||||
|
|
||||||
|
|||||||
+3
@@ -62,5 +62,8 @@ public class ProcessConfigVO extends ProcessConfig {
|
|||||||
@Schema(description = "状态名称")
|
@Schema(description = "状态名称")
|
||||||
private String statusName;
|
private String statusName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@Schema(description = "当前运单是否有关联凭证")
|
||||||
|
private Boolean hasRelatedVoucher;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,14 @@
|
|||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-system-api</artifactId>
|
<artifactId>blade-system-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.minio</groupId>
|
||||||
|
<artifactId>minio</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.config;
|
||||||
|
|
||||||
|
import org.springframework.amqp.core.Binding;
|
||||||
|
import org.springframework.amqp.core.BindingBuilder;
|
||||||
|
import org.springframework.amqp.core.DirectExchange;
|
||||||
|
import org.springframework.amqp.core.Queue;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证导入消息队列配置。
|
||||||
|
* RabbitMQ 连接参数由 Nacos 的 spring.rabbitmq 配置提供。
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class VoucherImportRabbitConfig {
|
||||||
|
|
||||||
|
public static final String EXCHANGE = "tms.voucher.import.exchange";
|
||||||
|
public static final String QUEUE = "tms.voucher.import.queue";
|
||||||
|
public static final String ROUTING_KEY = "tms.voucher.import";
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DirectExchange voucherImportExchange() {
|
||||||
|
return new DirectExchange(EXCHANGE, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Queue voucherImportQueue() {
|
||||||
|
return new Queue(QUEUE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Binding voucherImportBinding(Queue voucherImportQueue, DirectExchange voucherImportExchange) {
|
||||||
|
return BindingBuilder.bind(voucherImportQueue).to(voucherImportExchange).with(ROUTING_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.config;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证图片 MinIO 客户端配置。
|
||||||
|
* 连接参数由 Nacos 的 file.storage.minio 配置提供。
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class VoucherMinioConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MinioClient voucherMinioClient(
|
||||||
|
@Value("${file.storage.minio.endpoint:${minio.endpoint:}}") String endpoint,
|
||||||
|
@Value("${file.storage.minio.access-key-id:${minio.access-key:}}") String accessKey,
|
||||||
|
@Value("${file.storage.minio.access-key-secret:${minio.secret-key:}}") String secretKey) {
|
||||||
|
return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
-4
@@ -24,11 +24,13 @@ package org.springblade.transport.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||||
|
import io.minio.GetPresignedObjectUrlArgs;
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.http.Method;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.springblade.core.boot.ctrl.BladeController;
|
import org.springblade.core.boot.ctrl.BladeController;
|
||||||
import org.springblade.core.excel.util.ExcelUtil;
|
import org.springblade.core.excel.util.ExcelUtil;
|
||||||
import org.springblade.core.mp.support.Condition;
|
import org.springblade.core.mp.support.Condition;
|
||||||
@@ -38,10 +40,13 @@ import org.springblade.core.tool.api.R;
|
|||||||
import org.springblade.core.tool.utils.DateUtil;
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.transport.excel.ProcessConfigExcel;
|
import org.springblade.transport.excel.ProcessConfigExcel;
|
||||||
|
import org.springblade.transport.mapper.VoucherImageMapper;
|
||||||
import org.springblade.transport.pojo.entity.ProcessConfig;
|
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||||
|
import org.springblade.transport.pojo.entity.VoucherImage;
|
||||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.ProcessConfigVO;
|
import org.springblade.transport.pojo.vo.ProcessConfigVO;
|
||||||
import org.springblade.transport.service.IProcessConfigService;
|
import org.springblade.transport.service.IProcessConfigService;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@@ -50,7 +55,10 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 过程配置 控制器
|
* 过程配置 控制器
|
||||||
@@ -58,19 +66,64 @@ import java.util.List;
|
|||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@AllArgsConstructor
|
|
||||||
@PreAuth(menu = "process_config")
|
@PreAuth(menu = "process_config")
|
||||||
@RequestMapping("/process-config")
|
@RequestMapping("/process-config")
|
||||||
@Tag(name = "过程配置", description = "过程配置")
|
@Tag(name = "过程配置", description = "过程配置")
|
||||||
public class ProcessConfigController extends BladeController {
|
public class ProcessConfigController extends BladeController {
|
||||||
|
|
||||||
private final IProcessConfigService processConfigService;
|
private final IProcessConfigService processConfigService;
|
||||||
|
private final VoucherImageMapper voucherImageMapper;
|
||||||
|
private final MinioClient minioClient;
|
||||||
|
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}")
|
||||||
|
private String minioBucketName;
|
||||||
|
|
||||||
|
public ProcessConfigController(IProcessConfigService processConfigService, VoucherImageMapper voucherImageMapper,
|
||||||
|
MinioClient minioClient) {
|
||||||
|
this.processConfigService = processConfigService;
|
||||||
|
this.voucherImageMapper = voucherImageMapper;
|
||||||
|
this.minioClient = minioClient;
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
@ApiOperationSupport(order = 1)
|
@ApiOperationSupport(order = 1)
|
||||||
@Operation(summary = "详情", description = "传入id")
|
@Operation(summary = "详情", description = "传入id")
|
||||||
public R<ProcessConfigVO> detail(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
public R<ProcessConfigVO> detail(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||||
return R.data(processConfigService.detail(id));
|
@Parameter(description = "运单主键") @RequestParam(required = false) Long waybillId) {
|
||||||
|
ProcessConfigVO detail = processConfigService.detail(id);
|
||||||
|
detail.setHasRelatedVoucher(waybillId != null && voucherImageMapper.selectCount(
|
||||||
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<VoucherImage>()
|
||||||
|
.eq(VoucherImage::getWaybillId, waybillId)
|
||||||
|
.eq(VoucherImage::getMatched, 1)) > 0);
|
||||||
|
return R.data(detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/voucher-images")
|
||||||
|
@ApiOperationSupport(order = 2)
|
||||||
|
@Operation(summary = "查询运单已关联凭证图片")
|
||||||
|
public R<List<Map<String, Object>>> voucherImages(
|
||||||
|
@Parameter(description = "运单主键", required = true) @RequestParam Long waybillId) {
|
||||||
|
List<Map<String, Object>> images = voucherImageMapper.selectList(
|
||||||
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<VoucherImage>()
|
||||||
|
.eq(VoucherImage::getWaybillId, waybillId)
|
||||||
|
.eq(VoucherImage::getMatched, 1)
|
||||||
|
.orderByDesc(VoucherImage::getCreateTime))
|
||||||
|
.stream().map(image -> {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("id", image.getId());
|
||||||
|
result.put("imageName", image.getImageName());
|
||||||
|
result.put("plateNo", image.getPlateNo());
|
||||||
|
result.put("waybillNo", image.getWaybillNo());
|
||||||
|
result.put("objectKey", image.getObjectKey());
|
||||||
|
try {
|
||||||
|
result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||||
|
.method(Method.GET).bucket(minioBucketName).object(image.getObjectKey())
|
||||||
|
.expiry(1, TimeUnit.HOURS).build()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("生成凭证图片预览地址失败", e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}).toList();
|
||||||
|
return R.data(images);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
|
|||||||
+7
@@ -65,6 +65,13 @@ public class VoucherManageController extends BladeController {
|
|||||||
return R.success("文件已更新");
|
return R.success("文件已更新");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/reprocess")
|
||||||
|
@Operation(summary = "重新处理已上传凭证")
|
||||||
|
public R reprocess(@RequestParam Long id) {
|
||||||
|
voucherManageService.reprocessUploadedVoucher(id);
|
||||||
|
return R.success("重新处理完成");
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/remove")
|
@PostMapping("/remove")
|
||||||
@ApiOperationSupport(order = 5)
|
@ApiOperationSupport(order = 5)
|
||||||
@Operation(summary = "删除凭证")
|
@Operation(summary = "删除凭证")
|
||||||
|
|||||||
+12
-5
@@ -177,36 +177,43 @@ public class WaybillController extends BladeController {
|
|||||||
return R.data(waybillService.copy(id));
|
return R.data(waybillService.copy(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/cancel")
|
@PostMapping("/change-route")
|
||||||
@ApiOperationSupport(order = 11)
|
@ApiOperationSupport(order = 11)
|
||||||
|
@Operation(summary = "变更运输路线", description = "传入运单路线与变更记录")
|
||||||
|
public R changeRoute(@RequestBody Waybill waybill) {
|
||||||
|
return R.status(waybillService.changeRoute(waybill));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/cancel")
|
||||||
|
@ApiOperationSupport(order = 12)
|
||||||
@Operation(summary = "取消", description = "传入id")
|
@Operation(summary = "取消", description = "传入id")
|
||||||
public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||||
return R.status(waybillService.cancel(id));
|
return R.status(waybillService.cancel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/reassign")
|
@PostMapping("/reassign")
|
||||||
@ApiOperationSupport(order = 12)
|
@ApiOperationSupport(order = 13)
|
||||||
@Operation(summary = "重新派单", description = "传入id")
|
@Operation(summary = "重新派单", description = "传入id")
|
||||||
public R reassign(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
public R reassign(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||||
return R.status(waybillService.reassign(id));
|
return R.status(waybillService.reassign(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/complete")
|
@PostMapping("/complete")
|
||||||
@ApiOperationSupport(order = 13)
|
@ApiOperationSupport(order = 14)
|
||||||
@Operation(summary = "完成", description = "传入id")
|
@Operation(summary = "完成", description = "传入id")
|
||||||
public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||||
return R.status(waybillService.complete(id));
|
return R.status(waybillService.complete(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/batch-complete")
|
@PostMapping("/batch-complete")
|
||||||
@ApiOperationSupport(order = 14)
|
@ApiOperationSupport(order = 15)
|
||||||
@Operation(summary = "批量完成", description = "传入ids")
|
@Operation(summary = "批量完成", description = "传入ids")
|
||||||
public R<BusinessRemoveResultVO> batchComplete(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
public R<BusinessRemoveResultVO> batchComplete(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||||
return R.data(waybillService.batchComplete(ids));
|
return R.data(waybillService.batchComplete(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/road-loading")
|
@PostMapping("/road-loading")
|
||||||
@ApiOperationSupport(order = 15)
|
@ApiOperationSupport(order = 16)
|
||||||
@Operation(summary = "公路配载", description = "传入ids")
|
@Operation(summary = "公路配载", description = "传入ids")
|
||||||
public R<LoadingManageVO> roadLoading(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
public R<LoadingManageVO> roadLoading(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||||
return R.data(waybillService.roadLoading(ids));
|
return R.data(waybillService.roadLoading(ids));
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.event;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证压缩包上传完成事件。
|
||||||
|
*/
|
||||||
|
public class VoucherUploadCompletedEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final Long voucherId;
|
||||||
|
|
||||||
|
public VoucherUploadCompletedEvent(Long voucherId) {
|
||||||
|
super(voucherId);
|
||||||
|
this.voucherId = voucherId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getVoucherId() {
|
||||||
|
return voucherId;
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.listener;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.transport.config.VoucherImportRabbitConfig;
|
||||||
|
import org.springblade.transport.service.IVoucherManageService;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证压缩包后台处理消费者。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class VoucherImportMessageListener {
|
||||||
|
|
||||||
|
private final IVoucherManageService voucherManageService;
|
||||||
|
|
||||||
|
@RabbitListener(queues = VoucherImportRabbitConfig.QUEUE)
|
||||||
|
public void processVoucher(Long voucherId) {
|
||||||
|
voucherManageService.processUploadedVoucher(voucherId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.listener;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.transport.config.VoucherImportRabbitConfig;
|
||||||
|
import org.springblade.transport.event.VoucherUploadCompletedEvent;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.event.TransactionPhase;
|
||||||
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证上传完成后投递后台处理消息。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class VoucherUploadCompletedListener {
|
||||||
|
|
||||||
|
private final RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
public void publish(VoucherUploadCompletedEvent event) {
|
||||||
|
rabbitTemplate.convertAndSend(VoucherImportRabbitConfig.EXCHANGE,
|
||||||
|
VoucherImportRabbitConfig.ROUTING_KEY, event.getVoucherId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springblade.transport.pojo.entity.VoucherImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证图片明细 Mapper。
|
||||||
|
*/
|
||||||
|
public interface VoucherImageMapper extends BaseMapper<VoucherImage> {
|
||||||
|
|
||||||
|
@Delete("DELETE FROM blade_voucher_image WHERE voucher_id = #{voucherId}")
|
||||||
|
void deleteByVoucherId(@Param("voucherId") Long voucherId);
|
||||||
|
}
|
||||||
+4
-3
@@ -2,9 +2,9 @@
|
|||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="org.springblade.transport.mapper.VoucherWaybillBatchMapper">
|
<mapper namespace="org.springblade.transport.mapper.VoucherWaybillBatchMapper">
|
||||||
<select id="selectVoucherWaybillBatchPage" resultType="java.util.LinkedHashMap">
|
<select id="selectVoucherWaybillBatchPage" resultType="java.util.LinkedHashMap">
|
||||||
SELECT MAX(w.create_time) AS createTime, MAX(w.create_user) AS createUser,
|
SELECT MIN(w.id) AS id, MAX(w.create_time) AS createTime, MAX(w.create_user) AS createUser,
|
||||||
MAX(u.real_name) AS createUserName, COUNT(*) AS waybillCount,
|
MAX(u.real_name) AS createUserName, COUNT(*) AS waybillCount,
|
||||||
COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo, GROUP_CONCAT(w.id ORDER BY w.create_time DESC) AS waybillIds
|
COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo
|
||||||
FROM blade_waybill w
|
FROM blade_waybill w
|
||||||
LEFT JOIN blade_user u ON u.id = w.create_user
|
LEFT JOIN blade_user u ON u.id = w.create_user
|
||||||
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId}
|
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId}
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
ORDER BY createTime DESC
|
ORDER BY createTime DESC
|
||||||
</select>
|
</select>
|
||||||
<select id="selectWaybillBatchesByIds" resultType="java.util.LinkedHashMap">
|
<select id="selectWaybillBatchesByIds" resultType="java.util.LinkedHashMap">
|
||||||
SELECT w.id, COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo, 1 AS waybillCount
|
SELECT MIN(w.id) AS id, COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo, COUNT(*) AS waybillCount
|
||||||
FROM blade_waybill w
|
FROM blade_waybill w
|
||||||
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId} AND w.id IN
|
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId} AND w.id IN
|
||||||
<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
|
<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||||
|
GROUP BY COALESCE(NULLIF(w.batch_no, ''), '未分批运单')
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
+2
@@ -17,6 +17,8 @@ public interface IVoucherManageService extends BaseService<VoucherManage> {
|
|||||||
void submit(VoucherManageSubmitRequest request);
|
void submit(VoucherManageSubmitRequest request);
|
||||||
VoucherManage createUploadDraft(VoucherUploadDraftRequest request);
|
VoucherManage createUploadDraft(VoucherUploadDraftRequest request);
|
||||||
void completeUploadFile(VoucherFileCompleteRequest request);
|
void completeUploadFile(VoucherFileCompleteRequest request);
|
||||||
|
void processUploadedVoucher(Long voucherId);
|
||||||
|
void reprocessUploadedVoucher(Long voucherId);
|
||||||
void removeVoucher(Long id);
|
void removeVoucher(Long id);
|
||||||
IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd);
|
IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -46,6 +46,7 @@ public interface IWaybillService extends BaseService<Waybill> {
|
|||||||
List<WaybillExcel> exportWaybill(WaybillVO waybill, String ids);
|
List<WaybillExcel> exportWaybill(WaybillVO waybill, String ids);
|
||||||
List<WaybillExcel> importWaybill(List<WaybillExcel> data);
|
List<WaybillExcel> importWaybill(List<WaybillExcel> data);
|
||||||
WaybillVO copy(Long id);
|
WaybillVO copy(Long id);
|
||||||
|
boolean changeRoute(Waybill waybill);
|
||||||
boolean cancel(Long id);
|
boolean cancel(Long id);
|
||||||
boolean reassign(Long id);
|
boolean reassign(Long id);
|
||||||
boolean complete(Long id);
|
boolean complete(Long id);
|
||||||
|
|||||||
+228
-3
@@ -6,38 +6,76 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.transport.mapper.VoucherManageMapper;
|
import org.springblade.transport.mapper.VoucherManageMapper;
|
||||||
|
import org.springblade.transport.mapper.VoucherImageMapper;
|
||||||
import org.springblade.transport.mapper.VoucherWaybillBatchMapper;
|
import org.springblade.transport.mapper.VoucherWaybillBatchMapper;
|
||||||
|
import org.springblade.transport.event.VoucherUploadCompletedEvent;
|
||||||
import org.springblade.transport.pojo.dto.VoucherManageSubmitRequest;
|
import org.springblade.transport.pojo.dto.VoucherManageSubmitRequest;
|
||||||
import org.springblade.transport.pojo.dto.VoucherUploadDraftRequest;
|
import org.springblade.transport.pojo.dto.VoucherUploadDraftRequest;
|
||||||
import org.springblade.transport.pojo.dto.VoucherFileCompleteRequest;
|
import org.springblade.transport.pojo.dto.VoucherFileCompleteRequest;
|
||||||
import org.springblade.transport.pojo.entity.VoucherManage;
|
import org.springblade.transport.pojo.entity.VoucherManage;
|
||||||
import org.springblade.transport.pojo.entity.VoucherWaybillBatch;
|
import org.springblade.transport.pojo.entity.VoucherWaybillBatch;
|
||||||
|
import org.springblade.transport.pojo.entity.VoucherImage;
|
||||||
|
import org.springblade.transport.pojo.entity.Waybill;
|
||||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||||
import org.springblade.transport.pojo.vo.VoucherManageVO;
|
import org.springblade.transport.pojo.vo.VoucherManageVO;
|
||||||
import org.springblade.transport.service.IVoucherManageService;
|
import org.springblade.transport.service.IVoucherManageService;
|
||||||
import org.springblade.transport.service.IProjectApplyService;
|
import org.springblade.transport.service.IProjectApplyService;
|
||||||
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.wrapper.VoucherManageWrapper;
|
import org.springblade.transport.wrapper.VoucherManageWrapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.PutObjectArgs;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMapper, VoucherManage> implements IVoucherManageService {
|
public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMapper, VoucherManage> implements IVoucherManageService {
|
||||||
|
|
||||||
private final VoucherWaybillBatchMapper voucherWaybillBatchMapper;
|
private final VoucherWaybillBatchMapper voucherWaybillBatchMapper;
|
||||||
private final IProjectApplyService projectApplyService;
|
private final IProjectApplyService projectApplyService;
|
||||||
|
private final IWaybillService waybillService;
|
||||||
|
private final VoucherImageMapper voucherImageMapper;
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
private final MinioClient minioClient;
|
||||||
|
private final String minioBucketName;
|
||||||
|
private final String minioRootDirectory;
|
||||||
|
|
||||||
public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService) {
|
public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService,
|
||||||
|
IWaybillService waybillService,
|
||||||
|
VoucherImageMapper voucherImageMapper, ApplicationEventPublisher eventPublisher, MinioClient minioClient,
|
||||||
|
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}") String minioBucketName,
|
||||||
|
@Value("${file.storage.minio.root-directory:${minio.root-directory:}}") String minioRootDirectory) {
|
||||||
this.voucherWaybillBatchMapper = voucherWaybillBatchMapper;
|
this.voucherWaybillBatchMapper = voucherWaybillBatchMapper;
|
||||||
this.projectApplyService = projectApplyService;
|
this.projectApplyService = projectApplyService;
|
||||||
|
this.waybillService = waybillService;
|
||||||
|
this.voucherImageMapper = voucherImageMapper;
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
this.minioClient = minioClient;
|
||||||
|
this.minioBucketName = minioBucketName;
|
||||||
|
this.minioRootDirectory = minioRootDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,10 +152,11 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
}
|
}
|
||||||
relations.forEach(voucherWaybillBatchMapper::insert);
|
relations.forEach(voucherWaybillBatchMapper::insert);
|
||||||
voucher.setWaybillBatchNo(relations.stream().map(VoucherWaybillBatch::getWaybillBatchNo).distinct().collect(Collectors.joining(",")));
|
voucher.setWaybillBatchNo(relations.stream().map(VoucherWaybillBatch::getWaybillBatchNo).distinct().collect(Collectors.joining(",")));
|
||||||
int total = relations.stream().mapToInt(VoucherWaybillBatch::getWaybillCount).sum();
|
voucher.setRelatedWaybillCount(0);
|
||||||
voucher.setRelatedWaybillCount(total);
|
|
||||||
voucher.setUnRelatedWaybillCount(0);
|
voucher.setUnRelatedWaybillCount(0);
|
||||||
|
voucher.setProcessStatus("处理中");
|
||||||
updateById(voucher);
|
updateById(voucher);
|
||||||
|
eventPublisher.publishEvent(new VoucherUploadCompletedEvent(voucher.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,9 +172,96 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
voucher.setFileTaskId(request.getFileTaskId());
|
voucher.setFileTaskId(request.getFileTaskId());
|
||||||
voucher.setFileName(request.getFileName());
|
voucher.setFileName(request.getFileName());
|
||||||
voucher.setFileUrl(request.getFileUrl());
|
voucher.setFileUrl(request.getFileUrl());
|
||||||
|
voucher.setProcessStatus("处理中");
|
||||||
updateById(voucher);
|
updateById(voucher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void processUploadedVoucher(Long voucherId) {
|
||||||
|
processUploadedVoucher(voucherId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void reprocessUploadedVoucher(Long voucherId) {
|
||||||
|
processUploadedVoucher(voucherId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processUploadedVoucher(Long voucherId, boolean force) {
|
||||||
|
VoucherManage voucher = getById(voucherId);
|
||||||
|
if (voucher == null || Objects.equals(voucher.getIsDeleted(), 1)) {
|
||||||
|
log.warn("凭证解压任务不存在,忽略消息 voucherId:{}", voucherId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!force && "处理完成".equals(voucher.getProcessStatus())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
validateMinioConfig();
|
||||||
|
List<Waybill> waybills = listRelatedWaybills(voucher);
|
||||||
|
Map<String, Waybill> waybillByPlate = new HashMap<>();
|
||||||
|
for (Waybill waybill : waybills) {
|
||||||
|
waybillPlateNumbers(waybill).forEach(plateNo -> waybillByPlate.putIfAbsent(plateNo, waybill));
|
||||||
|
}
|
||||||
|
voucherImageMapper.deleteByVoucherId(voucher.getId());
|
||||||
|
int imageCount = 0;
|
||||||
|
Set<Long> relatedWaybillIds = new HashSet<>();
|
||||||
|
try (InputStream source = openSourceFile(voucher.getFileUrl());
|
||||||
|
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(source), StandardCharsets.UTF_8)) {
|
||||||
|
ZipEntry entry;
|
||||||
|
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] pathParts = entry.getName().replace('\\', '/').split("/");
|
||||||
|
if (pathParts.length < 2 || !isImageFile(pathParts[pathParts.length - 1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String plateNo = normalizePlateNo(pathParts[0]);
|
||||||
|
String imageName = safeFileName(pathParts[pathParts.length - 1]);
|
||||||
|
if (Func.isEmpty(plateNo) || Func.isEmpty(imageName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Waybill waybill = waybillByPlate.get(plateNo);
|
||||||
|
String waybillNo = waybill == null ? "unmatched" : safePathPart(waybill.getWaybillNo());
|
||||||
|
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, imageName);
|
||||||
|
minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||||
|
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024).build());
|
||||||
|
VoucherImage image = new VoucherImage();
|
||||||
|
image.setVoucherId(voucher.getId());
|
||||||
|
image.setVoucherBatchNo(voucher.getVoucherBatchNo());
|
||||||
|
image.setWaybillId(waybill == null ? null : waybill.getId());
|
||||||
|
image.setWaybillNo(waybill == null ? null : waybill.getWaybillNo());
|
||||||
|
image.setPlateNo(plateNo);
|
||||||
|
image.setImageName(imageName);
|
||||||
|
image.setObjectKey(objectKey);
|
||||||
|
image.setMatched(waybill == null ? 0 : 1);
|
||||||
|
image.setTenantId(voucher.getTenantId());
|
||||||
|
voucherImageMapper.insert(image);
|
||||||
|
imageCount++;
|
||||||
|
if (waybill != null) {
|
||||||
|
relatedWaybillIds.add(waybill.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VoucherManage update = new VoucherManage();
|
||||||
|
update.setId(voucher.getId());
|
||||||
|
update.setVoucherCount(imageCount);
|
||||||
|
update.setRelatedWaybillCount(relatedWaybillIds.size());
|
||||||
|
update.setUnRelatedWaybillCount(Math.max(waybills.size() - relatedWaybillIds.size(), 0));
|
||||||
|
update.setProcessStatus("处理完成");
|
||||||
|
updateById(update);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
VoucherManage update = new VoucherManage();
|
||||||
|
update.setId(voucher.getId());
|
||||||
|
update.setProcessStatus("处理失败");
|
||||||
|
updateById(update);
|
||||||
|
log.error("凭证压缩包处理失败 voucherId:{}", voucherId, exception);
|
||||||
|
throw new ServiceException("凭证压缩包处理失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void removeVoucher(Long id) {
|
public void removeVoucher(Long id) {
|
||||||
@@ -155,6 +281,105 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
return voucherWaybillBatchMapper.selectWaybillBatchesByIds(AuthUtil.getTenantId(), ids);
|
return voucherWaybillBatchMapper.selectWaybillBatchesByIds(AuthUtil.getTenantId(), ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Waybill> listRelatedWaybills(VoucherManage voucher) {
|
||||||
|
LambdaQueryWrapper<Waybill> query = Wrappers.<Waybill>lambdaQuery()
|
||||||
|
.eq(Waybill::getTenantId, voucher.getTenantId())
|
||||||
|
.eq(Waybill::getIsDeleted, 0);
|
||||||
|
List<String> batchNos = voucherWaybillBatchMapper.selectList(Wrappers.<VoucherWaybillBatch>lambdaQuery()
|
||||||
|
.eq(VoucherWaybillBatch::getVoucherId, voucher.getId()))
|
||||||
|
.stream().map(VoucherWaybillBatch::getWaybillBatchNo).filter(Func::isNotEmpty).distinct().toList();
|
||||||
|
if (Func.isEmpty(batchNos)) return List.of();
|
||||||
|
List<String> normalBatchNos = batchNos.stream().filter(batchNo -> !"未分批运单".equals(batchNo)).toList();
|
||||||
|
boolean containsUnbatchedWaybills = batchNos.contains("未分批运单");
|
||||||
|
query.and(wrapper -> {
|
||||||
|
if (Func.isNotEmpty(normalBatchNos)) {
|
||||||
|
wrapper.in(Waybill::getBatchNo, normalBatchNos);
|
||||||
|
}
|
||||||
|
if (containsUnbatchedWaybills) {
|
||||||
|
if (Func.isNotEmpty(normalBatchNos)) {
|
||||||
|
wrapper.or();
|
||||||
|
}
|
||||||
|
wrapper.isNull(Waybill::getBatchNo).or().eq(Waybill::getBatchNo, "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return waybillService.list(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream openSourceFile(String fileUrl) throws Exception {
|
||||||
|
URLConnection connection = new java.net.URL(fileUrl).openConnection();
|
||||||
|
connection.setConnectTimeout(30_000);
|
||||||
|
connection.setReadTimeout(300_000);
|
||||||
|
return connection.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateMinioConfig() {
|
||||||
|
if (Func.isEmpty(minioBucketName)) {
|
||||||
|
throw new ServiceException("Nacos 未配置 file.storage.minio.bucket-name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildObjectKey(Long voucherId, String waybillNo, String plateNo, String imageName) {
|
||||||
|
String objectKey = voucherId + "/" + waybillNo + "/" + safePathPart(plateNo) + "/" + imageName;
|
||||||
|
if (Func.isEmpty(minioRootDirectory)) {
|
||||||
|
return objectKey;
|
||||||
|
}
|
||||||
|
return minioRootDirectory.endsWith("/") ? minioRootDirectory + objectKey : minioRootDirectory + "/" + objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizePlateNo(String plateNo) {
|
||||||
|
return plateNo == null ? null : plateNo.replaceAll("[\\s-]", "").toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> waybillPlateNumbers(Waybill waybill) {
|
||||||
|
Set<String> plateNumbers = new HashSet<>();
|
||||||
|
addPlateNumber(plateNumbers, waybill.getVehicleNo());
|
||||||
|
addPlateNumber(plateNumbers, waybill.getTrailerVehicleNo());
|
||||||
|
addPlateNumbersFromJson(plateNumbers, waybill.getCarrierJson(), waybill.getId());
|
||||||
|
addPlateNumbersFromJson(plateNumbers, waybill.getTaskInfoJson(), waybill.getId());
|
||||||
|
return plateNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPlateNumbersFromJson(Set<String> plateNumbers, String json, Long waybillId) {
|
||||||
|
if (Func.isEmpty(json)) return;
|
||||||
|
try {
|
||||||
|
Object parsed = JsonUtil.parse(json, Object.class);
|
||||||
|
if (parsed instanceof Map<?, ?> map) {
|
||||||
|
addPlateNumber(plateNumbers, String.valueOf(map.get("vehicleNo")));
|
||||||
|
addPlateNumber(plateNumbers, String.valueOf(map.get("trailerVehicleNo")));
|
||||||
|
} else if (parsed instanceof List<?> rows) {
|
||||||
|
for (Object row : rows) {
|
||||||
|
if (row instanceof Map<?, ?> map) {
|
||||||
|
addPlateNumber(plateNumbers, String.valueOf(map.get("vehicleNo")));
|
||||||
|
addPlateNumber(plateNumbers, String.valueOf(map.get("trailerVehicleNo")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.warn("运单车牌信息解析失败 waybillId:{}", waybillId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPlateNumber(Set<String> plateNumbers, String plateNo) {
|
||||||
|
String normalizedPlateNo = normalizePlateNo(plateNo);
|
||||||
|
if (Func.isNotEmpty(normalizedPlateNo) && !"NULL".equals(normalizedPlateNo)) {
|
||||||
|
plateNumbers.add(normalizedPlateNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String safePathPart(String value) {
|
||||||
|
return value == null ? "" : value.replaceAll("[^0-9A-Za-z\\u4e00-\\u9fa5_-]", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String safeFileName(String value) {
|
||||||
|
return value == null ? "" : safePathPart(value.replaceFirst("(?s)^.*[/\\\\]", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isImageFile(String fileName) {
|
||||||
|
String lowerName = fileName.toLowerCase();
|
||||||
|
return lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg") || lowerName.endsWith(".png")
|
||||||
|
|| lowerName.endsWith(".bmp") || lowerName.endsWith(".webp");
|
||||||
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<VoucherManage> buildQuery(VoucherManageVO query) {
|
private LambdaQueryWrapper<VoucherManage> buildQuery(VoucherManageVO query) {
|
||||||
LambdaQueryWrapper<VoucherManage> wrapper = Wrappers.<VoucherManage>lambdaQuery().eq(VoucherManage::getIsDeleted, 0)
|
LambdaQueryWrapper<VoucherManage> wrapper = Wrappers.<VoucherManage>lambdaQuery().eq(VoucherManage::getIsDeleted, 0)
|
||||||
.like(Func.isNotEmpty(query.getVoucherBatchNo()), VoucherManage::getVoucherBatchNo, query.getVoucherBatchNo())
|
.like(Func.isNotEmpty(query.getVoucherBatchNo()), VoucherManage::getVoucherBatchNo, query.getVoucherBatchNo())
|
||||||
|
|||||||
+21
@@ -224,6 +224,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
target.setCarrierJson(source.getCarrierJson());
|
target.setCarrierJson(source.getCarrierJson());
|
||||||
target.setTaskInfoJson(source.getTaskInfoJson());
|
target.setTaskInfoJson(source.getTaskInfoJson());
|
||||||
target.setProcessJson(source.getProcessJson());
|
target.setProcessJson(source.getProcessJson());
|
||||||
|
target.setRouteJson(source.getRouteJson());
|
||||||
target.setFreightJson(source.getFreightJson());
|
target.setFreightJson(source.getFreightJson());
|
||||||
target.setAttachmentsJson(source.getAttachmentsJson());
|
target.setAttachmentsJson(source.getAttachmentsJson());
|
||||||
target.setRemark(source.getRemark());
|
target.setRemark(source.getRemark());
|
||||||
@@ -235,6 +236,24 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
return detail(target.getId());
|
return detail(target.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean changeRoute(Waybill waybill) {
|
||||||
|
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
||||||
|
if ("completed".equals(oldRecord.getBusinessStatus()) || "cancelled".equals(oldRecord.getBusinessStatus())) {
|
||||||
|
throw new ServiceException("当前运单状态不允许变更运输路线");
|
||||||
|
}
|
||||||
|
oldRecord.setRouteJson(TransportBusinessSupport.trimToNull(waybill.getRouteJson()));
|
||||||
|
oldRecord.setDepartureAddress(TransportBusinessSupport.trimToNull(waybill.getDepartureAddress()));
|
||||||
|
oldRecord.setArrivalAddress(TransportBusinessSupport.trimToNull(waybill.getArrivalAddress()));
|
||||||
|
oldRecord.setTaskInfoJson(TransportBusinessSupport.trimToNull(waybill.getTaskInfoJson()));
|
||||||
|
TransportBusinessSupport.validateLength(oldRecord.getRouteJson(), 8000, "路线信息不能超过8000字");
|
||||||
|
TransportBusinessSupport.validateLength(oldRecord.getDepartureAddress(), 255, "发货地址不能超过255字");
|
||||||
|
TransportBusinessSupport.validateLength(oldRecord.getArrivalAddress(), 255, "收货地址不能超过255字");
|
||||||
|
TransportBusinessSupport.validateLength(oldRecord.getTaskInfoJson(), 8000, "任务信息不能超过8000字");
|
||||||
|
return updateById(oldRecord);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean cancel(Long id) {
|
public boolean cancel(Long id) {
|
||||||
@@ -470,6 +489,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
waybill.setCarrierJson(TransportBusinessSupport.trimToNull(waybill.getCarrierJson()));
|
waybill.setCarrierJson(TransportBusinessSupport.trimToNull(waybill.getCarrierJson()));
|
||||||
waybill.setTaskInfoJson(TransportBusinessSupport.trimToNull(waybill.getTaskInfoJson()));
|
waybill.setTaskInfoJson(TransportBusinessSupport.trimToNull(waybill.getTaskInfoJson()));
|
||||||
waybill.setProcessJson(TransportBusinessSupport.trimToNull(waybill.getProcessJson()));
|
waybill.setProcessJson(TransportBusinessSupport.trimToNull(waybill.getProcessJson()));
|
||||||
|
waybill.setRouteJson(TransportBusinessSupport.trimToNull(waybill.getRouteJson()));
|
||||||
waybill.setFreightJson(TransportBusinessSupport.trimToNull(waybill.getFreightJson()));
|
waybill.setFreightJson(TransportBusinessSupport.trimToNull(waybill.getFreightJson()));
|
||||||
waybill.setAttachmentsJson(TransportBusinessSupport.trimToNull(waybill.getAttachmentsJson()));
|
waybill.setAttachmentsJson(TransportBusinessSupport.trimToNull(waybill.getAttachmentsJson()));
|
||||||
waybill.setDeptName(TransportBusinessSupport.trimToNull(waybill.getDeptName()));
|
waybill.setDeptName(TransportBusinessSupport.trimToNull(waybill.getDeptName()));
|
||||||
@@ -562,6 +582,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
TransportBusinessSupport.validateLength(waybill.getCarrierJson(), 8000, "承运信息不能超过8000字");
|
TransportBusinessSupport.validateLength(waybill.getCarrierJson(), 8000, "承运信息不能超过8000字");
|
||||||
TransportBusinessSupport.validateLength(waybill.getTaskInfoJson(), 8000, "任务信息不能超过8000字");
|
TransportBusinessSupport.validateLength(waybill.getTaskInfoJson(), 8000, "任务信息不能超过8000字");
|
||||||
TransportBusinessSupport.validateLength(waybill.getProcessJson(), 8000, "过程节点不能超过8000字");
|
TransportBusinessSupport.validateLength(waybill.getProcessJson(), 8000, "过程节点不能超过8000字");
|
||||||
|
TransportBusinessSupport.validateLength(waybill.getRouteJson(), 8000, "路线信息不能超过8000字");
|
||||||
TransportBusinessSupport.validateLength(waybill.getFreightJson(), 8000, "费用信息不能超过8000字");
|
TransportBusinessSupport.validateLength(waybill.getFreightJson(), 8000, "费用信息不能超过8000字");
|
||||||
TransportBusinessSupport.validateLength(waybill.getAttachmentsJson(), 8000, "附件不能超过8000字");
|
TransportBusinessSupport.validateLength(waybill.getAttachmentsJson(), 8000, "附件不能超过8000字");
|
||||||
TransportBusinessSupport.validateLength(waybill.getDeptName(), 255, "所属组织不能超过255字");
|
TransportBusinessSupport.validateLength(waybill.getDeptName(), 255, "所属组织不能超过255字");
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `blade_voucher_image` (
|
||||||
|
`id` bigint NOT NULL,
|
||||||
|
`tenant_id` varchar(12) NOT NULL DEFAULT '000000' COMMENT '租户ID',
|
||||||
|
`voucher_id` bigint NOT NULL COMMENT '凭证批次ID',
|
||||||
|
`voucher_batch_no` varchar(30) NOT NULL COMMENT '凭证批次号',
|
||||||
|
`waybill_id` bigint DEFAULT NULL COMMENT '关联运单ID',
|
||||||
|
`waybill_no` varchar(64) DEFAULT NULL COMMENT '关联运单号',
|
||||||
|
`plate_no` varchar(32) NOT NULL COMMENT '车牌号',
|
||||||
|
`image_name` varchar(255) NOT NULL COMMENT '图片名称',
|
||||||
|
`object_key` varchar(1000) NOT NULL COMMENT 'MinIO对象路径',
|
||||||
|
`matched` tinyint NOT NULL DEFAULT 0 COMMENT '是否匹配运单:0否、1是',
|
||||||
|
`create_user` bigint DEFAULT NULL,
|
||||||
|
`create_dept` bigint DEFAULT NULL,
|
||||||
|
`create_time` datetime DEFAULT NULL,
|
||||||
|
`update_user` bigint DEFAULT NULL,
|
||||||
|
`update_time` datetime DEFAULT NULL,
|
||||||
|
`status` tinyint NOT NULL DEFAULT 1,
|
||||||
|
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_voucher_image_voucher` (`voucher_id`),
|
||||||
|
KEY `idx_voucher_image_waybill` (`waybill_id`),
|
||||||
|
KEY `idx_voucher_image_plate` (`tenant_id`, `plate_no`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='凭证图片明细';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `blade_waybill`
|
||||||
|
ADD COLUMN `route_json` text DEFAULT NULL COMMENT '路线信息' AFTER `process_json`;
|
||||||
Reference in New Issue
Block a user