优化
This commit is contained in:
+24
@@ -10,6 +10,7 @@ import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.InboundMeasurementRequests;
|
||||
import com.baiyue.firstleg.dto.InboundMeasurementResponses;
|
||||
import com.baiyue.firstleg.dto.CartonMeasurementResult;
|
||||
import com.baiyue.firstleg.dto.CartonMeasurementCommand;
|
||||
import com.baiyue.firstleg.service.CartonMeasurementService;
|
||||
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
||||
import com.baiyue.firstleg.service.InboundMeasurementService;
|
||||
@@ -23,10 +24,13 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/** 平台后台入库测量录入、提交和 OCR 重量结果维护接口。 */
|
||||
@Tag(name = "平台入库测量", description = "维护测量草稿、箱规明细和可追溯 OCR 重量结果。")
|
||||
@@ -95,6 +99,26 @@ public class PlatformInboundMeasurementController {
|
||||
tenantId, inboundId, itemId, request.measurementVersion(), request.confirmedPackageCount()));
|
||||
}
|
||||
|
||||
@BusinessAudit(event = "INBOUND_CARTON_SCAN", resourceType = "INBOUND",
|
||||
resourceId = "#inboundId", description = "平台模拟设备单箱扫描")
|
||||
@Operation(summary = "平台模拟单箱设备扫描",
|
||||
description = "联调入口;data 中传箱号和设备测量值,image 传当前箱图片,复用正式单箱覆盖规则。")
|
||||
@PostMapping(value = "/measurement/carton-scans", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:inbound:measure:edit')")
|
||||
public ApiResponse<CartonMeasurementResult> simulateCartonScan(
|
||||
@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam long tenantId,
|
||||
@PathVariable long inboundId,
|
||||
@Valid @RequestPart("data") InboundMeasurementRequests.CartonScanRequest request,
|
||||
@RequestPart("image") MultipartFile image) {
|
||||
requirePlatformTenant(user, tenantId);
|
||||
CartonMeasurementCommand command = new CartonMeasurementCommand(
|
||||
request.boxCode(), request.length(), request.width(), request.height(), request.weight(),
|
||||
request.machineNo(), request.measuredAt(), image);
|
||||
return ApiResponse.success(cartonMeasurements.upsertForInbound(
|
||||
tenantId, user.userId(), inboundId, command));
|
||||
}
|
||||
|
||||
@BusinessAudit(event = "INBOUND_WEIGHT_OCR_RECORD", resourceType = "INBOUND",
|
||||
resourceId = "#inboundId", description = "登记称重图片 OCR 原值")
|
||||
@Operation(summary = "登记 OCR 重量结果", description = "按图片文件 ID 保存不可覆盖的 OCR 原值。")
|
||||
|
||||
@@ -13,6 +13,7 @@ import jakarta.validation.constraints.PositiveOrZero;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -62,14 +63,25 @@ public final class InboundMeasurementRequests {
|
||||
) {
|
||||
}
|
||||
|
||||
/** 平台联调使用;最终设备协议在外部适配层转换为相同领域命令。 */
|
||||
public record CartonScanRequest(
|
||||
@NotBlank @Size(max = 128) String boxCode,
|
||||
@NotNull @DecimalMin("0.001") @Digits(integer = 9, fraction = 3) BigDecimal length,
|
||||
@NotNull @DecimalMin("0.001") @Digits(integer = 9, fraction = 3) BigDecimal width,
|
||||
@NotNull @DecimalMin("0.001") @Digits(integer = 9, fraction = 3) BigDecimal height,
|
||||
@DecimalMin("0.001") @Digits(integer = 15, fraction = 3) BigDecimal weight,
|
||||
@NotBlank @Size(max = 64) String machineNo,
|
||||
@NotNull LocalDateTime measuredAt
|
||||
) {
|
||||
}
|
||||
|
||||
public record CreateWeightResultRequest(
|
||||
@NotNull @Positive Long fileId,
|
||||
@Size(max = 64) String ocrProvider,
|
||||
@Size(max = 1000) String ocrRawText,
|
||||
@DecimalMin("0.000") @Digits(integer = 15, fraction = 3) BigDecimal ocrWeightValue,
|
||||
@Size(max = 8) String ocrWeightUnit,
|
||||
@DecimalMin("0.00000") @DecimalMax("1.00000")
|
||||
@Digits(integer = 1, fraction = 5) BigDecimal confidence
|
||||
@DecimalMin("0.00000") @DecimalMax("1.00000") BigDecimal confidence
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -1,6 +1,7 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baiyue.firstleg.entity.InboundCartonLabelEntity;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundCartonLabelMapper;
|
||||
@@ -36,6 +37,15 @@ public class InboundCartonLabelRepository {
|
||||
.eq(InboundCartonLabelEntity::getBoxCode, boxCode));
|
||||
}
|
||||
|
||||
public boolean initializeOriginalPackageCount(long tenantId, long inboundId, int packageCount) {
|
||||
return inbounds.update(null, new LambdaUpdateWrapper<InboundOrderEntity>()
|
||||
.eq(InboundOrderEntity::getTenantId, tenantId)
|
||||
.eq(InboundOrderEntity::getId, inboundId)
|
||||
.isNull(InboundOrderEntity::getTotalPackageCount)
|
||||
.set(InboundOrderEntity::getTotalPackageCount, packageCount)
|
||||
.setSql("version = version + 1")) == 1;
|
||||
}
|
||||
|
||||
public void insert(InboundCartonLabelEntity label) {
|
||||
labels.insert(label);
|
||||
}
|
||||
|
||||
@@ -59,9 +59,25 @@ public class CartonMeasurementService {
|
||||
}
|
||||
|
||||
public CartonMeasurementResult upsert(long tenantId, long operatorId, CartonMeasurementCommand command) {
|
||||
return upsert(tenantId, operatorId, null, command);
|
||||
}
|
||||
|
||||
public CartonMeasurementResult upsertForInbound(long tenantId, long operatorId, long inboundId,
|
||||
CartonMeasurementCommand command) {
|
||||
if (inboundId <= 0) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "入库单ID必须大于0");
|
||||
}
|
||||
return upsert(tenantId, operatorId, inboundId, command);
|
||||
}
|
||||
|
||||
private CartonMeasurementResult upsert(long tenantId, long operatorId, Long expectedInboundId,
|
||||
CartonMeasurementCommand command) {
|
||||
validateCommand(command);
|
||||
String boxCode = command.boxCode().trim();
|
||||
CartonLabelBindingResolver.Binding binding = requireBinding(tenantId, boxCode);
|
||||
if (expectedInboundId != null && binding.inboundId() != expectedInboundId) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "箱号不属于当前入库单");
|
||||
}
|
||||
InboundOrderEntity inbound = requireDeviceWritableInbound(tenantId, binding.inboundId());
|
||||
MeasurementImageClient.UploadResult uploaded = uploadImage(
|
||||
tenantId, operatorId, inbound.getId(), command.image());
|
||||
|
||||
@@ -41,7 +41,11 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
|
||||
if (inbound == null) throw new BizException(ResultCode.NOT_FOUND, "入库单不存在");
|
||||
|
||||
List<InboundCartonLabelEntity> existing = new ArrayList<>(repository.findByInbound(tenantId, inboundId));
|
||||
if (!existing.isEmpty()) return batch(inbound, requireConsistent(existing));
|
||||
if (!existing.isEmpty()) {
|
||||
List<InboundCartonLabelEntity> consistent = requireConsistent(existing);
|
||||
freezeOriginalPackageCount(inbound, consistent.get(0).getTotalCartonCount());
|
||||
return batch(inbound, consistent);
|
||||
}
|
||||
if (!ALLOCATABLE_STATUSES.contains(inbound.getProcessStatus())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "当前入库单状态不允许首次生成箱号");
|
||||
}
|
||||
@@ -51,6 +55,7 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
|
||||
if (total < 1 || total > MAX_LABELS) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "唛头箱数必须为1至2000箱");
|
||||
}
|
||||
freezeOriginalPackageCount(inbound, total);
|
||||
requirePrintableInboundCode(inbound.getInboundCode(), total);
|
||||
|
||||
int cartonIndex = 0;
|
||||
@@ -146,6 +151,20 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void freezeOriginalPackageCount(InboundOrderEntity inbound, int total) {
|
||||
Integer original = inbound.getTotalPackageCount();
|
||||
if (original != null && original != total) {
|
||||
throw new BizException(ResultCode.CONFLICT, "订单总箱数与已冻结箱号数量不一致");
|
||||
}
|
||||
if (original == null) {
|
||||
if (!repository.initializeOriginalPackageCount(inbound.getTenantId(), inbound.getId(), total)) {
|
||||
throw new BizException(ResultCode.CONFLICT, "订单总箱数已变化,请刷新后重试");
|
||||
}
|
||||
inbound.setTotalPackageCount(total);
|
||||
inbound.setVersion(inbound.getVersion() == null ? 1 : inbound.getVersion() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private InboundCartonLabelEntity label(InboundOrderEntity inbound,
|
||||
OrderCargoClient.CargoLabelBinding cargo,
|
||||
int total, int index, LocalDateTime now) {
|
||||
|
||||
@@ -138,7 +138,7 @@ public class InboundMeasurementService {
|
||||
weight.setOcrRawText(request.ocrRawText());
|
||||
weight.setOcrWeightValue(request.ocrWeightValue());
|
||||
weight.setOcrWeightUnit(unit);
|
||||
weight.setConfidence(request.confidence());
|
||||
weight.setConfidence(normalizeConfidence(request.confidence()));
|
||||
weight.setRecognitionStatus(isPositive(request.ocrWeightValue()) && isSupportedUnit(unit)
|
||||
? WEIGHT_PENDING_CONFIRM : WEIGHT_NEEDS_MANUAL);
|
||||
weight.setCreatedBy(operatorId);
|
||||
@@ -356,6 +356,17 @@ public class InboundMeasurementService {
|
||||
return unit.trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private BigDecimal normalizeConfidence(BigDecimal confidence) {
|
||||
if (confidence == null) {
|
||||
return null;
|
||||
}
|
||||
if (confidence.compareTo(BigDecimal.ZERO) < 0
|
||||
|| confidence.compareTo(BigDecimal.ONE) > 0) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "OCR 置信度必须在 0 到 1 之间");
|
||||
}
|
||||
return confidence.setScale(5, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private boolean isSupportedUnit(String unit) {
|
||||
if (!StringUtils.hasText(unit)) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user