修复海外报价合作仓候选缓存刷新
This commit is contained in:
+6
-3
@@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -54,9 +55,11 @@ public class PlatformOverseasQuoteController {
|
||||
@GetMapping("/warehouse-options")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.QUOTE_LIST + "')")
|
||||
@Operation(summary = "List cached active overseas partner warehouse options",
|
||||
description = "Required authority: platform:overseas-master-data:quote:list; the browser never calls CN internal APIs")
|
||||
public ApiResponse<List<OverseasQuoteResponses.PartnerWarehouseOption>> warehouseOptions() {
|
||||
return ApiResponse.success(service.warehouseOptions());
|
||||
description = "Required authority: platform:overseas-master-data:quote:list; the browser never calls CN internal APIs; "
|
||||
+ "refresh=true bypasses the positive cache after CN partner master data changes")
|
||||
public ApiResponse<List<OverseasQuoteResponses.PartnerWarehouseOption>> warehouseOptions(
|
||||
@RequestParam(defaultValue = "false") boolean refresh) {
|
||||
return ApiResponse.success(service.warehouseOptions(refresh));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
|
||||
+2
-2
@@ -73,8 +73,8 @@ public class OverseasQuoteService {
|
||||
quote.getCreatedBy(), quote.getCreatedAt());
|
||||
}
|
||||
|
||||
public List<OverseasQuoteResponses.PartnerWarehouseOption> warehouseOptions() {
|
||||
return warehouses.options();
|
||||
public List<OverseasQuoteResponses.PartnerWarehouseOption> warehouseOptions(boolean refresh) {
|
||||
return refresh ? warehouses.refresh() : warehouses.options();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
||||
+27
-5
@@ -6,6 +6,7 @@ import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.Clock;
|
||||
@@ -23,26 +24,39 @@ public class PartnerWarehouseDirectory {
|
||||
private volatile Snapshot snapshot;
|
||||
|
||||
public List<OverseasQuoteResponses.PartnerWarehouseOption> options() {
|
||||
return options(false);
|
||||
}
|
||||
|
||||
/** Bypasses a still-valid positive snapshot after CN partner master data changes. */
|
||||
public List<OverseasQuoteResponses.PartnerWarehouseOption> refresh() {
|
||||
return options(true);
|
||||
}
|
||||
|
||||
private List<OverseasQuoteResponses.PartnerWarehouseOption> options(boolean forceRefresh) {
|
||||
Snapshot current = snapshot;
|
||||
Instant now = clock.instant();
|
||||
if (current != null && now.isBefore(current.expiresAt())) return current.options();
|
||||
if (!forceRefresh && current != null && now.isBefore(current.expiresAt())) return current.options();
|
||||
synchronized (this) {
|
||||
current = snapshot;
|
||||
now = clock.instant();
|
||||
if (current != null && now.isBefore(current.expiresAt())) return current.options();
|
||||
if (!forceRefresh && current != null && now.isBefore(current.expiresAt())) return current.options();
|
||||
var response = client.activePartners(internalToken);
|
||||
if (response == null || !response.successful() || response.data() == null) {
|
||||
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,
|
||||
"国内海外合作仓候选暂不可用");
|
||||
}
|
||||
List<OverseasQuoteResponses.PartnerWarehouseOption> options = response.data().stream()
|
||||
.filter(value -> value != null && value.id() != null)
|
||||
.filter(this::valid)
|
||||
.map(value -> new OverseasQuoteResponses.PartnerWarehouseOption(value.id().toString(),
|
||||
value.warehouseCode(), value.warehouseName(), value.countryCode()))
|
||||
value.warehouseCode().trim(), value.warehouseName().trim(),
|
||||
value.countryCode().trim().toUpperCase(java.util.Locale.ROOT)))
|
||||
.toList();
|
||||
// An empty directory can be a short-lived cross-region visibility gap after a partner
|
||||
// warehouse is created. Do not preserve that absence for the normal 30-minute TTL.
|
||||
if (options.isEmpty()) return List.of();
|
||||
if (options.isEmpty()) {
|
||||
snapshot = null;
|
||||
return List.of();
|
||||
}
|
||||
snapshot = new Snapshot(options, now.plus(ttl));
|
||||
return snapshot.options();
|
||||
}
|
||||
@@ -54,6 +68,14 @@ public class PartnerWarehouseDirectory {
|
||||
"所选海外合作仓不存在或已停用"));
|
||||
}
|
||||
|
||||
private boolean valid(DomesticPartnerWarehouseClient.Option value) {
|
||||
return value != null && value.id() != null
|
||||
&& StringUtils.hasText(value.warehouseCode())
|
||||
&& StringUtils.hasText(value.warehouseName())
|
||||
&& StringUtils.hasText(value.countryCode())
|
||||
&& value.countryCode().trim().matches("(?i)[A-Z]{2}");
|
||||
}
|
||||
|
||||
private record Snapshot(List<OverseasQuoteResponses.PartnerWarehouseOption> options, Instant expiresAt) {
|
||||
private Snapshot { options = List.copyOf(options); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user