This commit is contained in:
2026-09-09 12:02:12 +08:00
parent 9d492cb5ed
commit c3ae15abfe
19 changed files with 325 additions and 13 deletions
@@ -14,6 +14,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ContentDisposition;
import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import java.nio.charset.StandardCharsets;
import org.springframework.web.server.ResponseStatusException;
@RestController
@@ -55,6 +60,29 @@ public class RelabelApplicationController {
requirePlatform(user);
return ApiResponse.success(service.changeStatus(null, relabelNo, operator(user), request));
}
@PostMapping(value = "/{relabelNo}/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PreAuthorize("hasAuthority('" + OverseasPermissions.RELABEL_ORDER_CREATE + "')")
public ApiResponse<RelabelResponses.Detail> uploadFile(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String relabelNo, @RequestParam String scope, @RequestParam(required = false) Long itemId,
@RequestPart("file") MultipartFile file) {
requirePlatform(user);
String customerId = service.customerId(relabelNo);
return ApiResponse.success("ITEM".equalsIgnoreCase(scope)
? service.uploadItemFile(customerId, relabelNo, itemId == null ? 0L : itemId, operatorLong(user), file)
: service.uploadTotalFile(customerId, relabelNo, operatorLong(user), file));
}
@GetMapping("/{relabelNo}/files/{fileId}/download")
@PreAuthorize("hasAuthority('" + OverseasPermissions.RELABEL_ORDER_LIST + "')")
public ResponseEntity<byte[]> downloadFile(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String relabelNo, @PathVariable long fileId, @RequestParam String scope,
@RequestParam(required = false) Long itemId) {
requirePlatform(user);
var file = service.downloadFile(service.customerId(relabelNo), relabelNo, scope, itemId, fileId);
return ResponseEntity.ok().header("Content-Disposition", ContentDisposition.inline().filename(file.fileName(), StandardCharsets.UTF_8).build().toString())
.contentType(org.springframework.http.MediaType.parseMediaType(file.contentType())).contentLength(file.content().length).body(file.content());
}
private void requirePlatform(AuthenticatedUser user) {
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
if (!"PLATFORM".equals(user.appCode()) || user.tenantId() != 0L) {
@@ -69,4 +97,5 @@ public class RelabelApplicationController {
return value;
}
private String operator(AuthenticatedUser user) { return user.userId() == null ? user.username() : String.valueOf(user.userId()); }
private long operatorLong(AuthenticatedUser user) { return user.userId() == null ? 0L : user.userId(); }
}
@@ -11,6 +11,7 @@ public final class RelabelResponses {
String customerOrderNo, String overseasWarehouseId, LocalDateTime labelDeadline,
String contactName, String contactPhone, String status, int itemCount,
int totalPackageCount, int notifiedPackageCount, String skuCodes,
String totalFileId, String totalFileName, String totalFileUrl,
BigDecimal chargeAmount, String chargeCurrency, String walletStatus,
String walletErrorMessage,
LocalDateTime createdAt) {}
@@ -21,6 +21,9 @@ public class RelabelApplication {
private String contactName;
private String contactPhone;
private String remark;
private String totalFileId;
private String totalFileName;
private String totalFileUrl;
private String status;
private Integer itemCount;
private Integer totalPackageCount;
@@ -0,0 +1,58 @@
package com.baiyue.lastleg.relabel.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.*;
import org.springframework.http.client.MultipartBodyBuilder;
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 HttpRelabelDocumentFileGateway implements RelabelDocumentFileGateway {
@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.getRelabelPhotoFile()); }
@Override public UploadedFile upload(String customerId, String businessType, String businessId, long operatorId, MultipartFile file) {
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_PDF : MediaType.parseMediaType(file.getContentType())); }
catch (IOException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "无法读取换标PDF", e); }
MetadataEnvelope response = client.post().uri("/internal/file/v1/business-files/{tenantId}/{businessType}/{businessId}", tenantId(customerId), businessType, businessId)
.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(HttpStatus.BAD_REQUEST, "换标PDF上传失败"); })
.body(MetadataEnvelope.class);
requireSuccess(response, "换标PDF上传失败"); Metadata data = response == null ? null : response.data();
if (data == null || data.fileId() == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "文件服务未返回换标PDF信息");
return new UploadedFile(data.fileId(), data.fileName(), data.contentType(), data.sizeBytes() == null ? 0 : data.sizeBytes());
}
@Override public DownloadedFile download(String customerId, String businessType, String businessId, long fileId) {
ResponseEntity<byte[]> response = client.get().uri("/internal/file/v1/business-files/{tenantId}/{businessType}/{businessId}/{fileId}/content", tenantId(customerId), businessType, businessId, 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, "换标PDF读取失败");
}).toEntity(byte[].class);
String name = response.getHeaders().getContentDisposition().getFilename();
return new DownloadedFile(response.getBody() == null ? new byte[0] : response.getBody(), name == null ? fileId + ".pdf" : name,
response.getHeaders().getContentType() == null ? MediaType.APPLICATION_PDF_VALUE : response.getHeaders().getContentType().toString());
}
private long tenantId(String value) { try { return Long.parseLong(value); } catch (NumberFormatException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "客户ID格式无效", e); } }
private static void requireSuccess(MetadataEnvelope response, String message) { if (response == null || !"200".equals(response.code())) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, message); }
private record MetadataEnvelope(String code, String message, Metadata data) { }
private record Metadata(Long fileId, String fileName, String contentType, Long sizeBytes) { }
}
@@ -0,0 +1,11 @@
package com.baiyue.lastleg.relabel.gateway;
import org.springframework.web.multipart.MultipartFile;
public interface RelabelDocumentFileGateway {
UploadedFile upload(String customerId, String businessType, String businessId, long operatorId, MultipartFile file);
DownloadedFile download(String customerId, String businessType, String businessId, long fileId);
record UploadedFile(long fileId, String fileName, String contentType, long sizeBytes) { }
record DownloadedFile(byte[] content, String fileName, String contentType) { }
}
@@ -366,6 +366,16 @@ public class RelabelApplicationService implements WalletResultHandler {
private LastlegOrder insertTailOrder(String customerId, RelabelRequests.Create request, String requestHash,
String relabelNo, String warehouseId, int packageCount,
LocalDateTime now) {
String customerOrderNo = request.customerOrderNo().trim();
LastlegOrder existingReturn = orders.selectOne(new LambdaQueryWrapper<LastlegOrder>()
.eq(LastlegOrder::getCustomerId, customerId).eq(LastlegOrder::getOrderType, "RETURN_RELABEL")
.and(w -> w.eq(LastlegOrder::getSourceOrderNo, customerOrderNo).or().eq(LastlegOrder::getOrderNo, customerOrderNo))
.last("LIMIT 1"));
if (existingReturn != null) {
existingReturn.setRelabelOrderNo(relabelNo); existingReturn.setRelabelStatus(PENDING_PROCESSING);
existingReturn.setSubOrderStatus("RETURN_RELABEL:" + PENDING_PROCESSING); existingReturn.setPackageCount(packageCount);
existingReturn.setUpdatedAt(now); orders.updateById(existingReturn); return existingReturn;
}
LastlegOrder order = new LastlegOrder();
order.setId(IdWorker.getId());
order.setOrderNo(orderNoGenerator.next("RELABEL", warehouseId, order.getId()));