This commit is contained in:
2026-08-12 20:36:11 +08:00
parent 0380c0553b
commit 0ed1531683
26 changed files with 1690 additions and 15 deletions
@@ -0,0 +1,20 @@
package com.baiyue.firstleg.client;
import com.baiyue.common.core.api.ApiResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
/** Validates that a referenced File object belongs to the exact loading business resource. */
@FeignClient(name = "file-service", contextId = "firstlegLoadingDocumentFileClient",
url = "${baiyue.clients.file.url:}", path = "/internal/file/v1/business-files")
public interface LoadingDocumentFileClient {
@GetMapping("/{tenantId}/{businessType}/{businessId}/{fileId}")
ApiResponse<Metadata> metadata(@RequestHeader("X-Internal-Service-Token") String token,
@PathVariable long tenantId, @PathVariable String businessType,
@PathVariable String businessId, @PathVariable long fileId);
record Metadata(Long fileId, Long tenantId, String businessType, String businessId,
String fileName, String sha256, String contentType, Long sizeBytes) {}
}
@@ -11,6 +11,10 @@ import com.baiyue.firstleg.dto.ContainerDtos.TransportUpdateRequest;
import com.baiyue.firstleg.entity.ContainerBookingEntity;
import com.baiyue.firstleg.service.ContainerService;
import com.baiyue.firstleg.service.FirstlegScopeGuard;
import com.baiyue.firstleg.service.LoadingListService;
import com.baiyue.firstleg.dto.LoadingListDtos.BusinessDocument;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentAttachRequest;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentCompleteRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@@ -25,6 +29,7 @@ import org.springframework.web.bind.annotation.*;
@RequestMapping("/api/platform/v1/containers")
public class PlatformContainerController {
@Resource private ContainerService service;
@Resource private LoadingListService loadingLists;
@GetMapping @PreAuthorize("hasAuthority('platform:firstleg:container:list')")
public ApiResponse<PageResult<ContainerBookingEntity>> page(@AuthenticationPrincipal AuthenticatedUser user,
@Valid Query query) {
@@ -60,4 +65,37 @@ public class PlatformContainerController {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.updateTransport(id, user.userId(), user.username(), request));
}
@GetMapping("/{id}/clearance")
@PreAuthorize("hasAuthority('platform:firstleg:container:view')")
@Operation(summary = "查询货柜目的港清关资料")
public ApiResponse<BusinessDocument> clearance(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long id) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(loadingLists.containerClearance(id));
}
@PostMapping("/{id}/clearance/document")
@PreAuthorize("hasAuthority('platform:firstleg:container:edit')")
@Operation(summary = "上传或替换货柜目的港清关资料")
public ApiResponse<BusinessDocument> attachClearance(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long id,
@RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody DocumentAttachRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(loadingLists.attachContainerClearance(
id, user.userId(), user.username(), key, request));
}
@PostMapping("/{id}/clearance/complete")
@PreAuthorize("hasAuthority('platform:firstleg:container:edit')")
@Operation(summary = "确认货柜目的港清关完成")
public ApiResponse<BusinessDocument> completeClearance(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long id,
@RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody DocumentCompleteRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(loadingLists.completeContainerClearance(
id, user.userId(), user.username(), key, request));
}
}
@@ -10,6 +10,9 @@ import com.baiyue.firstleg.dto.LoadingListDtos.PreloadDetail;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadSummary;
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
import com.baiyue.firstleg.dto.LoadingListDtos.BusinessDocument;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentAttachRequest;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentCompleteRequest;
import com.baiyue.firstleg.service.FirstlegScopeGuard;
import com.baiyue.firstleg.service.LoadingListService;
import io.swagger.v3.oas.annotations.Operation;
@@ -22,6 +25,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -89,4 +93,38 @@ public class PlatformLoadingListController {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.confirmedDetail(planId, warehouseId));
}
@Operation(summary = "查询装柜清单出口报关文档")
@GetMapping("/loading-lists/{planId}/customs")
@PreAuthorize("hasAuthority('platform:firstleg:loading-list:view')")
public ApiResponse<BusinessDocument> declaration(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable @Positive long planId,
@RequestParam @Positive long warehouseId) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.loadingDeclaration(planId, warehouseId));
}
@Operation(summary = "上传或替换装柜清单出口报关文档")
@PostMapping("/loading-lists/{planId}/customs/document")
@PreAuthorize("hasAuthority('platform:firstleg:customs:manage')")
public ApiResponse<BusinessDocument> attachDeclaration(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable @Positive long planId,
@RequestParam @Positive long warehouseId, @RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody DocumentAttachRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.attachLoadingDeclaration(
planId, warehouseId, user.userId(), user.username(), key, request));
}
@Operation(summary = "确认装柜清单出口报关完成")
@PostMapping("/loading-lists/{planId}/customs/complete")
@PreAuthorize("hasAuthority('platform:firstleg:customs:manage')")
public ApiResponse<BusinessDocument> completeDeclaration(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable @Positive long planId,
@RequestParam @Positive long warehouseId, @RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody DocumentCompleteRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.completeLoadingDeclaration(
planId, warehouseId, user.userId(), user.username(), key, request));
}
}
@@ -3,6 +3,7 @@ package com.baiyue.firstleg.dto;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
@@ -57,6 +58,26 @@ public final class LoadingListDtos {
) {
}
public record DocumentAttachRequest(
@NotNull @Positive Long fileId,
@NotNull @Min(0) Integer expectedVersion,
@Size(max = 500) String operationReason
) {
}
public record DocumentCompleteRequest(
@NotNull @Min(0) Integer expectedVersion,
@Size(max = 500) String operationReason
) {
}
public record BusinessDocument(
Long id, String documentType, Long fileId, String fileName, String fileHash,
Long uploadedBy, String uploadedByName, LocalDateTime uploadedAt,
Long completedBy, String completedByName, LocalDateTime completedAt, Integer version
) {
}
public record Customer(Long accountId, String customerName) {
}
@@ -0,0 +1,24 @@
package com.baiyue.firstleg.entity;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class ContainerDocumentEntity {
private Long id;
private Long containerBookingId;
private String documentType;
private Long fileId;
private String fileName;
private String fileHash;
private Long uploadedBy;
private String uploadedByName;
private LocalDateTime createdAt;
private Long completedBy;
private String completedByName;
private LocalDateTime completedAt;
private Integer version;
}
@@ -3,6 +3,7 @@ package com.baiyue.firstleg.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
import com.baiyue.firstleg.entity.ContainerDocumentEntity;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.annotations.Insert;
@@ -99,6 +100,59 @@ public interface LoadingListQueryMapper {
""" + AGGREGATE_GROUP)
AggregateRow selectAggregate(@Param("planId") long planId);
@Select("""
SELECT
""" + AGGREGATE_COLUMNS + """
FROM fl_loading_plan p
JOIN fl_container_booking b ON b.id = p.container_booking_id
LEFT JOIN fl_loading_plan_item i ON i.loading_plan_id = p.id AND i.deleted = 0
WHERE p.container_booking_id = #{bookingId}
GROUP BY
""" + AGGREGATE_GROUP)
AggregateRow selectAggregateByBooking(@Param("bookingId") long bookingId);
@Select("""
SELECT id, container_booking_id, document_type, file_id, file_name, file_hash,
uploaded_by, uploaded_by_name, created_at, completed_by, completed_by_name,
completed_at, version
FROM fl_container_document
WHERE container_booking_id = #{bookingId} AND document_type = #{documentType}
""")
ContainerDocumentEntity selectDocument(@Param("bookingId") long bookingId,
@Param("documentType") String documentType);
@Insert("""
INSERT INTO fl_container_document
(id, container_booking_id, document_type, file_id, file_name, file_hash,
uploaded_by, uploaded_by_name, created_at, updated_at, version)
VALUES
(#{id}, #{containerBookingId}, #{documentType}, #{fileId}, #{fileName}, #{fileHash},
#{uploadedBy}, #{uploadedByName}, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3), 0)
""")
int insertDocument(ContainerDocumentEntity value);
@Update("""
UPDATE fl_container_document
SET file_id = #{fileId}, file_name = #{fileName}, file_hash = #{fileHash},
uploaded_by = #{uploadedBy}, uploaded_by_name = #{uploadedByName},
completed_by = NULL, completed_by_name = NULL, completed_at = NULL,
updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{id} AND version = #{version}
""")
int replaceDocument(ContainerDocumentEntity value);
@Update("""
UPDATE fl_container_document
SET completed_by = #{operatorId}, completed_by_name = #{operatorName},
completed_at = CURRENT_TIMESTAMP(3), updated_at = CURRENT_TIMESTAMP(3),
version = version + 1
WHERE id = #{documentId} AND version = #{expectedVersion} AND completed_at IS NULL
""")
int completeDocument(@Param("documentId") long documentId,
@Param("expectedVersion") int expectedVersion,
@Param("operatorId") long operatorId,
@Param("operatorName") String operatorName);
@Select("""
SELECT id AS item_id, account_id, customer_name, inbound_id, inbound_detail_id,
inbound_code, sku_code, destination_warehouse_name, product_name_zh,
@@ -12,6 +12,7 @@ import com.baiyue.firstleg.dto.LoadingListDtos.Item;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadDetail;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadSummary;
import com.baiyue.firstleg.entity.ContainerDocumentEntity;
import com.baiyue.firstleg.mapper.LoadingListQueryMapper;
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow;
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.CustomerRow;
@@ -50,6 +51,14 @@ public class LoadingListRepository {
return mapper.selectAggregate(planId);
}
public AggregateRow aggregateByBooking(long bookingId) { return mapper.selectAggregateByBooking(bookingId); }
public ContainerDocumentEntity document(long bookingId, String type) { return mapper.selectDocument(bookingId, type); }
public int createDocument(ContainerDocumentEntity value) { return mapper.insertDocument(value); }
public int replaceDocument(ContainerDocumentEntity value) { return mapper.replaceDocument(value); }
public int completeDocument(long id, int version, long operatorId, String operatorName) {
return mapper.completeDocument(id, version, operatorId, operatorName);
}
public PreloadDetail preloadDetail(AggregateRow row) {
List<ItemRow> items = mapper.selectItems(row.getPlanId());
List<Customer> customers = items.stream()
@@ -11,6 +11,11 @@ import com.baiyue.firstleg.dto.LoadingListDtos.PreloadDetail;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadSummary;
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
import com.baiyue.firstleg.dto.LoadingListDtos.BusinessDocument;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentAttachRequest;
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentCompleteRequest;
import com.baiyue.firstleg.client.LoadingDocumentFileClient;
import com.baiyue.firstleg.entity.ContainerDocumentEntity;
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow;
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.HistoryRow;
import com.baiyue.firstleg.repository.LoadingListRepository;
@@ -18,6 +23,10 @@ import com.baiyue.firstleg.repository.LoadingPickRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import feign.FeignException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
@@ -33,12 +42,20 @@ import java.util.Set;
@Service
public class LoadingListService {
private static final Set<String> PRELOAD_STATUSES = Set.of("EDITING", "LOADING");
public static final String DESTINATION_CLEARANCE = "DESTINATION_CLEARANCE";
public static final String CUSTOMS_DECLARATION = "CUSTOMS_DECLARATION";
private static final String CLEARANCE_FILE_TYPE = "CONTAINER_CLEARANCE_DOCUMENT";
private static final String DECLARATION_FILE_TYPE = "LOADING_CUSTOMS_DOCUMENT";
@Resource
private LoadingListRepository repository;
@Resource
private LoadingPickRepository pickRepository;
@Resource
private ObjectMapper objectMapper;
@Resource
private LoadingDocumentFileClient documentFiles;
@Value("${baiyue.security.internal-service-token:}")
private String internalToken;
public PageResult<PreloadSummary> preloadPage(PreloadQuery query) {
validateRange(query.createdFrom(), query.createdTo());
@@ -74,6 +91,182 @@ public class LoadingListService {
return repository.confirmedDetail(row);
}
public BusinessDocument containerClearance(long bookingId) {
requireBookingPlan(bookingId);
return document(repository.document(bookingId, DESTINATION_CLEARANCE));
}
@Transactional
@BusinessAudit(event = "CONTAINER_CLEARANCE_DOCUMENT_ATTACH", resourceType = "CONTAINER",
resourceId = "#bookingId", description = "上传目的港清关资料")
public BusinessDocument attachContainerClearance(long bookingId, long operatorId, String operatorName,
String key, DocumentAttachRequest request) {
AggregateRow plan = requireBookingPlan(bookingId);
return attachDocument(plan, DESTINATION_CLEARANCE, CLEARANCE_FILE_TYPE,
Long.toString(bookingId), operatorId, operatorName, key, request);
}
@Transactional
@BusinessAudit(event = "CONTAINER_CLEARANCE_COMPLETE", resourceType = "CONTAINER",
resourceId = "#bookingId", description = "确认目的港清关完成")
public BusinessDocument completeContainerClearance(long bookingId, long operatorId, String operatorName,
String key, DocumentCompleteRequest request) {
return completeDocument(requireBookingPlan(bookingId), DESTINATION_CLEARANCE,
operatorId, operatorName, key, request);
}
public BusinessDocument loadingDeclaration(long planId, long warehouseId) {
AggregateRow plan = requirePlan(planId);
requireWarehouse(plan, warehouseId);
requireCompletedPlan(plan);
return document(repository.document(plan.getContainerBookingId(), CUSTOMS_DECLARATION));
}
@Transactional
@BusinessAudit(event = "LOADING_CUSTOMS_DOCUMENT_ATTACH", resourceType = "LOADING_PLAN",
resourceId = "#planId", description = "上传出口报关文档")
public BusinessDocument attachLoadingDeclaration(long planId, long warehouseId, long operatorId,
String operatorName, String key,
DocumentAttachRequest request) {
AggregateRow plan = requirePlan(planId);
requireWarehouse(plan, warehouseId);
requireCompletedPlan(plan);
return attachDocument(plan, CUSTOMS_DECLARATION, DECLARATION_FILE_TYPE,
Long.toString(planId), operatorId, operatorName, key, request);
}
@Transactional
@BusinessAudit(event = "LOADING_CUSTOMS_COMPLETE", resourceType = "LOADING_PLAN",
resourceId = "#planId", description = "确认出口报关完成")
public BusinessDocument completeLoadingDeclaration(long planId, long warehouseId, long operatorId,
String operatorName, String key,
DocumentCompleteRequest request) {
AggregateRow plan = requirePlan(planId);
requireWarehouse(plan, warehouseId);
requireCompletedPlan(plan);
return completeDocument(plan, CUSTOMS_DECLARATION, operatorId, operatorName, key, request);
}
private BusinessDocument attachDocument(AggregateRow plan, String documentType, String businessType,
String businessId, long operatorId, String operatorName,
String key, DocumentAttachRequest request) {
String requestHash = sha256("ATTACH|" + documentType + '|' + plan.getPlanId() + '|'
+ request.fileId() + '|' + request.expectedVersion() + '|' + trim(request.operationReason()));
String operationId = operationId("ATTACH_" + documentType, plan.getPlanId(), key);
HistoryRow replay = repository.history(operationId);
if (replay != null) {
requireSameRequest(replay, requestHash);
return requireDocument(plan.getContainerBookingId(), documentType);
}
ContainerDocumentEntity current = repository.document(plan.getContainerBookingId(), documentType);
if (current == null && request.expectedVersion() != 0) throw conflict("资料版本已变化,请刷新后重试");
if (current != null && !java.util.Objects.equals(current.getVersion(), request.expectedVersion())) {
throw conflict("资料版本已变化,请刷新后重试");
}
if (current != null && current.getCompletedAt() != null) throw conflict("已完成的资料不可替换");
LoadingDocumentFileClient.Metadata metadata = validateDocumentFile(
businessType, businessId, request.fileId());
Map<String, Object> beforeSnapshot = documentSnapshot(current);
ContainerDocumentEntity value = current == null ? new ContainerDocumentEntity() : current;
if (current == null) value.setId(IdWorker.getId());
value.setContainerBookingId(plan.getContainerBookingId()); value.setDocumentType(documentType);
value.setFileId(metadata.fileId()); value.setFileName(metadata.fileName()); value.setFileHash(metadata.sha256());
value.setUploadedBy(operatorId); value.setUploadedByName(trim(operatorName));
try {
int changed = current == null ? repository.createDocument(value) : repository.replaceDocument(value);
if (changed != 1) throw conflict("资料版本已变化,请刷新后重试");
} catch (DuplicateKeyException exception) {
throw conflict("资料已被其他操作上传,请刷新后重试");
}
ContainerDocumentEntity saved = requireDocumentEntity(plan.getContainerBookingId(), documentType);
repository.appendHistory(plan.getPlanId(), operationId, "ATTACH_" + documentType,
plan.getPlanStatus(), plan.getPlanStatus(), json(beforeSnapshot),
json(values("requestHash", requestHash, "document", documentSnapshot(saved))),
operatorId, trim(operatorName), trim(request.operationReason()));
return document(saved);
}
private BusinessDocument completeDocument(AggregateRow plan, String documentType, long operatorId,
String operatorName, String key,
DocumentCompleteRequest request) {
String requestHash = sha256("COMPLETE|" + documentType + '|' + plan.getPlanId() + '|'
+ request.expectedVersion() + '|' + trim(request.operationReason()));
String operationId = operationId("COMPLETE_" + documentType, plan.getPlanId(), key);
HistoryRow replay = repository.history(operationId);
if (replay != null) {
requireSameRequest(replay, requestHash);
return requireDocument(plan.getContainerBookingId(), documentType);
}
ContainerDocumentEntity current = requireDocumentEntity(plan.getContainerBookingId(), documentType);
if (current.getCompletedAt() != null) {
if (!java.util.Objects.equals(current.getVersion(), request.expectedVersion())) {
throw conflict("资料版本已变化,请刷新后重试");
}
return document(current);
}
if (repository.completeDocument(current.getId(), request.expectedVersion(), operatorId,
trim(operatorName)) != 1) throw conflict("资料版本已变化,请刷新后重试");
ContainerDocumentEntity saved = requireDocumentEntity(plan.getContainerBookingId(), documentType);
repository.appendHistory(plan.getPlanId(), operationId, "COMPLETE_" + documentType,
plan.getPlanStatus(), plan.getPlanStatus(), json(documentSnapshot(current)),
json(values("requestHash", requestHash, "document", documentSnapshot(saved))),
operatorId, trim(operatorName), trim(request.operationReason()));
return document(saved);
}
private LoadingDocumentFileClient.Metadata validateDocumentFile(String businessType, String businessId,
long fileId) {
try {
var response = documentFiles.metadata(internalToken, 0, businessType, businessId, fileId);
if (response == null || !response.successful() || response.data() == null
|| !Long.valueOf(fileId).equals(response.data().fileId())
|| !Long.valueOf(0).equals(response.data().tenantId())
|| !businessType.equals(response.data().businessType())
|| !businessId.equals(response.data().businessId())
|| !StringUtils.hasText(response.data().fileName())
|| !StringUtils.hasText(response.data().sha256())) {
throw badRequest("附件不存在、归属不匹配或文件元数据不完整");
}
return response.data();
} catch (FeignException exception) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "文件服务暂不可用");
}
}
private AggregateRow requireBookingPlan(long bookingId) {
AggregateRow row = repository.aggregateByBooking(bookingId);
if (row == null) throw notFound("货柜或关联装柜计划不存在");
return row;
}
private static void requireCompletedPlan(AggregateRow plan) {
if (!"COMPLETED".equals(plan.getPlanStatus())) throw conflict("仅完成装柜清单可办理报关");
}
private ContainerDocumentEntity requireDocumentEntity(long bookingId, String documentType) {
ContainerDocumentEntity value = repository.document(bookingId, documentType);
if (value == null) throw conflict("请先上传资料");
return value;
}
private BusinessDocument requireDocument(long bookingId, String documentType) {
return document(requireDocumentEntity(bookingId, documentType));
}
private static BusinessDocument document(ContainerDocumentEntity value) {
return value == null ? null : new BusinessDocument(value.getId(), value.getDocumentType(), value.getFileId(),
value.getFileName(), value.getFileHash(), value.getUploadedBy(), value.getUploadedByName(),
value.getCreatedAt(), value.getCompletedBy(), value.getCompletedByName(), value.getCompletedAt(),
value.getVersion());
}
private static Map<String, Object> documentSnapshot(ContainerDocumentEntity value) {
return value == null ? Map.of() : values("id", value.getId(), "type", value.getDocumentType(),
"fileId", value.getFileId(), "fileName", value.getFileName(), "fileHash", value.getFileHash(),
"completedAt", value.getCompletedAt() == null ? null : value.getCompletedAt().toString(),
"version", value.getVersion());
}
@Transactional
@BusinessAudit(event = "PRELOAD_TRANSPORT_UPDATE", resourceType = "LOADING_PLAN",
resourceId = "#planId", description = "修改预装集装箱号与航次")