二期内容提交

This commit is contained in:
2026-08-04 19:48:35 +08:00
parent 954adb1287
commit c3bf4edd53
8 changed files with 281 additions and 6 deletions
@@ -9,6 +9,7 @@ import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
@@ -17,6 +18,10 @@ import org.springframework.web.multipart.MultipartFile;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import org.springframework.http.CacheControl;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
/** Firstleg 保存第三方尺寸测量证据图片的内部入口。 */ /** Firstleg 保存第三方尺寸测量证据图片的内部入口。 */
@RestController @RestController
@@ -62,6 +67,26 @@ public class InternalMeasurementImageController {
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@GetMapping("/{tenantId}/{inboundId}/{fileId}")
public ResponseEntity<byte[]> download(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@PathVariable long tenantId,
@PathVariable long inboundId,
@PathVariable long fileId) {
requireToken(token);
if (tenantId <= 0 || inboundId <= 0 || fileId <= 0) {
throw new BizException(ResultCode.BAD_REQUEST, "租户、入库单和文件ID必须大于0");
}
var image = images.download(tenantId, inboundId, fileId);
return ResponseEntity.ok()
.cacheControl(CacheControl.noStore())
.contentType(MediaType.parseMediaType(image.contentType()))
.contentLength(image.content().length)
.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline()
.filename(image.fileName(), StandardCharsets.UTF_8).build().toString())
.body(image.content());
}
private void requireToken(String token) { private void requireToken(String token) {
if (token == null || !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) { if (token == null || !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) {
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效"); throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
@@ -40,4 +40,16 @@ class InternalMeasurementImageControllerTest {
.isInstanceOf(BizException.class) .isInstanceOf(BizException.class)
.hasMessageContaining("凭证无效"); .hasMessageContaining("凭证无效");
} }
@Test
void 内部下载同时校验租户入库单和文件ID() {
when(images.download(11L, 21L, 41L)).thenReturn(
new MeasurementImageService.Download("measure.png", MediaType.IMAGE_PNG_VALUE,
new byte[]{1, 2, 3}));
var response = controller.download("internal-token", 11L, 21L, 41L);
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_PNG);
assertThat(response.getBody()).containsExactly(1, 2, 3);
verify(images).download(11L, 21L, 41L);
}
} }
@@ -5,6 +5,7 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -28,6 +29,13 @@ public interface MeasurementImageClient {
@PathVariable long inboundId, @PathVariable long inboundId,
@PathVariable long fileId); @PathVariable long fileId);
@GetMapping("/{tenantId}/{inboundId}/{fileId}")
org.springframework.http.ResponseEntity<byte[]> download(
@RequestHeader("X-Internal-Service-Token") String token,
@PathVariable long tenantId,
@PathVariable long inboundId,
@PathVariable long fileId);
record UploadResult(Long fileId, String fileName, String contentType, Long sizeBytes, record UploadResult(Long fileId, String fileName, String contentType, Long sizeBytes,
String recognitionStatus, BigDecimal weightValue, String weightUnit, String recognitionStatus, BigDecimal weightValue, String weightUnit,
Double confidence, String error) { Double confidence, String error) {
@@ -14,6 +14,9 @@ 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.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@Tag(name = "客户端入库结果与费用") @Tag(name = "客户端入库结果与费用")
@RestController @RestController
@@ -36,4 +39,20 @@ public class MerchantInboundBillingController {
} }
return ApiResponse.success(service.find(user.tenantId(), orderId)); return ApiResponse.success(service.find(user.tenantId(), orderId));
} }
@GetMapping("/{orderId}/inbound-billing/images/{fileId}")
@PreAuthorize("hasAuthority('merchant:order:view')")
@Operation(summary = "查看已发布计费明细中的测量或称重图片")
public ResponseEntity<byte[]> image(
@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable long orderId,
@PathVariable long fileId) {
if (user == null || !"MERCHANT".equals(user.appCode()) || user.tenantId() == null) {
throw new BizException(ResultCode.FORBIDDEN, "仅允许客户端访问");
}
var image = service.downloadImage(user.tenantId(), orderId, fileId);
return ResponseEntity.ok().cacheControl(CacheControl.noStore())
.contentType(MediaType.parseMediaType(image.contentType()))
.contentLength(image.content().length).body(image.content());
}
} }
@@ -2,6 +2,47 @@ package com.baiyue.firstleg.dto;
import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse; import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse;
import java.math.BigDecimal;
import java.util.List;
public record MerchantInboundBillingResponse(InboundOrderDetail inbound, public record MerchantInboundBillingResponse(InboundOrderDetail inbound,
MeasurementSummary measurementSummary,
List<MeasurementItem> measurementItems,
List<WeightResult> weightResults,
List<ImageReference> measurementImages,
List<ImageReference> weightImages,
BillingBasis billingBasis,
FeeSnapshotResponse latestFee) { FeeSnapshotResponse latestFee) {
public record MeasurementSummary(
Long measurementId, String sourceType, String measurementStatus,
Integer actualPackageCount, BigDecimal actualGrossWeightKg,
BigDecimal actualVolumeCbm, BigDecimal volumeWeightKg, String actualGoodsName) {
}
public record ImageReference(
Long fileId, String imageType, Long measurementItemId,
Long weightResultId, String previewUrl) {
}
public record MeasurementItem(
Long id, String itemNo, String boxSpecification, Integer packageCount,
BigDecimal lengthCm, BigDecimal widthCm, BigDecimal heightCm,
BigDecimal unitWeightKg, BigDecimal totalWeightKg,
BigDecimal unitVolumeCbm, BigDecimal totalVolumeCbm,
BigDecimal volumeWeightKg) {
}
public record WeightResult(
Long id, Long fileId, BigDecimal recognizedWeightValue, String recognizedWeightUnit,
BigDecimal adjustedWeightValue, String adjustedWeightUnit,
BigDecimal finalWeightKg, String recognitionStatus) {
}
public record BillingBasis(
String chargeMode, String chargeUnit, String chargeableBasis,
BigDecimal actualWeightKg, BigDecimal actualVolumeCbm,
BigDecimal convertedQuantity, BigDecimal chargeableQuantity,
BigDecimal chargeableWeightKg, BigDecimal chargeableVolumeCbm) {
}
} }
@@ -3,18 +3,34 @@ package com.baiyue.firstleg.service;
import com.baiyue.common.core.api.ResultCode; import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException; import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.MerchantInboundBillingResponse; import com.baiyue.firstleg.dto.MerchantInboundBillingResponse;
import com.baiyue.firstleg.dto.InboundMeasurementResponses;
import com.baiyue.firstleg.dto.InboundOrderDetail;
import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse;
import com.baiyue.firstleg.client.MeasurementImageClient;
import com.baiyue.firstleg.entity.InboundOrderEntity; import com.baiyue.firstleg.entity.InboundOrderEntity;
import com.baiyue.firstleg.repository.InboundOrderRepository; import com.baiyue.firstleg.repository.InboundOrderRepository;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
@Service @Service
public class MerchantInboundBillingService { public class MerchantInboundBillingService {
private final InboundOrderRepository orders; private final InboundOrderRepository orders;
private final FbaPricingService pricing; private final FbaPricingService pricing;
private final InboundMeasurementService measurements;
private final MeasurementImageClient imageClient;
private final String internalToken;
public MerchantInboundBillingService(InboundOrderRepository orders, FbaPricingService pricing) { public MerchantInboundBillingService(InboundOrderRepository orders, FbaPricingService pricing,
InboundMeasurementService measurements,
MeasurementImageClient imageClient,
@org.springframework.beans.factory.annotation.Value(
"${baiyue.security.internal-service-token}") String internalToken) {
this.orders = orders; this.orders = orders;
this.pricing = pricing; this.pricing = pricing;
this.measurements = measurements;
this.imageClient = imageClient;
this.internalToken = internalToken;
} }
public MerchantInboundBillingResponse find(long tenantId, long orderId) { public MerchantInboundBillingResponse find(long tenantId, long orderId) {
@@ -25,7 +41,90 @@ public class MerchantInboundBillingService {
if (!"INBOUNDED".equals(inbound.getProcessStatus()) || !"SENT".equals(inbound.getFeeStatus())) { if (!"INBOUNDED".equals(inbound.getProcessStatus()) || !"SENT".equals(inbound.getFeeStatus())) {
throw new BizException(ResultCode.NOT_FOUND, "入库费用尚未由客服审核并发布"); throw new BizException(ResultCode.NOT_FOUND, "入库费用尚未由客服审核并发布");
} }
return new MerchantInboundBillingResponse(orders.detail(tenantId, inbound), InboundOrderDetail platformDetail = orders.detail(tenantId, inbound);
pricing.latest(tenantId, inbound.getId())); InboundOrderDetail merchantDetail = new InboundOrderDetail(
platformDetail.summary(), platformDetail.publicRemark(), platformDetail.palletLocations(),
platformDetail.files().stream()
.filter(file -> "PUBLIC".equals(file.visibility()) || "MERCHANT".equals(file.visibility()))
.toList(), List.of(), List.of());
InboundMeasurementResponses.Detail measurement = measurements.detail(tenantId, inbound.getId());
FeeSnapshotResponse fee = pricing.latest(tenantId, inbound.getId());
var summary = new MerchantInboundBillingResponse.MeasurementSummary(
measurement.measurementId(), measurement.sourceType(), measurement.measurementStatus(),
measurement.actualPackageCount(), measurement.actualGrossWeightKg(),
measurement.actualVolumeCbm(), measurement.volumeWeightKg(), measurement.actualGoodsName());
var measurementImages = measurement.items().stream()
.filter(item -> item.intelligentImageFileId() != null)
.map(item -> new MerchantInboundBillingResponse.ImageReference(
item.intelligentImageFileId(), "MEASUREMENT", item.id(), null,
previewUrl(orderId, item.intelligentImageFileId())))
.toList();
var weightImages = measurement.weightResults().stream()
.filter(weight -> weight.fileId() != null)
.map(weight -> new MerchantInboundBillingResponse.ImageReference(
weight.fileId(), "WEIGHT", null, weight.id(), previewUrl(orderId, weight.fileId())))
.toList();
var measurementItems = measurement.items().stream()
.map(item -> new MerchantInboundBillingResponse.MeasurementItem(
item.id(), item.itemNo(), item.boxSpecification(), item.packageCount(),
item.lengthCm(), item.widthCm(), item.heightCm(), item.unitWeightKg(),
item.totalWeightKg(), item.unitVolumeCbm(), item.totalVolumeCbm(), item.volumeWeightKg()))
.toList();
var weightResults = measurement.weightResults().stream()
.map(weight -> new MerchantInboundBillingResponse.WeightResult(
weight.id(), weight.fileId(), weight.ocrWeightValue(), weight.ocrWeightUnit(),
weight.adjustedWeightValue(), weight.adjustedWeightUnit(),
weight.finalWeightKg(), weight.recognitionStatus()))
.toList();
var basis = new MerchantInboundBillingResponse.BillingBasis(
fee.chargeMode(), fee.chargeUnit(), chargeableBasis(fee), fee.actualWeightKg(),
fee.actualVolumeCbm(), fee.convertedQuantity(), fee.chargeableQuantity(),
platformDetail.summary().chargeableWeightKg(),
platformDetail.summary().chargeableVolumeCbm());
return new MerchantInboundBillingResponse(merchantDetail, summary, measurementItems,
weightResults, measurementImages, weightImages, basis, fee);
}
public ImageDownload downloadImage(long tenantId, long orderId, long fileId) {
InboundOrderEntity inbound = requirePublished(tenantId, orderId);
InboundMeasurementResponses.Detail measurement = measurements.detail(tenantId, inbound.getId());
boolean referenced = measurement.items().stream()
.anyMatch(item -> Long.valueOf(fileId).equals(item.intelligentImageFileId()))
|| measurement.weightResults().stream()
.anyMatch(weight -> Long.valueOf(fileId).equals(weight.fileId()));
if (!referenced) throw new BizException(ResultCode.NOT_FOUND, "计费明细中不存在该测量图片");
org.springframework.http.ResponseEntity<byte[]> response = imageClient.download(
internalToken, tenantId, inbound.getId(), fileId);
if (response == null || response.getBody() == null) {
throw new BizException(ResultCode.NOT_FOUND, "测量图片不存在");
}
String contentType = response.getHeaders().getContentType() == null
? "application/octet-stream" : response.getHeaders().getContentType().toString();
return new ImageDownload(response.getBody(), contentType);
}
private InboundOrderEntity requirePublished(long tenantId, long orderId) {
InboundOrderEntity inbound = orders.findByOrderId(tenantId, orderId);
if (inbound == null) throw new BizException(ResultCode.NOT_FOUND, "订单尚未生成入库单");
if (!"INBOUNDED".equals(inbound.getProcessStatus()) || !"SENT".equals(inbound.getFeeStatus())) {
throw new BizException(ResultCode.NOT_FOUND, "入库费用尚未由客服审核并发布");
}
return inbound;
}
private String previewUrl(long orderId, long fileId) {
return "/api/merchant/v1/first-leg/orders/" + orderId
+ "/inbound-billing/images/" + fileId;
}
private String chargeableBasis(FeeSnapshotResponse fee) {
int compared = ("KG".equals(fee.chargeMode()) ? fee.actualWeightKg() : fee.actualVolumeCbm())
.compareTo(fee.convertedQuantity());
if (compared == 0) return "EQUAL";
if ("KG".equals(fee.chargeMode())) return compared > 0 ? "ACTUAL_WEIGHT" : "VOLUME_WEIGHT";
return compared > 0 ? "ACTUAL_VOLUME" : "WEIGHT_CONVERTED_VOLUME";
}
public record ImageDownload(byte[] content, String contentType) {
} }
} }
@@ -32,4 +32,11 @@ class MerchantInboundBillingControllerContractTest {
"admin", "PLATFORM", "WEB", Set.of()); "admin", "PLATFORM", "WEB", Set.of());
assertThatThrownBy(() -> controller.find(platform, 77L)).isInstanceOf(BizException.class); assertThatThrownBy(() -> controller.find(platform, 77L)).isInstanceOf(BizException.class);
} }
@Test
void 商户测量图片复用订单查看权限() throws Exception {
var method = MerchantInboundBillingController.class.getDeclaredMethod(
"image", AuthenticatedUser.class, long.class, long.class);
assertThat(method.getAnnotation(PreAuthorize.class).value())
.isEqualTo("hasAuthority('merchant:order:view')");
}
} }
@@ -3,10 +3,15 @@ package com.baiyue.firstleg.service;
import com.baiyue.common.core.exception.BizException; import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse; import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse;
import com.baiyue.firstleg.dto.InboundOrderDetail; import com.baiyue.firstleg.dto.InboundOrderDetail;
import com.baiyue.firstleg.dto.InboundMeasurementResponses;
import com.baiyue.firstleg.client.MeasurementImageClient;
import com.baiyue.firstleg.entity.InboundOrderEntity; import com.baiyue.firstleg.entity.InboundOrderEntity;
import com.baiyue.firstleg.repository.InboundOrderRepository; import com.baiyue.firstleg.repository.InboundOrderRepository;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -17,20 +22,43 @@ import static org.mockito.Mockito.when;
class MerchantInboundBillingServiceTest { class MerchantInboundBillingServiceTest {
private final InboundOrderRepository orders = mock(InboundOrderRepository.class); private final InboundOrderRepository orders = mock(InboundOrderRepository.class);
private final FbaPricingService pricing = mock(FbaPricingService.class); private final FbaPricingService pricing = mock(FbaPricingService.class);
private final MerchantInboundBillingService service = new MerchantInboundBillingService(orders, pricing); private final InboundMeasurementService measurements = mock(InboundMeasurementService.class);
private final MeasurementImageClient imageClient = mock(MeasurementImageClient.class);
private final MerchantInboundBillingService service = new MerchantInboundBillingService(
orders, pricing, measurements, imageClient, "internal-token");
@Test @Test
void 客户只能看到本租户已完成入库且已发布的费用() { void 客户只能看到本租户已完成入库且已发布的费用() {
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT"); InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
InboundOrderDetail detail = mock(InboundOrderDetail.class); InboundOrderDetail detail = new InboundOrderDetail(mock(com.baiyue.firstleg.dto.InboundOrderSummary.class),
"客户备注", List.of(), List.of(
new InboundOrderDetail.FileSummary(1L, "RELATED", "public.pdf", "PUBLIC", 1, 1L, null),
new InboundOrderDetail.FileSummary(2L, "INTERNAL", "internal.pdf", "INTERNAL", 1, 1L, null)),
List.of(new InboundOrderDetail.InternalNoteSummary(1L, "内部备注", 1L, "客服", null)),
List.of(new InboundOrderDetail.StatusHistorySummary(null, "INBOUNDED", "COMPLETE",
"内部原因", 1L, "客服", null)));
InboundMeasurementResponses.Detail measurement = mock(InboundMeasurementResponses.Detail.class);
FeeSnapshotResponse fee = mock(FeeSnapshotResponse.class); FeeSnapshotResponse fee = mock(FeeSnapshotResponse.class);
when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound); when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound);
when(orders.detail(10001L, inbound)).thenReturn(detail); when(orders.detail(10001L, inbound)).thenReturn(detail);
when(measurements.detail(10001L, 88L)).thenReturn(measurement);
when(measurement.items()).thenReturn(List.of());
when(measurement.weightResults()).thenReturn(List.of());
when(pricing.latest(10001L, 88L)).thenReturn(fee); when(pricing.latest(10001L, 88L)).thenReturn(fee);
when(fee.chargeMode()).thenReturn("KG");
when(fee.chargeUnit()).thenReturn("KG");
when(fee.actualWeightKg()).thenReturn(new BigDecimal("20"));
when(fee.actualVolumeCbm()).thenReturn(new BigDecimal("0.24"));
when(fee.convertedQuantity()).thenReturn(new BigDecimal("40.08"));
when(fee.chargeableQuantity()).thenReturn(new BigDecimal("40.08"));
var result = service.find(10001L, 77L); var result = service.find(10001L, 77L);
assertThat(result.inbound()).isSameAs(detail); assertThat(result.inbound().files()).extracting(InboundOrderDetail.FileSummary::fileId)
.containsExactly(1L);
assertThat(result.inbound().internalNotes()).isEmpty();
assertThat(result.inbound().statusHistory()).isEmpty();
assertThat(result.billingBasis().chargeableBasis()).isEqualTo("VOLUME_WEIGHT");
assertThat(result.latestFee()).isSameAs(fee); assertThat(result.latestFee()).isSameAs(fee);
verify(orders).findByOrderId(10001L, 77L); verify(orders).findByOrderId(10001L, 77L);
} }
@@ -56,6 +84,42 @@ class MerchantInboundBillingServiceTest {
verify(pricing, never()).latest(20002L, 88L); verify(pricing, never()).latest(20002L, 88L);
} }
@Test
void 只能下载已发布计费明细实际引用的图片() {
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
InboundMeasurementResponses.Detail measurement = mock(InboundMeasurementResponses.Detail.class);
InboundMeasurementResponses.Item item = mock(InboundMeasurementResponses.Item.class);
when(item.intelligentImageFileId()).thenReturn(41L);
when(measurement.items()).thenReturn(List.of(item));
when(measurement.weightResults()).thenReturn(List.of());
when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound);
when(measurements.detail(10001L, 88L)).thenReturn(measurement);
when(imageClient.download("internal-token", 10001L, 88L, 41L)).thenReturn(
org.springframework.http.ResponseEntity.ok()
.contentType(org.springframework.http.MediaType.IMAGE_PNG)
.body(new byte[]{1, 2, 3}));
var image = service.downloadImage(10001L, 77L, 41L);
assertThat(image.contentType()).isEqualTo("image/png");
assertThat(image.content()).containsExactly(1, 2, 3);
}
@Test
void 拒绝下载未被当前计费明细引用的图片() {
InboundOrderEntity inbound = inbound("INBOUNDED", "SENT");
InboundMeasurementResponses.Detail measurement = mock(InboundMeasurementResponses.Detail.class);
when(measurement.items()).thenReturn(List.of());
when(measurement.weightResults()).thenReturn(List.of());
when(orders.findByOrderId(10001L, 77L)).thenReturn(inbound);
when(measurements.detail(10001L, 88L)).thenReturn(measurement);
assertThatThrownBy(() -> service.downloadImage(10001L, 77L, 99L))
.isInstanceOf(BizException.class)
.hasMessageContaining("不存在该测量图片");
verify(imageClient, never()).download("internal-token", 10001L, 88L, 99L);
}
private static InboundOrderEntity inbound(String process, String feeStatus) { private static InboundOrderEntity inbound(String process, String feeStatus) {
InboundOrderEntity inbound = new InboundOrderEntity(); InboundOrderEntity inbound = new InboundOrderEntity();
inbound.setId(88L); inbound.setId(88L);