优化
This commit is contained in:
+13
@@ -52,6 +52,19 @@ public class InboundCartonLabelRepository {
|
||||
.eq(InboundCartonLabelEntity::getBoxCode, boxCode));
|
||||
}
|
||||
|
||||
public List<InboundCartonLabelEntity> findGloballyByBoxCode(String boxCode) {
|
||||
return labels.selectList(new LambdaQueryWrapper<InboundCartonLabelEntity>()
|
||||
.eq(InboundCartonLabelEntity::getBoxCode, boxCode)
|
||||
.last("LIMIT 2"));
|
||||
}
|
||||
|
||||
public InboundOrderEntity findInbound(long tenantId, long inboundId) {
|
||||
return inbounds.selectOne(new LambdaQueryWrapper<InboundOrderEntity>()
|
||||
.eq(InboundOrderEntity::getTenantId, tenantId)
|
||||
.eq(InboundOrderEntity::getId, inboundId)
|
||||
.eq(InboundOrderEntity::getDeleted, false));
|
||||
}
|
||||
|
||||
public void initializeLegacyFingerprint(long tenantId, long inboundId, int version, String fingerprint) {
|
||||
labels.update(null, new LambdaUpdateWrapper<InboundCartonLabelEntity>()
|
||||
.eq(InboundCartonLabelEntity::getTenantId, tenantId)
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class MeasurementRequestAuthenticator {
|
||||
MeasurementOpenApiProperties.AppCredential credential = properties.getApps().get(headers.appKey());
|
||||
if (credential == null || !credential.isEnabled()
|
||||
|| properties.isSignatureRequired() && !StringUtils.hasText(credential.getSecret())
|
||||
|| credential.getTenantId() == null || credential.getTenantId() <= 0
|
||||
|| credential.getTenantId() == null || credential.getTenantId() < 0
|
||||
|| credential.getSubjectId() == null || credential.getSubjectId() <= 0) {
|
||||
throw unauthorized("应用凭据无效");
|
||||
}
|
||||
|
||||
@@ -154,9 +154,28 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
|
||||
@Override
|
||||
public CartonLabelBindingResolver.Binding resolveByBoxCode(long tenantId, String boxCode) {
|
||||
InboundCartonLabelEntity label = requireByBoxCode(tenantId, boxCode);
|
||||
return new CartonLabelBindingResolver.Binding(label.getInboundId(), label.getInboundDetailId(),
|
||||
label.getInboundCode(), label.getCartonIndex(), label.getTotalCartonCount(),
|
||||
label.getBoxSpecification());
|
||||
return binding(label);
|
||||
}
|
||||
|
||||
public PlatformBinding resolvePlatformByBoxCode(String boxCode) {
|
||||
if (!StringUtils.hasText(boxCode)) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "箱号不能为空");
|
||||
}
|
||||
List<InboundCartonLabelEntity> matches = repository.findGloballyByBoxCode(boxCode.trim());
|
||||
if (matches.isEmpty()) throw new BizException(ResultCode.NOT_FOUND, "箱号不存在");
|
||||
if (matches.size() != 1) {
|
||||
throw new BizException(ResultCode.CONFLICT, "箱号归属不唯一,禁止设备写入");
|
||||
}
|
||||
InboundCartonLabelEntity label = matches.get(0);
|
||||
if ("VOID".equals(label.getBatchStatus())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "箱码已失效,请重新打印");
|
||||
}
|
||||
InboundOrderEntity inbound = repository.findInbound(label.getTenantId(), label.getInboundId());
|
||||
if (inbound == null) throw new BizException(ResultCode.NOT_FOUND, "箱号关联的入库单不存在");
|
||||
if (inbound.getWarehouseId() == null || inbound.getWarehouseId() <= 0) {
|
||||
throw new BizException(ResultCode.CONFLICT, "入库单未绑定有效国内仓库");
|
||||
}
|
||||
return new PlatformBinding(label.getTenantId(), inbound.getWarehouseId(), binding(label));
|
||||
}
|
||||
|
||||
public InboundCartonLabels.Resolution resolveDetailsByBoxCode(long tenantId, String boxCode) {
|
||||
@@ -179,6 +198,16 @@ public class InboundCartonLabelService implements CartonLabelBindingResolver {
|
||||
return label;
|
||||
}
|
||||
|
||||
private CartonLabelBindingResolver.Binding binding(InboundCartonLabelEntity label) {
|
||||
return new CartonLabelBindingResolver.Binding(label.getInboundId(), label.getInboundDetailId(),
|
||||
label.getInboundCode(), label.getCartonIndex(), label.getTotalCartonCount(),
|
||||
label.getBoxSpecification());
|
||||
}
|
||||
|
||||
public record PlatformBinding(long tenantId, long warehouseId,
|
||||
CartonLabelBindingResolver.Binding binding) {
|
||||
}
|
||||
|
||||
private List<OrderCargoClient.CargoLabelBinding> loadCargoes(InboundOrderEntity inbound) {
|
||||
try {
|
||||
ApiResponse<List<OrderCargoClient.CargoLabelBinding>> response = orderCargoClient.listForCartonLabels(
|
||||
|
||||
+29
-2
@@ -48,6 +48,7 @@ public class MojiaMeasurementOpenApiService {
|
||||
private final Validator validator;
|
||||
private final MeasurementImageClient imageClient;
|
||||
private final MojiaMeasurementReceiptRepository receipts;
|
||||
private final InboundCartonLabelService cartonLabels;
|
||||
|
||||
@Value("${baiyue.security.internal-service-token}")
|
||||
private String internalToken;
|
||||
@@ -57,13 +58,15 @@ public class MojiaMeasurementOpenApiService {
|
||||
ObjectMapper objectMapper,
|
||||
Validator validator,
|
||||
MeasurementImageClient imageClient,
|
||||
MojiaMeasurementReceiptRepository receipts) {
|
||||
MojiaMeasurementReceiptRepository receipts,
|
||||
InboundCartonLabelService cartonLabels) {
|
||||
this.authenticator = authenticator;
|
||||
this.cartonMeasurements = cartonMeasurements;
|
||||
this.objectMapper = objectMapper;
|
||||
this.validator = validator;
|
||||
this.imageClient = imageClient;
|
||||
this.receipts = receipts;
|
||||
this.cartonLabels = cartonLabels;
|
||||
}
|
||||
|
||||
public UploadResponse receive(MeasurementRequestHeaders headers, String rawJson) {
|
||||
@@ -73,10 +76,26 @@ public class MojiaMeasurementOpenApiService {
|
||||
"POST", CALLBACK_PATH, headers, rawJson, false);
|
||||
request = parseAndValidate(rawJson);
|
||||
MultipartFile image = decodeJpeg(request.picture());
|
||||
if (app.tenantId() != 0) {
|
||||
throw new BizException(ResultCode.UNAUTHORIZED, "墨家设备必须使用平台级应用凭据");
|
||||
}
|
||||
long machineWarehouseId = requireMachineWarehouse(app, request.machineNo());
|
||||
final InboundCartonLabelService.PlatformBinding platformBinding;
|
||||
try {
|
||||
platformBinding = cartonLabels.resolvePlatformByBoxCode(request.boxCode());
|
||||
} catch (BizException exception) {
|
||||
if (exception.getResultCode() == ResultCode.NOT_FOUND) {
|
||||
return saveUnmatched(app, request, image, exception);
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
if (platformBinding.warehouseId() != machineWarehouseId) {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "箱号不属于当前设备绑定仓库");
|
||||
}
|
||||
CartonMeasurementResult result;
|
||||
try {
|
||||
result = cartonMeasurements.upsert(
|
||||
app.tenantId(), app.subjectId(), new CartonMeasurementCommand(
|
||||
platformBinding.tenantId(), app.subjectId(), new CartonMeasurementCommand(
|
||||
request.boxCode(), request.length(), request.width(), request.height(),
|
||||
request.machineNo(), request.measuredAt() == null ? LocalDateTime.now() : request.measuredAt(),
|
||||
image, optionalTrim(request.shipmentId())));
|
||||
@@ -106,6 +125,14 @@ public class MojiaMeasurementOpenApiService {
|
||||
}
|
||||
}
|
||||
|
||||
private long requireMachineWarehouse(AuthenticatedMeasurementApp app, String machineNo) {
|
||||
Long warehouseId = app.warehouseBindings().get(machineNo.trim());
|
||||
if (warehouseId == null || warehouseId <= 0) {
|
||||
throw new BizException(ResultCode.UNAUTHORIZED, "设备编号未绑定有效仓库");
|
||||
}
|
||||
return warehouseId;
|
||||
}
|
||||
|
||||
private UploadResponse saveUnmatched(AuthenticatedMeasurementApp app, UploadRequest request,
|
||||
MultipartFile image, BizException matchFailure) {
|
||||
long receiptId = IdWorker.getId();
|
||||
|
||||
@@ -836,10 +836,14 @@ paths:
|
||||
post:
|
||||
summary: 提交一条第三方箱规测量结果和一张图片
|
||||
description: >-
|
||||
data 中的 inboundCode 匹配入库单,inboundDetailId 必须匹配该入库单关联订单中的已有货物明细;
|
||||
每次只保存一条箱规明细和一张图片,不审核、不计费、不入库,也不改变入库单状态。
|
||||
首轮联调可由服务端配置 signature-required=false:此时只强制固定 X-App-Key 和
|
||||
Idempotency-Key,X-Timestamp、X-Nonce、X-Signature 可不传;恢复签名后按下述规则计算。
|
||||
墨家 JSON 协议使用平台级 AppKey(tenant-id=0)和正数设备服务主体 ID;
|
||||
按全平台唯一 boxCode 找到入库单真实客户,再校验 machineNo 绑定的仓库与入库单仓库一致。
|
||||
匹配结果按真实客户保存,未匹配的联调收件按平台作用域保存;
|
||||
每次只保存一条箱规明细和一张图片,不审核、不计费、不入库。
|
||||
multipart 协议的 data 中则以 inboundCode 和 inboundDetailId 匹配指定客户的入库单及货物明细。
|
||||
首轮联调可由服务端配置 signature-required=false:此时 JSON 协议只强制 X-App-Key,
|
||||
multipart 协议还要求 Idempotency-Key;X-Timestamp、X-Nonce、X-Signature 可不传。
|
||||
恢复签名后按下述规则计算。
|
||||
签名串依次为 METHOD、原始 PATH、X-Timestamp、X-Nonce、Idempotency-Key、
|
||||
签名body的 SHA-256 小写十六进制,六部分使用 LF(\n)连接且末尾不追加换行。
|
||||
签名body = data原始UTF-8文本 + LF + image文件内容SHA-256小写十六进制。
|
||||
|
||||
Reference in New Issue
Block a user