1
This commit is contained in:
+2
-2
@@ -71,7 +71,7 @@ public class MerchantFirstLegOrderController {
|
||||
@RequestHeader("Idempotency-Key") String idempotencyKey,
|
||||
@Valid @RequestBody OrderRequests.SaveRequest request) {
|
||||
return ApiResponse.success(service.createDraft(
|
||||
OrderScopeGuard.merchant(user), user.userId(), request, idempotencyKey));
|
||||
OrderScopeGuard.merchant(user), user.userId(), user.username(), request, idempotencyKey));
|
||||
}
|
||||
|
||||
@BusinessAudit(event = "ORDER_BATCH_CREATE", resourceType = "FIRST_LEG_ORDER", description = "Batch create drafts / 批量创建草稿")
|
||||
@@ -84,7 +84,7 @@ public class MerchantFirstLegOrderController {
|
||||
@RequestHeader("Idempotency-Key") String idempotencyKey,
|
||||
@RequestBody List<MerchantBatchDraftDtos.Item> items) {
|
||||
return ApiResponse.success(batchDraftService.create(
|
||||
OrderScopeGuard.merchant(user), user.userId(), idempotencyKey, items));
|
||||
OrderScopeGuard.merchant(user), user.userId(), user.username(), idempotencyKey, items));
|
||||
}
|
||||
|
||||
@BusinessAudit(event = "ORDER_UPDATE", resourceType = "FIRST_LEG_ORDER", resourceId = "#id", description = "Update draft / 修改草稿")
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class PlatformFirstLegOrderController {
|
||||
@RequestHeader("Idempotency-Key") String idempotencyKey,
|
||||
@Valid @RequestBody OrderRequests.SaveRequest request) {
|
||||
long scopedTenant = OrderScopeGuard.platform(user, tenantId);
|
||||
return ApiResponse.success(service.createDraft(scopedTenant, user.userId(), request, idempotencyKey));
|
||||
return ApiResponse.success(service.createDraft(scopedTenant, user.userId(), user.username(), request, idempotencyKey));
|
||||
}
|
||||
|
||||
@BusinessAudit(event = "ORDER_UPDATE", resourceType = "FIRST_LEG_ORDER", resourceId = "#id", description = "平台编辑订单资料")
|
||||
|
||||
@@ -16,6 +16,7 @@ public record MerchantInboundOrderRow(
|
||||
String inboundCode,
|
||||
String channelName,
|
||||
String destinationType,
|
||||
String pickupMethod,
|
||||
String customerName,
|
||||
String receivingCompanyName,
|
||||
String specificationModel,
|
||||
|
||||
@@ -20,6 +20,12 @@ public class MerchantBatchDraftItemService {
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public OrderEntity create(long tenantId, long userId, OrderRequests.SaveRequest draft, String idempotencyKey) {
|
||||
return create(tenantId, userId, null, draft, idempotencyKey);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public OrderEntity create(long tenantId, long userId, String username,
|
||||
OrderRequests.SaveRequest draft, String idempotencyKey) {
|
||||
var violations = validator.validate(draft);
|
||||
if (!violations.isEmpty()) {
|
||||
String message = violations.stream()
|
||||
@@ -29,7 +35,7 @@ public class MerchantBatchDraftItemService {
|
||||
.orElse("草稿参数不合法");
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, message);
|
||||
}
|
||||
Long id = orders.createDraft(tenantId, userId, draft, idempotencyKey);
|
||||
Long id = orders.createDraft(tenantId, userId, username, draft, idempotencyKey);
|
||||
return repository.findOwned(tenantId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ public class MerchantBatchDraftService {
|
||||
|
||||
public MerchantBatchDraftDtos.Response create(long tenantId, long userId, String batchKey,
|
||||
List<MerchantBatchDraftDtos.Item> items) {
|
||||
return create(tenantId, userId, null, batchKey, items);
|
||||
}
|
||||
|
||||
public MerchantBatchDraftDtos.Response create(long tenantId, long userId, String username,
|
||||
String batchKey, List<MerchantBatchDraftDtos.Item> items) {
|
||||
if (!StringUtils.hasText(batchKey)) throw new BizException("必须提供 Idempotency-Key");
|
||||
if (batchKey.trim().length() > 128) throw new BizException("Idempotency-Key 长度不能超过128");
|
||||
if (items == null || items.isEmpty()) throw new BizException("批量草稿不能为空");
|
||||
@@ -44,7 +49,7 @@ public class MerchantBatchDraftService {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
OrderEntity order = itemService.create(tenantId, userId, item.draft(), derivedKey(batchKey, rowId));
|
||||
OrderEntity order = itemService.create(tenantId, userId, username, item.draft(), derivedKey(batchKey, rowId));
|
||||
results.add(MerchantBatchDraftDtos.Result.success(index, rowId, order.getId(), order.getOrderNo(), order.getInboundCode()));
|
||||
} catch (Exception ex) {
|
||||
String code = ex instanceof BizException be ? be.getResultCode().code() : "S0500";
|
||||
|
||||
@@ -128,7 +128,7 @@ public class MerchantInboundOrderListService {
|
||||
inbound == null ? null : inbound.inboundId(),
|
||||
inbound == null || inbound.inboundCode() == null || inbound.inboundCode().isBlank()
|
||||
? order.getInboundCode() : inbound.inboundCode(), order.getChannelName(),
|
||||
order.getDestinationType(), order.getCustomerName(),
|
||||
order.getDestinationType(), order.getPickupMethod(), order.getCustomerName(),
|
||||
consignee == null ? null : consignee.getCompanyName(), joined(cargoes, OrderCargoEntity::getModel),
|
||||
order.getStatus(), totalQuantity, grossWeight, netWeight, volume,
|
||||
commonUnitPrice(cargoes), declaredAmount, joined(cargoes, OrderCargoEntity::getCurrency),
|
||||
|
||||
@@ -59,17 +59,25 @@ public class OrderApplicationService {
|
||||
/** 创建草稿时使用数据库唯一键保证请求幂等。 */
|
||||
@Transactional
|
||||
public Long createDraft(long tenantId, long userId, OrderRequests.SaveRequest request, String idempotencyKey) {
|
||||
return createDraft(tenantId, userId, null, request, idempotencyKey);
|
||||
}
|
||||
|
||||
/** 创建草稿时同时保存创建操作人的名称快照,供平台订单详情展示。 */
|
||||
@Transactional
|
||||
public Long createDraft(long tenantId, long userId, String username,
|
||||
OrderRequests.SaveRequest request, String idempotencyKey) {
|
||||
IdempotencyContext idempotency = beginIdempotency(tenantId, userId, "CREATE_DRAFT", 0L, idempotencyKey,
|
||||
requestHash("CREATE_DRAFT", request));
|
||||
if (idempotency.replayed()) {
|
||||
return idempotency.record().getResultOrderId();
|
||||
}
|
||||
Long orderId = createDraftInternal(tenantId, userId, request);
|
||||
Long orderId = createDraftInternal(tenantId, userId, username, request);
|
||||
completeIdempotency(idempotency, orderId);
|
||||
return orderId;
|
||||
}
|
||||
|
||||
private Long createDraftInternal(long tenantId, long userId, OrderRequests.SaveRequest request) {
|
||||
private Long createDraftInternal(long tenantId, long userId, String username,
|
||||
OrderRequests.SaveRequest request) {
|
||||
productImageGuard.requireOwned(tenantId, request.cargoes());
|
||||
var snapshots = masterDataGuard.validate(tenantId, request);
|
||||
requirePricingChannel(tenantId, request.receivingWarehouseId(), request.channelCode(),
|
||||
@@ -86,7 +94,7 @@ public class OrderApplicationService {
|
||||
throw duplicateInboundCode(order, duplicate);
|
||||
}
|
||||
saveChildren(tenantId, userId, order.getId(), request);
|
||||
record(tenantId, order.getId(), null, OrderStatus.DRAFT, "CREATE", null, userId, null);
|
||||
record(tenantId, order.getId(), null, OrderStatus.DRAFT, "CREATE", null, userId, username);
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,18 @@ public class UnifiedOrderQueryService {
|
||||
FirstlegUnifiedOrderClient.Detail inbound = order.getStatus() == OrderStatus.SUBMITTED
|
||||
? null : firstlegDetail(tenantId, orderId);
|
||||
String latestNote = orderDetail.internalNotes().isEmpty() ? null : orderDetail.internalNotes().get(0).getContent();
|
||||
String creatorName = orderDetail.statusHistory().stream()
|
||||
.filter(item -> "CREATE".equals(item.getAction()))
|
||||
.map(item -> item.getOperatorName())
|
||||
.filter(StringUtils::hasText)
|
||||
.findFirst()
|
||||
.orElseGet(() -> orderDetail.statusHistory().stream()
|
||||
.map(item -> item.getOperatorName())
|
||||
.filter(StringUtils::hasText)
|
||||
.findFirst()
|
||||
.orElse(null));
|
||||
UnifiedOrderResponse.Row summary = row(order, inbound == null ? null : inbound.summary(), latestNote,
|
||||
childCounts(List.of(order)), CargoSummary.from(orderDetail.cargoes()));
|
||||
childCounts(List.of(order)), CargoSummary.from(orderDetail.cargoes()), creatorName);
|
||||
List<UnifiedOrderResponse.TimelineEvent> timeline = new ArrayList<>();
|
||||
orderDetail.statusHistory().forEach(item -> timeline.add(new UnifiedOrderResponse.TimelineEvent(
|
||||
"ORDER", "STATUS", orderMainStatus(item.getToStatus()), item.getAction(), item.getReason(),
|
||||
@@ -174,6 +184,12 @@ public class UnifiedOrderQueryService {
|
||||
private UnifiedOrderResponse.Row row(OrderEntity order, FirstlegUnifiedOrderClient.Summary summary,
|
||||
String latestInternalNote, Map<Long, Integer> childCounts,
|
||||
CargoSummary cargo) {
|
||||
return row(order, summary, latestInternalNote, childCounts, cargo, null);
|
||||
}
|
||||
|
||||
private UnifiedOrderResponse.Row row(OrderEntity order, FirstlegUnifiedOrderClient.Summary summary,
|
||||
String latestInternalNote, Map<Long, Integer> childCounts,
|
||||
CargoSummary cargo, String creatorName) {
|
||||
if (summary == null) {
|
||||
String mainStatus = orderMainStatus(order.getStatus());
|
||||
return new UnifiedOrderResponse.Row(order.getId(), order.getTenantId(), order.getOrderNo(), mainStatus,
|
||||
@@ -187,7 +203,7 @@ public class UnifiedOrderQueryService {
|
||||
null, null, null, null, order.getPickupMethod(), order.getTaxMode(), order.getContainerMode(),
|
||||
order.getCustomsInspection(), order.getDestinationType(), order.getAmazonWarehouseCode(),
|
||||
null, null, null, null, netWeightKg(cargo), null, null, order.getExpressCompany(), order.getExpressTrackingNo(),
|
||||
string(order.getCreatedBy()), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo),
|
||||
StringUtils.hasText(creatorName) ? creatorName : string(order.getCreatedBy()), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo),
|
||||
null, null, null, null, null,
|
||||
order.getCustomerRemark(), latestInternalNote,
|
||||
order.getCreatedAt(), order.getUpdatedAt(), order.getRequestedPickupAt());
|
||||
@@ -206,7 +222,7 @@ public class UnifiedOrderQueryService {
|
||||
inbound.chargeableWeightKg(), inbound.chargeableVolumeCbm(), inbound.volumeWeightKg(),
|
||||
inbound.actualNetWeightKg() != null ? inbound.actualNetWeightKg() : netWeightKg(cargo),
|
||||
inbound.measureCompletedAt(), inbound.inboundedAt(), inbound.expressCompanyName(), inbound.expressTrackingNo(),
|
||||
inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), measurement(inbound),
|
||||
StringUtils.hasText(creatorName) ? creatorName : inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), measurement(inbound),
|
||||
preload(summary.preload()), loading(summary.loading()),
|
||||
delivery(summary.delivery()), transport(inbound), order.getCustomerRemark(), latestInternalNote,
|
||||
order.getCreatedAt(), order.getUpdatedAt(), inbound.requestedPickupAt());
|
||||
|
||||
@@ -748,6 +748,7 @@ components:
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
inboundCode: {type: string}
|
||||
channelName: {type: string}
|
||||
pickupMethod: {type: string, enum: [SELF_EXPRESS, PICKUP, SELF_TRUCK], nullable: true}
|
||||
destinationWarehouseName: {type: string}
|
||||
status: {type: string}
|
||||
packageCount: {type: integer}
|
||||
|
||||
@@ -41,6 +41,7 @@ class MerchantInboundOrderListServiceTest {
|
||||
order.setStatus(OrderStatus.APPROVED);
|
||||
order.setChannelName("美森");
|
||||
order.setDestinationType("FBA");
|
||||
order.setPickupMethod("SELF_TRUCK");
|
||||
order.setDestinationWarehouseName("ONT8");
|
||||
order.setCustomerName("测试客户");
|
||||
order.setShipmentId("FBA-SHIPMENT-77");
|
||||
@@ -82,6 +83,7 @@ class MerchantInboundOrderListServiceTest {
|
||||
assertThat(row.receivingCompanyName()).isEqualTo("收货公司");
|
||||
assertThat(row.operatorName()).isEqualTo("操作员");
|
||||
assertThat(row.destinationWarehouseName()).isEqualTo("ONT8");
|
||||
assertThat(row.pickupMethod()).isEqualTo("SELF_TRUCK");
|
||||
assertThat(row.expressCompany()).isEqualTo("UPS");
|
||||
assertThat(row.skuNos()).isEqualTo("SKU-A、SKU-B");
|
||||
assertThat(row.shipmentId()).isEqualTo("FBA-SHIPMENT-77");
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.baiyue.order.dto.UnifiedOrderResponse;
|
||||
import com.baiyue.order.entity.OrderCargoEntity;
|
||||
import com.baiyue.order.entity.OrderEntity;
|
||||
import com.baiyue.order.entity.OrderInternalNoteEntity;
|
||||
import com.baiyue.order.entity.OrderStatusHistoryEntity;
|
||||
import com.baiyue.order.entity.OrderStatus;
|
||||
import com.baiyue.order.repository.OrderRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -247,6 +248,24 @@ class UnifiedOrderQueryServiceTest {
|
||||
verify(firstleg, never()).detail(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailUsesCreateHistoryOperatorNameInsteadOfNumericCreatorId() {
|
||||
OrderEntity order = order(OrderStatus.SUBMITTED);
|
||||
OrderStatusHistoryEntity created = new OrderStatusHistoryEntity();
|
||||
created.setAction("CREATE");
|
||||
created.setOperatorId(2L);
|
||||
created.setOperatorName("platform_admin");
|
||||
when(orders.findOwned(10001L, 77L)).thenReturn(order);
|
||||
when(orders.findCargo(10001L, 77L)).thenReturn(List.of());
|
||||
when(orders.findParties(10001L, 77L)).thenReturn(List.of());
|
||||
when(orders.findNotes(10001L, 77L)).thenReturn(List.of());
|
||||
when(orders.findHistory(10001L, 77L)).thenReturn(List.of(created));
|
||||
|
||||
var result = service.detail(10001L, 77L, true);
|
||||
|
||||
assertThat(result.summary().createdBy()).isEqualTo("platform_admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fulfillmentStatusFilterUsesFirstlegPagingWithoutPostPageFiltering() {
|
||||
UnifiedOrderQuery query = query("INBOUNDED");
|
||||
|
||||
Reference in New Issue
Block a user