优化
This commit is contained in:
@@ -3189,8 +3189,12 @@ CREATE TABLE IF NOT EXISTS fl_price_book_channel (
|
|||||||
reference_transit VARCHAR(128) NULL COMMENT '参考时效',
|
reference_transit VARCHAR(128) NULL COMMENT '参考时效',
|
||||||
surcharge_description VARCHAR(2000) NULL COMMENT '附加费说明,仅展示',
|
surcharge_description VARCHAR(2000) NULL COMMENT '附加费说明,仅展示',
|
||||||
remark VARCHAR(2000) NULL COMMENT '渠道备注',
|
remark VARCHAR(2000) NULL COMMENT '渠道备注',
|
||||||
|
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||||
|
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||||||
|
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE KEY uk_price_book_channel (price_book_version_id, channel_code),
|
UNIQUE KEY uk_price_book_channel (price_book_version_id, channel_code),
|
||||||
KEY idx_price_book_channel_code (channel_code, price_book_version_id)
|
KEY idx_price_book_channel_code (channel_code, price_book_version_id)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.baiyue.firstleg.client;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Print Service contract for stateless inbound carton label rendering. */
|
||||||
|
@FeignClient(name = "print-service", contextId = "firstlegPrintPdfClient",
|
||||||
|
url = "${baiyue.clients.print.url:}", path = "/internal/print/v1/pdfs")
|
||||||
|
public interface PrintPdfClient {
|
||||||
|
@PostMapping(value = "/inbound-marks", consumes = "application/json")
|
||||||
|
ResponseEntity<byte[]> generateInboundMarks(
|
||||||
|
@RequestHeader("X-Internal-Service-Token") String token,
|
||||||
|
@RequestHeader(value = "X-Request-Id", required = false) String requestId,
|
||||||
|
@RequestBody InboundMarkPdfRequest request);
|
||||||
|
|
||||||
|
record InboundMarkPdfRequest(String templateCode, PageSpec pageSpec,
|
||||||
|
String downloadFileName, List<Label> labels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record PageSpec(BigDecimal widthMm, BigDecimal heightMm) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record Label(String topCode, String warehouseText, String cartonText,
|
||||||
|
String routeText, String qrContent) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -11,6 +11,7 @@ import com.baiyue.firstleg.dto.InboundOrderDetail;
|
|||||||
import com.baiyue.firstleg.dto.InboundOrderQuery;
|
import com.baiyue.firstleg.dto.InboundOrderQuery;
|
||||||
import com.baiyue.firstleg.dto.InboundOrderSummary;
|
import com.baiyue.firstleg.dto.InboundOrderSummary;
|
||||||
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
||||||
|
import com.baiyue.firstleg.service.InboundCartonLabelPrintService;
|
||||||
import com.baiyue.firstleg.service.InboundOrderQueryService;
|
import com.baiyue.firstleg.service.InboundOrderQueryService;
|
||||||
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;
|
||||||
@@ -22,9 +23,14 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
|||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** 平台端入库单列表和详情只读接口。 */
|
/** 平台端入库单列表和详情只读接口。 */
|
||||||
@Tag(name = "平台入库单", description = "二期入库单列表、页签、筛选和只读聚合详情")
|
@Tag(name = "平台入库单", description = "二期入库单列表、页签、筛选和只读聚合详情")
|
||||||
@@ -33,6 +39,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/api/platform/inbounds")
|
@RequestMapping("/api/platform/inbounds")
|
||||||
public class PlatformInboundOrderController {
|
public class PlatformInboundOrderController {
|
||||||
@Resource private InboundOrderQueryService service;
|
@Resource private InboundOrderQueryService service;
|
||||||
|
@Resource private InboundCartonLabelPrintService cartonLabelPrintService;
|
||||||
|
|
||||||
@Operation(summary = "分页查询入库单",
|
@Operation(summary = "分页查询入库单",
|
||||||
description = "默认分页查询全部企业入库单;可按目标租户和二期状态页签筛选入库单快照。")
|
description = "默认分页查询全部企业入库单;可按目标租户和二期状态页签筛选入库单快照。")
|
||||||
@@ -61,6 +68,23 @@ public class PlatformInboundOrderController {
|
|||||||
return ApiResponse.success(service.detail(tenantId, inboundId));
|
return ApiResponse.success(service.detail(tenantId, inboundId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "生成入仓箱唛 PDF",
|
||||||
|
description = "首次调用按订单货物明细冻结整单箱号;重复打印及指定箱下标补打复用原箱号映射。")
|
||||||
|
@PostMapping("/{inboundId}/carton-label-pdf")
|
||||||
|
@PreAuthorize("hasAuthority('platform:firstleg:inbound:view')")
|
||||||
|
public ResponseEntity<byte[]> printCartonLabels(
|
||||||
|
@AuthenticationPrincipal AuthenticatedUser user,
|
||||||
|
@RequestParam long tenantId,
|
||||||
|
@PathVariable long inboundId,
|
||||||
|
@RequestParam(required = false) List<@Min(1) Integer> cartonIndex,
|
||||||
|
@RequestHeader(value = "X-Request-Id", required = false) String requestId) {
|
||||||
|
requirePlatformTenant(user, tenantId);
|
||||||
|
if (inboundId <= 0) {
|
||||||
|
throw new BizException(ResultCode.BAD_REQUEST, "入库单ID必须大于零");
|
||||||
|
}
|
||||||
|
return cartonLabelPrintService.generate(tenantId, inboundId, cartonIndex, requestId);
|
||||||
|
}
|
||||||
|
|
||||||
private static void requirePlatformTenant(AuthenticatedUser user, long tenantId) {
|
private static void requirePlatformTenant(AuthenticatedUser user, long tenantId) {
|
||||||
FirstlegScopeGuard.platform(user, tenantId);
|
FirstlegScopeGuard.platform(user, tenantId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public final class InboundMeasurementResponses {
|
|||||||
String originalDocumentNo,
|
String originalDocumentNo,
|
||||||
BigDecimal originalGrossWeightKg,
|
BigDecimal originalGrossWeightKg,
|
||||||
BigDecimal originalVolumeCbm,
|
BigDecimal originalVolumeCbm,
|
||||||
|
Integer originalPackageCount,
|
||||||
Integer actualPackageCount,
|
Integer actualPackageCount,
|
||||||
BigDecimal actualGrossWeightKg,
|
BigDecimal actualGrossWeightKg,
|
||||||
BigDecimal actualVolumeCbm,
|
BigDecimal actualVolumeCbm,
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
package com.baiyue.firstleg.mapper;
|
package com.baiyue.firstleg.mapper;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baiyue.firstleg.entity.PriceBookChannelEntity;
|
import com.baiyue.firstleg.entity.PriceBookChannelEntity;
|
||||||
public interface PriceBookChannelMapper extends BaseMapper<PriceBookChannelEntity> {}
|
import org.apache.ibatis.annotations.Delete;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
public interface PriceBookChannelMapper extends BaseMapper<PriceBookChannelEntity> {
|
||||||
|
@Delete("DELETE FROM fl_price_book_channel WHERE price_book_version_id = #{versionId}")
|
||||||
|
int physicallyDeleteByPriceBookVersionId(@Param("versionId") long versionId);
|
||||||
|
}
|
||||||
|
|||||||
+7
@@ -42,6 +42,13 @@ public class InboundMeasurementRepository {
|
|||||||
.eq(InboundOrderEntity::getId, inboundId));
|
.eq(InboundOrderEntity::getId, inboundId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InboundOrderEntity lockInbound(long tenantId, long inboundId) {
|
||||||
|
return inboundOrders.selectOne(new LambdaQueryWrapper<InboundOrderEntity>()
|
||||||
|
.eq(InboundOrderEntity::getTenantId, tenantId)
|
||||||
|
.eq(InboundOrderEntity::getId, inboundId)
|
||||||
|
.last("FOR UPDATE"));
|
||||||
|
}
|
||||||
|
|
||||||
public boolean updateInbound(InboundOrderEntity inbound) {
|
public boolean updateInbound(InboundOrderEntity inbound) {
|
||||||
return inboundOrders.update(inbound, new LambdaUpdateWrapper<InboundOrderEntity>()
|
return inboundOrders.update(inbound, new LambdaUpdateWrapper<InboundOrderEntity>()
|
||||||
.eq(InboundOrderEntity::getTenantId, inbound.getTenantId())
|
.eq(InboundOrderEntity::getTenantId, inbound.getTenantId())
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.baiyue.common.core.api.PageResult;
|
|||||||
import com.baiyue.firstleg.entity.InboundFeeEntity;
|
import com.baiyue.firstleg.entity.InboundFeeEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundIdempotencyEntity;
|
import com.baiyue.firstleg.entity.InboundIdempotencyEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
||||||
|
import com.baiyue.firstleg.entity.InboundMeasurementItemEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundOutboxEntity;
|
import com.baiyue.firstleg.entity.InboundOutboxEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||||
@@ -16,6 +17,7 @@ import com.baiyue.firstleg.mapper.InboundAllocationQueryMapper;
|
|||||||
import com.baiyue.firstleg.mapper.InboundFeeMapper;
|
import com.baiyue.firstleg.mapper.InboundFeeMapper;
|
||||||
import com.baiyue.firstleg.mapper.InboundIdempotencyMapper;
|
import com.baiyue.firstleg.mapper.InboundIdempotencyMapper;
|
||||||
import com.baiyue.firstleg.mapper.InboundMeasurementMapper;
|
import com.baiyue.firstleg.mapper.InboundMeasurementMapper;
|
||||||
|
import com.baiyue.firstleg.mapper.InboundMeasurementItemMapper;
|
||||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||||
import com.baiyue.firstleg.mapper.InboundOutboxMapper;
|
import com.baiyue.firstleg.mapper.InboundOutboxMapper;
|
||||||
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||||
@@ -30,6 +32,8 @@ public class InboundWorkflowRepository {
|
|||||||
@Resource
|
@Resource
|
||||||
private InboundMeasurementMapper measurements;
|
private InboundMeasurementMapper measurements;
|
||||||
@Resource
|
@Resource
|
||||||
|
private InboundMeasurementItemMapper measurementItems;
|
||||||
|
@Resource
|
||||||
private MeasureReviewRecordMapper reviews;
|
private MeasureReviewRecordMapper reviews;
|
||||||
@Resource
|
@Resource
|
||||||
private InboundStatusHistoryMapper histories;
|
private InboundStatusHistoryMapper histories;
|
||||||
@@ -55,6 +59,13 @@ public class InboundWorkflowRepository {
|
|||||||
.eq(InboundMeasurementEntity::getId, measurementId));
|
.eq(InboundMeasurementEntity::getId, measurementId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public java.util.List<InboundMeasurementItemEntity> listMeasurementItems(
|
||||||
|
long tenantId, long measurementId) {
|
||||||
|
return measurementItems.selectList(new LambdaQueryWrapper<InboundMeasurementItemEntity>()
|
||||||
|
.eq(InboundMeasurementItemEntity::getTenantId, tenantId)
|
||||||
|
.eq(InboundMeasurementItemEntity::getMeasurementId, measurementId));
|
||||||
|
}
|
||||||
|
|
||||||
public int transitionReview(long tenantId, long inboundId, long operatorId, int expectedVersion,
|
public int transitionReview(long tenantId, long inboundId, long operatorId, int expectedVersion,
|
||||||
boolean approved) {
|
boolean approved) {
|
||||||
return approved
|
return approved
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class PriceBookRepository {
|
|||||||
}
|
}
|
||||||
public void deletePendingChildren(long versionId) {
|
public void deletePendingChildren(long versionId) {
|
||||||
rates.physicallyDeletePendingByPriceBookVersionId(versionId);
|
rates.physicallyDeletePendingByPriceBookVersionId(versionId);
|
||||||
channels.delete(new LambdaQueryWrapper<PriceBookChannelEntity>().eq(PriceBookChannelEntity::getPriceBookVersionId, versionId));
|
channels.physicallyDeleteByPriceBookVersionId(versionId);
|
||||||
}
|
}
|
||||||
public List<PriceChannelCatalogEntity> activeCatalog() {
|
public List<PriceChannelCatalogEntity> activeCatalog() {
|
||||||
return catalog.selectList(new LambdaQueryWrapper<PriceChannelCatalogEntity>()
|
return catalog.selectList(new LambdaQueryWrapper<PriceChannelCatalogEntity>()
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class CartonMeasurementService {
|
|||||||
UpsertOutcome outcome;
|
UpsertOutcome outcome;
|
||||||
try {
|
try {
|
||||||
outcome = transactionTemplate.execute(status -> persist(
|
outcome = transactionTemplate.execute(status -> persist(
|
||||||
tenantId, operatorId, binding, inbound, boxCode, command, uploaded.fileId()));
|
tenantId, operatorId, binding, boxCode, command, uploaded.fileId()));
|
||||||
} catch (RuntimeException exception) {
|
} catch (RuntimeException exception) {
|
||||||
cleanupImage(tenantId, operatorId, inbound.getId(), uploaded.fileId(), "回传落库失败补偿");
|
cleanupImage(tenantId, operatorId, inbound.getId(), uploaded.fileId(), "回传落库失败补偿");
|
||||||
throw exception;
|
throw exception;
|
||||||
@@ -111,9 +111,8 @@ public class CartonMeasurementService {
|
|||||||
if (!repository.updateMeasurement(measurement)) {
|
if (!repository.updateMeasurement(measurement)) {
|
||||||
throw concurrentModification();
|
throw concurrentModification();
|
||||||
}
|
}
|
||||||
// 提交审核后仍允许平台人工修正代表箱数,入库单快照必须随当前批次同步。
|
// 提交审核后仍允许平台人工修正代表箱数,但订单原始总箱数不可被测量汇总覆盖。
|
||||||
if ("MEASURE_PENDING".equals(inbound.getProcessStatus())) {
|
if ("MEASURE_PENDING".equals(inbound.getProcessStatus())) {
|
||||||
inbound.setTotalPackageCount(measurement.getTotalPackageCount());
|
|
||||||
inbound.setActualVolumeCbm(measurement.getTotalVolumeCbm());
|
inbound.setActualVolumeCbm(measurement.getTotalVolumeCbm());
|
||||||
inbound.setVolumeWeightKg(measurement.getVolumeWeightKg());
|
inbound.setVolumeWeightKg(measurement.getVolumeWeightKg());
|
||||||
if (!repository.updateInbound(inbound)) {
|
if (!repository.updateInbound(inbound)) {
|
||||||
@@ -125,8 +124,15 @@ public class CartonMeasurementService {
|
|||||||
|
|
||||||
private UpsertOutcome persist(long tenantId, long operatorId,
|
private UpsertOutcome persist(long tenantId, long operatorId,
|
||||||
CartonLabelBindingResolver.Binding binding,
|
CartonLabelBindingResolver.Binding binding,
|
||||||
InboundOrderEntity inbound, String boxCode,
|
String boxCode,
|
||||||
CartonMeasurementCommand command, long imageFileId) {
|
CartonMeasurementCommand command, long imageFileId) {
|
||||||
|
InboundOrderEntity inbound = repository.lockInbound(tenantId, binding.inboundId());
|
||||||
|
if (inbound == null) {
|
||||||
|
throw new BizException(ResultCode.NOT_FOUND, "入库单不存在");
|
||||||
|
}
|
||||||
|
if (!DEVICE_WRITABLE_STATUSES.contains(inbound.getProcessStatus())) {
|
||||||
|
throw new BizException(ResultCode.CONFLICT, "测量已提交,设备不能新增或覆盖数据");
|
||||||
|
}
|
||||||
InboundMeasurementItemEntity item = repository.findItemByBoxCode(tenantId, boxCode);
|
InboundMeasurementItemEntity item = repository.findItemByBoxCode(tenantId, boxCode);
|
||||||
boolean created = item == null;
|
boolean created = item == null;
|
||||||
InboundMeasurementEntity measurement;
|
InboundMeasurementEntity measurement;
|
||||||
|
|||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
package com.baiyue.firstleg.service;
|
||||||
|
|
||||||
|
import com.baiyue.common.core.api.ApiResponse;
|
||||||
|
import com.baiyue.common.core.api.ResultCode;
|
||||||
|
import com.baiyue.common.core.exception.BizException;
|
||||||
|
import com.baiyue.firstleg.client.PrintPdfClient;
|
||||||
|
import com.baiyue.firstleg.dto.InboundCartonLabels;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import feign.FeignException;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Allocates stable carton codes and forwards immutable label facts to Print. */
|
||||||
|
@Service
|
||||||
|
public class InboundCartonLabelPrintService {
|
||||||
|
private static final BigDecimal PAGE_MM = new BigDecimal("100");
|
||||||
|
private static final List<String> SAFE_RESPONSE_HEADERS = List.of(
|
||||||
|
HttpHeaders.CONTENT_DISPOSITION, HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA,
|
||||||
|
"X-Print-Page-Count", "X-Request-Id", "X-Trace-Id");
|
||||||
|
|
||||||
|
private final InboundCartonLabelService labelService;
|
||||||
|
private final PrintPdfClient printClient;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Value("${baiyue.security.internal-service-token}")
|
||||||
|
private String internalToken;
|
||||||
|
|
||||||
|
public InboundCartonLabelPrintService(InboundCartonLabelService labelService,
|
||||||
|
PrintPdfClient printClient,
|
||||||
|
ObjectMapper objectMapper) {
|
||||||
|
this.labelService = labelService;
|
||||||
|
this.printClient = printClient;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseEntity<byte[]> generate(long tenantId, long inboundId,
|
||||||
|
List<Integer> cartonIndexes, String requestId) {
|
||||||
|
InboundCartonLabels.Batch batch = labelService.list(tenantId, inboundId, cartonIndexes);
|
||||||
|
PrintPdfClient.InboundMarkPdfRequest request = new PrintPdfClient.InboundMarkPdfRequest(
|
||||||
|
"INBOUND_MARK_V1", new PrintPdfClient.PageSpec(PAGE_MM, PAGE_MM),
|
||||||
|
batch.inboundCode() + "-" + batch.totalCount() + "件-箱唛.pdf",
|
||||||
|
batch.labels().stream().map(this::toPrintLabel).toList());
|
||||||
|
try {
|
||||||
|
return requirePdf(printClient.generateInboundMarks(internalToken, requestId, request));
|
||||||
|
} catch (FeignException exception) {
|
||||||
|
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务暂不可用,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrintPdfClient.Label toPrintLabel(InboundCartonLabels.Label label) {
|
||||||
|
return new PrintPdfClient.Label(label.topCode(), label.warehouseText(), label.cartonText(),
|
||||||
|
label.routeText(), label.boxCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<byte[]> requirePdf(ResponseEntity<byte[]> response) {
|
||||||
|
byte[] body = response == null || response.getBody() == null ? new byte[0] : response.getBody();
|
||||||
|
MediaType contentType = response == null ? null : response.getHeaders().getContentType();
|
||||||
|
if (contentType == null || !MediaType.APPLICATION_PDF.isCompatibleWith(contentType) || body.length == 0) {
|
||||||
|
throw downstreamError(body);
|
||||||
|
}
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||||
|
SAFE_RESPONSE_HEADERS.forEach(name -> {
|
||||||
|
String value = response.getHeaders().getFirst(name);
|
||||||
|
if (StringUtils.hasText(value)) headers.set(name, value);
|
||||||
|
});
|
||||||
|
headers.setContentLength(body.length);
|
||||||
|
return ResponseEntity.ok().headers(headers).body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BizException downstreamError(byte[] body) {
|
||||||
|
try {
|
||||||
|
ApiResponse<Object> error = objectMapper.readValue(body, new TypeReference<>() { });
|
||||||
|
if (error != null && !error.successful()) {
|
||||||
|
return new BizException(ResultCode.of(error.code()),
|
||||||
|
StringUtils.hasText(error.message()) ? error.message() : "打印服务生成失败");
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// Non-contract responses are treated as unavailable instead of exposing downstream content.
|
||||||
|
}
|
||||||
|
return new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务返回了无效响应");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -273,6 +273,7 @@ public class InboundMeasurementService {
|
|||||||
measurement == null ? null : measurement.getOriginalDocumentNo(),
|
measurement == null ? null : measurement.getOriginalDocumentNo(),
|
||||||
measurement == null ? null : measurement.getOriginalGrossWeightKg(),
|
measurement == null ? null : measurement.getOriginalGrossWeightKg(),
|
||||||
measurement == null ? null : measurement.getOriginalVolumeCbm(),
|
measurement == null ? null : measurement.getOriginalVolumeCbm(),
|
||||||
|
inbound.getTotalPackageCount(),
|
||||||
measurement == null ? inbound.getTotalPackageCount() : measurement.getTotalPackageCount(),
|
measurement == null ? inbound.getTotalPackageCount() : measurement.getTotalPackageCount(),
|
||||||
inbound.getActualGrossWeightKg(),
|
inbound.getActualGrossWeightKg(),
|
||||||
measurement == null ? inbound.getActualVolumeCbm() : measurement.getTotalVolumeCbm(),
|
measurement == null ? inbound.getActualVolumeCbm() : measurement.getTotalVolumeCbm(),
|
||||||
|
|||||||
@@ -68,6 +68,17 @@ public class MeasurementReviewService {
|
|||||||
if (!"SUBMITTED".equals(measurement.getStatus())) {
|
if (!"SUBMITTED".equals(measurement.getStatus())) {
|
||||||
throw new BizException(ResultCode.CONFLICT, "测量批次不是待审核状态");
|
throw new BizException(ResultCode.CONFLICT, "测量批次不是待审核状态");
|
||||||
}
|
}
|
||||||
|
if (approved) {
|
||||||
|
long confirmed = repository.listMeasurementItems(tenantId, measurement.getId()).stream()
|
||||||
|
.map(com.baiyue.firstleg.entity.InboundMeasurementItemEntity::getPackageCount)
|
||||||
|
.filter(java.util.Objects::nonNull)
|
||||||
|
.mapToLong(Integer::longValue)
|
||||||
|
.sum();
|
||||||
|
if (order.getTotalPackageCount() == null || confirmed != order.getTotalPackageCount()) {
|
||||||
|
throw new BizException(ResultCode.VALIDATION_FAILED,
|
||||||
|
"人工确认箱数合计与入库单总箱数不一致,不能审核通过");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (repository.transitionReview(tenantId, inboundId, operatorId,
|
if (repository.transitionReview(tenantId, inboundId, operatorId,
|
||||||
request.expectedVersion(), approved) != 1) {
|
request.expectedVersion(), approved) != 1) {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ baiyue:
|
|||||||
url: ${ORDER_SERVICE_URL:}
|
url: ${ORDER_SERVICE_URL:}
|
||||||
file:
|
file:
|
||||||
url: ${FILE_SERVICE_URL:}
|
url: ${FILE_SERVICE_URL:}
|
||||||
|
print:
|
||||||
|
url: ${PRINT_SERVICE_URL:}
|
||||||
firstleg:
|
firstleg:
|
||||||
inbound:
|
inbound:
|
||||||
allow-rejected-measurement: false
|
allow-rejected-measurement: false
|
||||||
|
|||||||
@@ -454,8 +454,12 @@ CREATE TABLE IF NOT EXISTS fl_price_book_channel (
|
|||||||
reference_transit VARCHAR(128) NULL COMMENT '参考时效',
|
reference_transit VARCHAR(128) NULL COMMENT '参考时效',
|
||||||
surcharge_description VARCHAR(2000) NULL COMMENT '附加费说明,仅展示',
|
surcharge_description VARCHAR(2000) NULL COMMENT '附加费说明,仅展示',
|
||||||
remark VARCHAR(2000) NULL COMMENT '渠道备注',
|
remark VARCHAR(2000) NULL COMMENT '渠道备注',
|
||||||
|
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||||
|
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||||||
|
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE KEY uk_price_book_channel (price_book_version_id, channel_code),
|
UNIQUE KEY uk_price_book_channel (price_book_version_id, channel_code),
|
||||||
KEY idx_price_book_channel_code (channel_code, price_book_version_id)
|
KEY idx_price_book_channel_code (channel_code, price_book_version_id)
|
||||||
|
|||||||
+3
@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ class InboundOrderControllerContractTest {
|
|||||||
AuthenticatedUser.class, Long.class, InboundOrderQuery.class);
|
AuthenticatedUser.class, Long.class, InboundOrderQuery.class);
|
||||||
assertAuthority("detail", "hasAuthority('platform:firstleg:inbound:view')",
|
assertAuthority("detail", "hasAuthority('platform:firstleg:inbound:view')",
|
||||||
AuthenticatedUser.class, long.class, long.class);
|
AuthenticatedUser.class, long.class, long.class);
|
||||||
|
assertAuthority("printCartonLabels", "hasAuthority('platform:firstleg:inbound:view')",
|
||||||
|
AuthenticatedUser.class, long.class, long.class, List.class, String.class);
|
||||||
|
|
||||||
Method page = PlatformInboundOrderController.class.getDeclaredMethod(
|
Method page = PlatformInboundOrderController.class.getDeclaredMethod(
|
||||||
"page", AuthenticatedUser.class, Long.class, InboundOrderQuery.class);
|
"page", AuthenticatedUser.class, Long.class, InboundOrderQuery.class);
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.baiyue.firstleg.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@DisplayName("价格版本渠道 Mapper 契约")
|
||||||
|
class PriceBookChannelMapperTest {
|
||||||
|
@Test
|
||||||
|
@DisplayName("重新生成版本快照时使用物理删除避免唯一键冲突")
|
||||||
|
void shouldPhysicallyDeleteVersionChannels() throws Exception {
|
||||||
|
Method method = PriceBookChannelMapper.class
|
||||||
|
.getMethod("physicallyDeleteByPriceBookVersionId", long.class);
|
||||||
|
|
||||||
|
assertThat(method.getAnnotation(Delete.class).value())
|
||||||
|
.containsExactly("DELETE FROM fl_price_book_channel WHERE price_book_version_id = #{versionId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,10 @@ class PriceBookSqlContractTest {
|
|||||||
.getContentAsString(StandardCharsets.UTF_8);
|
.getContentAsString(StandardCharsets.UTF_8);
|
||||||
String migration = new ClassPathResource("db/phase2-fba-pricing-migration.sql")
|
String migration = new ClassPathResource("db/phase2-fba-pricing-migration.sql")
|
||||||
.getContentAsString(StandardCharsets.UTF_8);
|
.getContentAsString(StandardCharsets.UTF_8);
|
||||||
|
String channelTable = schema.substring(
|
||||||
|
schema.indexOf("CREATE TABLE IF NOT EXISTS fl_price_book_channel"),
|
||||||
|
schema.indexOf("ENGINE=InnoDB COMMENT='价格版本渠道及船期快照'")
|
||||||
|
);
|
||||||
assertThat(schema)
|
assertThat(schema)
|
||||||
.contains("CREATE TABLE IF NOT EXISTS fl_price_channel_catalog")
|
.contains("CREATE TABLE IF NOT EXISTS fl_price_channel_catalog")
|
||||||
.contains("CREATE TABLE IF NOT EXISTS fl_price_origin_alias")
|
.contains("CREATE TABLE IF NOT EXISTS fl_price_origin_alias")
|
||||||
@@ -32,6 +36,11 @@ class PriceBookSqlContractTest {
|
|||||||
.contains("destination_name VARCHAR(128)")
|
.contains("destination_name VARCHAR(128)")
|
||||||
.contains("parse_issues_json JSON NULL")
|
.contains("parse_issues_json JSON NULL")
|
||||||
.contains("DEFAULT 'PENDING_CONFIRMATION'");
|
.contains("DEFAULT 'PENDING_CONFIRMATION'");
|
||||||
|
assertThat(channelTable)
|
||||||
|
.contains("created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID'")
|
||||||
|
.contains("updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID'")
|
||||||
|
.contains("deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记'")
|
||||||
|
.contains("version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号'");
|
||||||
assertThat(migration)
|
assertThat(migration)
|
||||||
.doesNotContain("fl_price_book_version")
|
.doesNotContain("fl_price_book_version")
|
||||||
.doesNotContain("'pricing_tax_mode'")
|
.doesNotContain("'pricing_tax_mode'")
|
||||||
|
|||||||
+2
-1
@@ -50,6 +50,7 @@ class CartonMeasurementServiceTest {
|
|||||||
when(resolver.resolveByBoxCode(10001L, "RK-001#5-1"))
|
when(resolver.resolveByBoxCode(10001L, "RK-001#5-1"))
|
||||||
.thenReturn(new CartonLabelBindingResolver.Binding(70001L, 80001L, "RK-001", 1, 5, "50x40x30cm"));
|
.thenReturn(new CartonLabelBindingResolver.Binding(70001L, 80001L, "RK-001", 1, 5, "50x40x30cm"));
|
||||||
when(repository.findInbound(10001L, 70001L)).thenReturn(inbound("WAIT_MEASURE"));
|
when(repository.findInbound(10001L, 70001L)).thenReturn(inbound("WAIT_MEASURE"));
|
||||||
|
when(repository.lockInbound(10001L, 70001L)).thenReturn(inbound("WAIT_MEASURE"));
|
||||||
when(images.upload(any(), any(Long.TYPE), any(Long.TYPE), any(Long.TYPE), any()))
|
when(images.upload(any(), any(Long.TYPE), any(Long.TYPE), any(Long.TYPE), any()))
|
||||||
.thenReturn(ApiResponse.success(new MeasurementImageClient.UploadResult(
|
.thenReturn(ApiResponse.success(new MeasurementImageClient.UploadResult(
|
||||||
99001L, "measure.jpg", "image/jpeg", 10L, "NOT_REQUIRED", null, null, null, null)));
|
99001L, "measure.jpg", "image/jpeg", 10L, "NOT_REQUIRED", null, null, null, null)));
|
||||||
@@ -130,7 +131,7 @@ class CartonMeasurementServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void confirmedPackageCountCanBeCorrectedAfterSubmissionAndSyncsInboundSnapshot() {
|
void confirmedPackageCountCanBeCorrectedAfterSubmissionWithoutChangingOriginalPackageCount() {
|
||||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||||
InboundMeasurementEntity measurement = measurement(30001L, "SUBMITTED", 2);
|
InboundMeasurementEntity measurement = measurement(30001L, "SUBMITTED", 2);
|
||||||
measurement.setTotalPackageCount(1);
|
measurement.setTotalPackageCount(1);
|
||||||
|
|||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package com.baiyue.firstleg.service;
|
||||||
|
|
||||||
|
import com.baiyue.common.core.api.ResultCode;
|
||||||
|
import com.baiyue.common.core.exception.BizException;
|
||||||
|
import com.baiyue.firstleg.client.PrintPdfClient;
|
||||||
|
import com.baiyue.firstleg.dto.InboundCartonLabels;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class InboundCartonLabelPrintServiceTest {
|
||||||
|
private final InboundCartonLabelService labels = mock(InboundCartonLabelService.class);
|
||||||
|
private final PrintPdfClient print = mock(PrintPdfClient.class);
|
||||||
|
private InboundCartonLabelPrintService service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
service = new InboundCartonLabelPrintService(labels, print, new ObjectMapper());
|
||||||
|
ReflectionTestUtils.setField(service, "internalToken", "internal-token");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void forwardsStableLabelsAndOnlySafePdfHeaders() {
|
||||||
|
when(labels.list(10001L, 70001L, List.of(1))).thenReturn(batch());
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||||
|
headers.setContentDispositionFormData("attachment", "labels.pdf");
|
||||||
|
headers.set("X-Print-Page-Count", "1");
|
||||||
|
headers.set("X-Internal-Service-Token", "must-not-leak");
|
||||||
|
when(print.generateInboundMarks(org.mockito.ArgumentMatchers.anyString(),
|
||||||
|
org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any()))
|
||||||
|
.thenReturn(new ResponseEntity<>(new byte[] {1, 2, 3}, headers,
|
||||||
|
org.springframework.http.HttpStatus.OK));
|
||||||
|
|
||||||
|
ResponseEntity<byte[]> response = service.generate(10001L, 70001L, List.of(1), "request-1");
|
||||||
|
|
||||||
|
ArgumentCaptor<PrintPdfClient.InboundMarkPdfRequest> request =
|
||||||
|
ArgumentCaptor.forClass(PrintPdfClient.InboundMarkPdfRequest.class);
|
||||||
|
verify(print).generateInboundMarks(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||||
|
org.mockito.ArgumentMatchers.eq("request-1"), request.capture());
|
||||||
|
assertThat(request.getValue().downloadFileName()).isEqualTo("PCHK2608040174-19件-箱唛.pdf");
|
||||||
|
assertThat(request.getValue().labels()).singleElement().satisfies(label -> {
|
||||||
|
assertThat(label.qrContent()).isEqualTo("PCHK2608040174#19-1");
|
||||||
|
assertThat(label.cartonText()).isEqualTo("CTNS:19-1");
|
||||||
|
});
|
||||||
|
assertThat(response.getBody()).containsExactly(1, 2, 3);
|
||||||
|
assertThat(response.getHeaders().getFirst("X-Print-Page-Count")).isEqualTo("1");
|
||||||
|
assertThat(response.getHeaders()).doesNotContainKey("X-Internal-Service-Token");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void convertsPrintJsonBusinessFailureInsteadOfReturningItAsPdf() throws Exception {
|
||||||
|
when(labels.list(10001L, 70001L, null)).thenReturn(batch());
|
||||||
|
byte[] error = "{\"code\":\"A0422\",\"message\":\"标签字段超出模板限制\","
|
||||||
|
.concat("\"data\":null,\"requestId\":\"req-2\",\"traceId\":\"trace-2\"}")
|
||||||
|
.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
when(print.generateInboundMarks(org.mockito.ArgumentMatchers.anyString(),
|
||||||
|
org.mockito.ArgumentMatchers.isNull(), org.mockito.ArgumentMatchers.any()))
|
||||||
|
.thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(error));
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> service.generate(10001L, 70001L, null, null))
|
||||||
|
.isInstanceOf(BizException.class)
|
||||||
|
.hasMessageContaining("标签字段超出模板限制")
|
||||||
|
.extracting(value -> ((BizException) value).getResultCode())
|
||||||
|
.isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InboundCartonLabels.Batch batch() {
|
||||||
|
InboundCartonLabels.Label label = new InboundCartonLabels.Label(
|
||||||
|
1L, 2L, 1, "音箱", 19, "PCHK2608040174#19-1", 19, 1,
|
||||||
|
"50x40x30cm", new BigDecimal("50"), new BigDecimal("40"), new BigDecimal("30"),
|
||||||
|
"PCHK2608040174", "MCC1", "CTNS:19-1", "洛杉矶普船海卡");
|
||||||
|
return new InboundCartonLabels.Batch(70001L, 80001L, "PCHK2608040174", 19, List.of(label));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.baiyue.firstleg.config.InboundProperties;
|
|||||||
import com.baiyue.firstleg.dto.ExceptionReleaseRequest;
|
import com.baiyue.firstleg.dto.ExceptionReleaseRequest;
|
||||||
import com.baiyue.firstleg.dto.MeasurementReviewRequest;
|
import com.baiyue.firstleg.dto.MeasurementReviewRequest;
|
||||||
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
||||||
|
import com.baiyue.firstleg.entity.InboundMeasurementItemEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||||
import com.baiyue.firstleg.repository.InboundWorkflowRepository;
|
import com.baiyue.firstleg.repository.InboundWorkflowRepository;
|
||||||
@@ -79,6 +80,23 @@ class MeasurementReviewServiceTest {
|
|||||||
assertThat(history.getValue().getToStatus()).isEqualTo("WAIT_SPLIT");
|
assertThat(history.getValue().getToStatus()).isEqualTo("WAIT_SPLIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("确认箱数合计不等于订单总箱数时不能审核通过")
|
||||||
|
void shouldRejectApprovalWhenConfirmedPackageCountDoesNotMatchOrder() {
|
||||||
|
Fixture fixture = fixture(false);
|
||||||
|
InboundMeasurementItemEntity item = new InboundMeasurementItemEntity();
|
||||||
|
item.setPackageCount(4);
|
||||||
|
when(fixture.repository.listMeasurementItems(10001L, 20L)).thenReturn(List.of(item));
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> fixture.service.approve(10001L, 9L, "审核员", 10L, "approve-mismatch",
|
||||||
|
new MeasurementReviewRequest(20L, 3, null)))
|
||||||
|
.isInstanceOf(BizException.class)
|
||||||
|
.extracting(error -> ((BizException) error).getResultCode())
|
||||||
|
.isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||||
|
|
||||||
|
verify(fixture.repository, never()).transitionReview(anyLong(), anyLong(), anyLong(), anyInt(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("驳回原因必填且状态推进为测量驳回")
|
@DisplayName("驳回原因必填且状态推进为测量驳回")
|
||||||
void shouldRequireRejectReason() {
|
void shouldRequireRejectReason() {
|
||||||
@@ -175,10 +193,15 @@ class MeasurementReviewServiceTest {
|
|||||||
InboundOrderEntity order = new InboundOrderEntity();
|
InboundOrderEntity order = new InboundOrderEntity();
|
||||||
order.setProcessStatus("MEASURE_PENDING");
|
order.setProcessStatus("MEASURE_PENDING");
|
||||||
order.setMeasureReviewStatus("PENDING");
|
order.setMeasureReviewStatus("PENDING");
|
||||||
|
order.setTotalPackageCount(5);
|
||||||
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
|
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
|
||||||
|
measurement.setId(20L);
|
||||||
measurement.setStatus("SUBMITTED");
|
measurement.setStatus("SUBMITTED");
|
||||||
|
InboundMeasurementItemEntity item = new InboundMeasurementItemEntity();
|
||||||
|
item.setPackageCount(5);
|
||||||
when(repository.findOwned(10001L, 10L)).thenReturn(order);
|
when(repository.findOwned(10001L, 10L)).thenReturn(order);
|
||||||
when(repository.findMeasurement(10001L, 10L, 20L)).thenReturn(measurement);
|
when(repository.findMeasurement(10001L, 10L, 20L)).thenReturn(measurement);
|
||||||
|
when(repository.listMeasurementItems(10001L, 20L)).thenReturn(List.of(item));
|
||||||
return new Fixture(service, repository, idempotency, order, measurement);
|
return new Fixture(service, repository, idempotency, order, measurement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user