贴标申请,仓储转运

This commit is contained in:
2026-10-14 11:36:35 +08:00
parent 83dc6dea3f
commit e14bdb4f49
37 changed files with 884 additions and 32 deletions
@@ -52,6 +52,16 @@ public class CargoDiscardController {
return ApiResponse.success(service.detail(scope(user, customerId), discardNo));
}
@GetMapping("/{discardNo}/photos/files/{fileId}/download")
@org.springframework.security.access.prepost.PreAuthorize("hasAuthority('platform:overseas-warehouse:discard:list')")
public org.springframework.http.ResponseEntity<byte[]> downloadPhoto(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam String customerId, @PathVariable String discardNo, @PathVariable long fileId) {
var file = service.downloadPhoto(scope(user, customerId), discardNo, fileId);
return org.springframework.http.ResponseEntity.ok().cacheControl(org.springframework.http.CacheControl.noStore())
.header(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION,
org.springframework.http.ContentDisposition.inline().filename(file.fileName(), java.nio.charset.StandardCharsets.UTF_8).build().toString())
.contentType(org.springframework.http.MediaType.parseMediaType(file.contentType())).body(file.content());
}
@PostMapping("/{discardNo}/review")
public ApiResponse<CargoDiscardResponses.Detail> review(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam(required = false) String customerId, @PathVariable String discardNo,
@@ -36,6 +36,16 @@ public class MerchantCargoDiscardController {
@PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_DISCARD_LIST + "')")
public ApiResponse<CargoDiscardResponses.Detail> detail(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String discardNo) { return ApiResponse.success(service.detail(customer(user), discardNo)); }
@GetMapping("/{discardNo}/photos/files/{fileId}/download")
@org.springframework.security.access.prepost.PreAuthorize("hasAuthority('merchant:overseas-warehouse:discard:list')")
public org.springframework.http.ResponseEntity<byte[]> downloadPhoto(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String discardNo, @PathVariable long fileId) {
var file = service.downloadPhoto(customer(user), discardNo, fileId);
return org.springframework.http.ResponseEntity.ok().cacheControl(org.springframework.http.CacheControl.noStore())
.header(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION,
org.springframework.http.ContentDisposition.inline().filename(file.fileName(), java.nio.charset.StandardCharsets.UTF_8).build().toString())
.contentType(org.springframework.http.MediaType.parseMediaType(file.contentType())).body(file.content());
}
@PostMapping("/{discardNo}/cancel")
@PreAuthorize("hasAuthority('" + OverseasPermissions.MERCHANT_DISCARD_CANCEL + "')")
public ApiResponse<CargoDiscardResponses.Detail> cancel(@AuthenticationPrincipal AuthenticatedUser user,
@@ -25,7 +25,10 @@ public final class CargoDiscardResponses {
public record History(String fromStatus, String toStatus, String operatorId,
String remark, LocalDateTime createdAt) {}
public record Photo(Long fileId, String palletNo, String fileName, String contentType, Long sizeBytes,
String operatorId, LocalDateTime createdAt, LocalDateTime submittedAt) {}
public record Detail(Summary summary, BigDecimal feePerPackage, String remark,
String reviewedBy, String reviewRemark, List<Item> items,
List<History> histories) {}
List<History> histories, List<Photo> photos) {}
}
@@ -0,0 +1,22 @@
package com.baiyue.lastleg.discard.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("ll_cargo_discard_photo")
public class CargoDiscardPhoto {
@TableId private Long id;
private String customerId;
private Long applicationId;
private String palletNo;
private Long fileId;
private String fileName;
private String contentType;
private Long sizeBytes;
private String operatorId;
private LocalDateTime createdAt;
private LocalDateTime submittedAt;
}
@@ -0,0 +1,12 @@
package com.baiyue.lastleg.discard.gateway;
import org.springframework.web.multipart.MultipartFile;
public interface CargoDiscardPhotoFileGateway {
UploadedFile upload(String customerId, long taskId, long operatorId, MultipartFile file);
void validate(String customerId, long taskId, long fileId);
DownloadedFile download(String customerId, long taskId, long fileId);
record UploadedFile(long fileId, String fileName, String contentType, long sizeBytes) { }
record DownloadedFile(byte[] content, String fileName, String contentType) { }
}
@@ -0,0 +1,135 @@
package com.baiyue.lastleg.discard.gateway;
import com.baiyue.lastleg.config.LastlegRestClientFactory;
import com.baiyue.lastleg.config.LastlegRestClientProperties;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import java.io.IOException;
@Component
public class HttpCargoDiscardPhotoFileGateway implements CargoDiscardPhotoFileGateway {
private static final String BUSINESS_TYPE = "OVERSEAS_CARGO_DISCARD_PHOTO";
@Resource private RestClient.Builder restClientBuilder;
@Resource private LastlegRestClientFactory restClientFactory;
@Resource private LastlegRestClientProperties restClientProperties;
@Value("${baiyue.clients.file.url:http://127.0.0.1:9205}") private String baseUrl;
@Value("${baiyue.security.internal-service-token:}") private String internalToken;
private RestClient client;
@PostConstruct
void initializeClient() {
client = restClientFactory.build(restClientBuilder, baseUrl,
restClientProperties.getCargoDiscardPhotoFile());
}
@Override
public UploadedFile upload(String customerId, long taskId, long operatorId, MultipartFile file) {
long tenantId = tenantId(customerId);
MultipartBodyBuilder body = new MultipartBodyBuilder();
try {
body.part("file", new ByteArrayResource(file.getBytes()) {
@Override public String getFilename() { return file.getOriginalFilename(); }
}).contentType(file.getContentType() == null ? MediaType.APPLICATION_OCTET_STREAM
: MediaType.parseMediaType(file.getContentType()));
} catch (IOException exception) {
throw new IllegalArgumentException("Cannot read cargo-discard photo", exception);
}
MetadataEnvelope response = client.post()
.uri("/internal/file/v1/business-files/{tenantId}/{businessType}/{businessId}",
tenantId, BUSINESS_TYPE, taskId)
.header("X-Internal-Service-Token", internalToken)
.header("X-Operator-Id", String.valueOf(operatorId))
.contentType(MediaType.MULTIPART_FORM_DATA).body(body.build()).retrieve()
.onStatus(HttpStatusCode::isError, (request, result) -> {
throw new ResponseStatusException(result.getStatusCode().is4xxClientError()
? HttpStatus.BAD_REQUEST : HttpStatus.SERVICE_UNAVAILABLE,
"Cargo discard photo upload failed");
}).body(MetadataEnvelope.class);
requireSuccess(response, "Cargo discard photo upload failed");
Metadata data = response == null ? null : response.data();
if (data == null || data.fileId() == null) {
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,
"File service returned no cargo-discard photo metadata");
}
return new UploadedFile(data.fileId(), data.fileName(), data.contentType(),
data.sizeBytes() == null ? 0L : data.sizeBytes());
}
@Override
public void validate(String customerId, long taskId, long fileId) {
long tenantId = tenantId(customerId);
MetadataEnvelope response = client.get()
.uri("/internal/file/v1/business-files/{tenantId}/{businessType}/{businessId}/{fileId}",
tenantId, BUSINESS_TYPE, taskId, fileId)
.header("X-Internal-Service-Token", internalToken).retrieve()
.onStatus(HttpStatusCode::isError, (request, result) -> {
throw new ResponseStatusException(result.getStatusCode().is4xxClientError()
? HttpStatus.BAD_REQUEST : HttpStatus.SERVICE_UNAVAILABLE,
"Cargo discard photo does not exist or its ownership is invalid");
}).body(MetadataEnvelope.class);
requireSuccess(response, "Cargo discard photo does not exist or its ownership is invalid");
Metadata data = response == null ? null : response.data();
if (data == null || data.fileId() == null || data.fileId() != fileId
|| data.tenantId() == null || data.tenantId() != tenantId
|| !BUSINESS_TYPE.equals(data.businessType())
|| !String.valueOf(taskId).equals(data.businessId())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Cargo discard photo metadata does not match the PDA task");
}
}
@Override
public DownloadedFile download(String customerId, long taskId, long fileId) {
validate(customerId, taskId, fileId);
ResponseEntity<byte[]> response = client.get()
.uri("/internal/file/v1/business-files/{tenantId}/{businessType}/{businessId}/{fileId}/content",
tenantId(customerId), BUSINESS_TYPE, taskId, fileId)
.header("X-Internal-Service-Token", internalToken).retrieve()
.onStatus(HttpStatusCode::isError, (request, result) -> {
throw new ResponseStatusException(result.getStatusCode().is4xxClientError()
? HttpStatus.NOT_FOUND : HttpStatus.SERVICE_UNAVAILABLE, "Cargo discard photo download failed");
}).toEntity(byte[].class);
String fileName = response.getHeaders().getContentDisposition().getFilename();
String contentType = response.getHeaders().getContentType() == null
? MediaType.APPLICATION_OCTET_STREAM_VALUE : response.getHeaders().getContentType().toString();
return new DownloadedFile(response.getBody() == null ? new byte[0] : response.getBody(),
fileName == null ? "cargo-discard-photo" : fileName, contentType);
}
private static long tenantId(String customerId) {
try { return Long.parseLong(customerId); }
catch (NumberFormatException exception) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Customer ID must be numeric", exception);
}
}
private static void requireSuccess(MetadataEnvelope response, String fallback) {
if (response == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, fallback);
if ("200".equals(response.code())) return;
HttpStatus status = switch (String.valueOf(response.code())) {
case "A0400", "A0422" -> HttpStatus.BAD_REQUEST;
case "A0403" -> HttpStatus.FORBIDDEN;
case "A0404" -> HttpStatus.NOT_FOUND;
default -> HttpStatus.SERVICE_UNAVAILABLE;
};
throw new ResponseStatusException(status,
response.message() == null || response.message().isBlank() ? fallback : response.message());
}
private record MetadataEnvelope(String code, String message, Metadata data) { }
private record Metadata(Long fileId, Long tenantId, String businessType, String businessId,
String fileName, String contentType, Long sizeBytes) { }
}
@@ -19,4 +19,7 @@ public interface CargoDiscardApplicationMapper extends BaseMapper<CargoDiscardAp
@Select("SELECT * FROM ll_cargo_discard_application WHERE discard_no=#{discardNo} AND overseas_warehouse_id=#{warehouseId} LIMIT 1 FOR UPDATE")
CargoDiscardApplication findNoByWarehouse(@Param("discardNo") String discardNo, @Param("warehouseId") String warehouseId);
@Select("SELECT * FROM ll_cargo_discard_application WHERE discard_no=#{discardNo} AND overseas_warehouse_id=#{warehouseId} LIMIT 1")
CargoDiscardApplication readNoByWarehouse(@Param("discardNo") String discardNo, @Param("warehouseId") String warehouseId);
}
@@ -0,0 +1,14 @@
package com.baiyue.lastleg.discard.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.lastleg.discard.entity.CargoDiscardPhoto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface CargoDiscardPhotoMapper extends BaseMapper<CargoDiscardPhoto> {
@Select("SELECT * FROM ll_cargo_discard_photo WHERE customer_id=#{customerId} AND application_id=#{applicationId} ORDER BY created_at,id")
List<CargoDiscardPhoto> listApplication(@Param("customerId") String customerId, @Param("applicationId") Long applicationId);
}
@@ -24,6 +24,27 @@ public class PdaCargoDiscardController {
@Valid @RequestBody PdaCargoDiscardDtos.Scan request) {
return ApiResponse.success(service.resolve(PdaWarehouseIdentity.require(user), request.palletNo()));
}
@PostMapping(value = "/{discardNo}/pallets/{palletNo}/photos/files", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
public ApiResponse<com.baiyue.lastleg.discard.dto.CargoDiscardResponses.Photo> uploadPhoto(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String discardNo, @PathVariable String palletNo,
@RequestParam("file") org.springframework.web.multipart.MultipartFile file) {
return ApiResponse.success(service.uploadPhoto(PdaWarehouseIdentity.require(user), discardNo, palletNo,
user.userId() == null ? 0L : user.userId(), file));
}
@GetMapping("/{discardNo}/pallets/{palletNo}/photos/files")
public ApiResponse<List<com.baiyue.lastleg.discard.dto.CargoDiscardResponses.Photo>> photos(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String discardNo, @PathVariable String palletNo) {
return ApiResponse.success(service.photos(PdaWarehouseIdentity.require(user), discardNo, palletNo));
}
@GetMapping("/{discardNo}/pallets/{palletNo}/photos/files/{fileId}/download")
public org.springframework.http.ResponseEntity<byte[]> downloadPhoto(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String discardNo, @PathVariable String palletNo, @PathVariable long fileId) {
var file = service.downloadPhoto(PdaWarehouseIdentity.require(user), discardNo, palletNo, fileId);
return org.springframework.http.ResponseEntity.ok().cacheControl(org.springframework.http.CacheControl.noStore())
.header(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION,
org.springframework.http.ContentDisposition.inline().filename(file.fileName(), java.nio.charset.StandardCharsets.UTF_8).build().toString())
.contentType(org.springframework.http.MediaType.parseMediaType(file.contentType())).body(file.content());
}
@PostMapping("/{discardNo}/pallets/{palletNo}/confirm")
public ApiResponse<PdaCargoDiscardDtos.PalletTask> confirm(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String discardNo, @PathVariable String palletNo, @Valid @RequestBody PdaCargoDiscardDtos.Confirm request) {
@@ -12,7 +12,8 @@ public final class PdaCargoDiscardDtos {
private PdaCargoDiscardDtos() { }
public record Scan(@NotBlank @Size(max = 128) String palletNo) { }
public record ActualLine(@NotNull Long itemId, @PositiveOrZero int actualPackages) { }
public record Confirm(@NotEmpty List<@Valid ActualLine> lines) { }
public record Confirm(@NotEmpty List<@Valid ActualLine> lines,
@NotEmpty @Size(max = 20) List<@NotNull @jakarta.validation.constraints.Positive Long> photoFileIds) { }
public record Sku(Long itemId, String skuCode, String productName, int plannedPackages,
Integer actualPackages, String status) { }
public record PalletTask(String discardNo, String customerId, String overseasWarehouseId,
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
@Service
public class PdaCargoDiscardService {
@Resource private com.baiyue.lastleg.discard.service.CargoDiscardPhotoService photoService;
@Resource private CargoDiscardApplicationMapper applications;
@Resource private CargoDiscardItemMapper items;
@Resource private CargoDiscardCartonMapper cartons;
@@ -78,6 +79,8 @@ public class PdaCargoDiscardService {
|| !application.getCustomerId().equals(item.getCustomerId())
|| !Objects.equals(application.getId(), item.getApplicationId())))
throw conflict("废弃申请终态存在未完成明细,不能恢复作业结果");
photoService.confirm(application, task.palletNo(), request.photoFileIds(),
palletItems.stream().allMatch(item -> "COMPLETED".equals(item.getPdaStatus())));
// A completed pallet is an immutable result, including the final pallet after application completion.
if (palletItems.stream().allMatch(item -> "COMPLETED".equals(item.getPdaStatus()))) return task;
if (terminal) throw conflict("废弃终态不能执行未完成托盘");
@@ -109,6 +112,36 @@ public class PdaCargoDiscardService {
.findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "托盘不属于该废弃单"));
}
@Transactional
public CargoDiscardResponses.Photo uploadPhoto(PdaWarehouseIdentity identity, String discardNo, String palletNo,
long operatorUserId, org.springframework.web.multipart.MultipartFile file) {
CargoDiscardApplication application = photoApplication(identity, discardNo, palletNo, true);
var task = palletTasks(application).stream().filter(t -> t.palletNo().equals(palletNo.trim())).findFirst().orElseThrow();
if (!CargoDiscardService.APPROVED.equals(application.getStatus()) || "COMPLETED".equals(task.status()))
throw conflict("已完成或未审核的废弃托盘不能上传照片");
return photoService.upload(application, task.palletNo(), identity.operatorKey(), operatorUserId, file);
}
@Transactional(readOnly = true)
public List<CargoDiscardResponses.Photo> photos(PdaWarehouseIdentity identity, String discardNo, String palletNo) {
return photoService.list(photoApplication(identity, discardNo, palletNo, false), palletNo.trim(), false);
}
@Transactional(readOnly = true)
public com.baiyue.lastleg.discard.gateway.CargoDiscardPhotoFileGateway.DownloadedFile downloadPhoto(
PdaWarehouseIdentity identity, String discardNo, String palletNo, long fileId) {
return photoService.download(photoApplication(identity, discardNo, palletNo, false), palletNo.trim(), fileId, false);
}
private CargoDiscardApplication photoApplication(PdaWarehouseIdentity identity, String discardNo, String palletNo, boolean lock) {
var warehouseId = String.valueOf(identity.overseasWarehouseId());
var application = lock ? applications.findNoByWarehouse(discardNo, warehouseId) : applications.readNoByWarehouse(discardNo, warehouseId);
if (application == null) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "废弃任务不存在或不属于当前仓库");
if (palletTasks(application).stream().noneMatch(t -> t.palletNo().equals(palletNo.trim())))
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "托盘不属于该废弃单");
return application;
}
private List<PdaCargoDiscardDtos.PalletTask> palletTasks(CargoDiscardApplication application) {
return items.listApplication(application.getCustomerId(), application.getId()).stream()
.collect(Collectors.groupingBy(this::physicalGroupKey, LinkedHashMap::new, Collectors.toList()))
@@ -0,0 +1,75 @@
package com.baiyue.lastleg.discard.service;
import com.baiyue.lastleg.discard.dto.CargoDiscardResponses;
import com.baiyue.lastleg.discard.entity.CargoDiscardApplication;
import com.baiyue.lastleg.discard.entity.CargoDiscardPhoto;
import com.baiyue.lastleg.discard.gateway.CargoDiscardPhotoFileGateway;
import com.baiyue.lastleg.discard.mapper.CargoDiscardPhotoMapper;
import jakarta.annotation.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@Service
public class CargoDiscardPhotoService {
@Resource private CargoDiscardPhotoMapper photos;
@Resource private CargoDiscardPhotoFileGateway files;
public List<CargoDiscardResponses.Photo> list(CargoDiscardApplication application, String palletNo, boolean submittedOnly) {
return rows(application, palletNo).stream().filter(p -> !submittedOnly || p.getSubmittedAt() != null)
.map(this::view).toList();
}
public CargoDiscardResponses.Photo upload(CargoDiscardApplication application, String palletNo,
String operatorId, long operatorUserId, MultipartFile file) {
if (rows(application, palletNo).size() >= 20) throw conflict("每个托盘最多上传 20 张废弃照片");
var uploaded = files.upload(application.getCustomerId(), application.getId(), operatorUserId, file);
CargoDiscardPhoto photo = new CargoDiscardPhoto();
photo.setCustomerId(application.getCustomerId()); photo.setApplicationId(application.getId());
photo.setPalletNo(palletNo); photo.setFileId(uploaded.fileId()); photo.setFileName(uploaded.fileName());
photo.setContentType(uploaded.contentType()); photo.setSizeBytes(uploaded.sizeBytes());
photo.setOperatorId(operatorId); photo.setCreatedAt(LocalDateTime.now()); photos.insert(photo);
return view(photo);
}
// Called under the application row lock, in the same transaction as inventory deduction.
public void confirm(CargoDiscardApplication application, String palletNo, List<Long> fileIds, boolean completed) {
if (fileIds == null || fileIds.isEmpty() || fileIds.size() > 20
|| fileIds.stream().anyMatch(id -> id == null || id <= 0) || new HashSet<>(fileIds).size() != fileIds.size())
throw conflict("每个托盘须提交 1–20 张不重复的废弃照片");
var rows = rows(application, palletNo);
var submitted = rows.stream().filter(p -> p.getSubmittedAt() != null).map(CargoDiscardPhoto::getFileId).toList();
if (completed) {
if (!new HashSet<>(submitted).equals(new HashSet<>(fileIds))) throw conflict("已完成托盘的废弃照片不能修改");
return;
}
var selected = rows.stream().filter(p -> fileIds.contains(p.getFileId())).toList();
if (selected.size() != fileIds.size()) throw conflict("照片不属于当前废弃单或托盘");
selected.forEach(p -> files.validate(application.getCustomerId(), application.getId(), p.getFileId()));
var now = LocalDateTime.now();
selected.forEach(p -> { p.setSubmittedAt(now); photos.updateById(p); });
}
public CargoDiscardPhotoFileGateway.DownloadedFile download(CargoDiscardApplication application, String palletNo,
long fileId, boolean submittedOnly) {
var photo = rows(application, palletNo).stream().filter(p -> Objects.equals(p.getFileId(), fileId))
.filter(p -> !submittedOnly || p.getSubmittedAt() != null).findFirst()
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "废弃照片不存在或尚未提交"));
return files.download(application.getCustomerId(), application.getId(), photo.getFileId());
}
private List<CargoDiscardPhoto> rows(CargoDiscardApplication application, String palletNo) {
return photos.listApplication(application.getCustomerId(), application.getId()).stream()
.filter(p -> palletNo == null || palletNo.equals(p.getPalletNo())).toList();
}
private CargoDiscardResponses.Photo view(CargoDiscardPhoto photo) {
return new CargoDiscardResponses.Photo(photo.getFileId(), photo.getPalletNo(), photo.getFileName(),
photo.getContentType(), photo.getSizeBytes(), photo.getOperatorId(), photo.getCreatedAt(), photo.getSubmittedAt());
}
private static ResponseStatusException conflict(String message) { return new ResponseStatusException(HttpStatus.CONFLICT, message); }
}
@@ -60,6 +60,7 @@ public class CargoDiscardService {
public static final String COMPLETED = "COMPLETED";
private static final Set<String> STATUSES = Set.of(PENDING_REVIEW, APPROVED, REJECTED, CANCELLED, PDA_COMPLETED, COMPLETED);
@Resource private CargoDiscardPhotoService photoService;
@Resource private CargoDiscardApplicationMapper applications;
@Resource private CargoDiscardItemMapper items;
@Resource private CargoDiscardCartonMapper discardCartons;
@@ -214,7 +215,13 @@ public class CargoDiscardService {
.map(value -> new CargoDiscardResponses.History(value.getFromStatus(), value.getToStatus(),
value.getOperatorId(), value.getRemark(), value.getCreatedAt())).toList();
return new CargoDiscardResponses.Detail(summary(application), application.getFeePerPackage(),
application.getRemark(), application.getReviewedBy(), application.getReviewRemark(), itemViews, historyViews);
application.getRemark(), application.getReviewedBy(), application.getReviewRemark(), itemViews, historyViews, photoService.list(application, null, true));
}
public com.baiyue.lastleg.discard.gateway.CargoDiscardPhotoFileGateway.DownloadedFile downloadPhoto(String customerId, String discardNo, long fileId) {
CargoDiscardApplication application = applications.findNo(customerId, discardNo);
if (application == null) throw notFound("Cargo discard application not found");
return photoService.download(application, null, fileId, true);
}
@Transactional