优化
This commit is contained in:
+37
@@ -0,0 +1,37 @@
|
||||
package com.baiyue.warehouse.controller;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.warehouse.dto.OverseasStorageFeeDtos;
|
||||
import com.baiyue.warehouse.service.OverseasStorageFeeService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/internal/warehouse/v1/customer-overseas-storage-fees")
|
||||
public class InternalOverseasStorageFeeController {
|
||||
@Resource private OverseasStorageFeeService service;
|
||||
@Value("${baiyue.security.internal-service-token}") private String internalToken;
|
||||
private byte[] expected;
|
||||
@PostConstruct void init() { expected = internalToken.getBytes(StandardCharsets.UTF_8); }
|
||||
@PostMapping("/defaults")
|
||||
public ApiResponse<OverseasStorageFeeDtos.DefaultInitializationResponse> defaults(
|
||||
@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||
@RequestBody OverseasStorageFeeDtos.DefaultInitializationRequest request) {
|
||||
verify(token); return ApiResponse.success(service.initializeDefaults(request));
|
||||
}
|
||||
@DeleteMapping("/defaults")
|
||||
public ApiResponse<Void> compensate(@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||
@RequestBody OverseasStorageFeeDtos.CompensationRequest request) {
|
||||
verify(token); service.compensateDefaults(request); return ApiResponse.success(null);
|
||||
}
|
||||
private void verify(String token) {
|
||||
if (!MessageDigest.isEqual(expected, token == null ? new byte[0] : token.getBytes(StandardCharsets.UTF_8)))
|
||||
throw new BizException(ResultCode.FORBIDDEN, "无效的内部服务凭据");
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.baiyue.warehouse.controller;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.warehouse.dto.OverseasStorageFeeDtos;
|
||||
import com.baiyue.warehouse.service.OverseasStorageFeeService;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/iam/tenants/{tenantId}/overseas-storage-fees")
|
||||
public class PlatformCustomerOverseasStorageFeeController {
|
||||
@Resource private OverseasStorageFeeService service;
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('platform:customer:view')")
|
||||
public ApiResponse<OverseasStorageFeeDtos.View> get(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable long tenantId) { return ApiResponse.success(service.detailByTenant(tenantId)); }
|
||||
@PutMapping
|
||||
@PreAuthorize("hasAuthority('platform:customer:edit')")
|
||||
public ApiResponse<Void> put(@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long tenantId,
|
||||
@Valid @RequestBody OverseasStorageFeeDtos.UpdateRequest request) {
|
||||
if (request.customerId() != tenantId) {
|
||||
throw new com.baiyue.common.core.exception.BizException(
|
||||
com.baiyue.common.core.api.ResultCode.VALIDATION_FAILED, "客户ID与路径不一致");
|
||||
}
|
||||
if (request.tiers() == null || request.tiers().size() != 9
|
||||
|| request.tiers().stream().filter(t -> "STORAGE".equals(t.feeType())).count() != 5
|
||||
|| request.tiers().stream().filter(t -> "RETURN_STORAGE".equals(t.feeType())).count() != 4) {
|
||||
throw new com.baiyue.common.core.exception.BizException(
|
||||
com.baiyue.common.core.api.ResultCode.VALIDATION_FAILED, "客户费用必须包含仓租5档和退货仓储费4档");
|
||||
}
|
||||
OverseasStorageFeeDtos.View view = service.detailByTenant(tenantId);
|
||||
service.update(view.id(), request); return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,16 @@ public final class OverseasStorageFeeDtos {
|
||||
|
||||
@Schema(name = "OverseasStorageFeeTierRequest", description = "仓储费阶梯")
|
||||
public record TierRequest(
|
||||
@NotBlank @Pattern(regexp = "^[A-Z_]+$") String feeType,
|
||||
@NotBlank @Pattern(regexp = "^[A-Z]+$") String billingUnit,
|
||||
@NotNull @Min(1) @Max(36500) Integer startDay,
|
||||
@NotNull @Min(1) @Max(36500) Integer endDay,
|
||||
@NotNull @DecimalMin(value = "0", inclusive = false) @Digits(integer = 12, fraction = 6)
|
||||
@Min(1) @Max(36500) Integer endDay,
|
||||
@NotNull @DecimalMin(value = "0", inclusive = true) @Digits(integer = 12, fraction = 6)
|
||||
BigDecimal unitPrice
|
||||
) {
|
||||
public TierRequest(Integer startDay, Integer endDay, BigDecimal unitPrice) {
|
||||
this("STORAGE", "CBM", startDay, endDay, unitPrice);
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(name = "OverseasStorageFeeCreateRequest", description = "新增海外仓阶梯费用")
|
||||
@@ -68,7 +73,11 @@ public final class OverseasStorageFeeDtos {
|
||||
}
|
||||
|
||||
@Schema(name = "OverseasStorageFeeTierView", description = "仓储费阶梯明细")
|
||||
public record TierView(Long id, Integer startDay, Integer endDay, BigDecimal unitPrice, Integer sortNo) {
|
||||
public record TierView(Long id, Integer startDay, Integer endDay, BigDecimal unitPrice, Integer sortNo,
|
||||
String feeType, String billingUnit) {
|
||||
public TierView(Long id, Integer startDay, Integer endDay, BigDecimal unitPrice, Integer sortNo) {
|
||||
this(id, startDay, endDay, unitPrice, sortNo, "STORAGE", "CBM");
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(name = "OverseasStorageFeeView", description = "海外仓阶梯费用详情")
|
||||
@@ -83,4 +92,9 @@ public final class OverseasStorageFeeDtos {
|
||||
LocalDateTime updatedAt
|
||||
) {
|
||||
}
|
||||
|
||||
public record DefaultInitializationRequest(@NotNull @Positive Long tenantId, @NotBlank String initializationKey,
|
||||
Long operatorId, String customerName) {}
|
||||
public record DefaultInitializationResponse(String configId, boolean createdByThisInitialization) {}
|
||||
public record CompensationRequest(@NotNull @Positive Long tenantId, @NotBlank String initializationKey) {}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ public class OverseasStorageFeeRuleEntity extends BaseEntity {
|
||||
private Long tenantId;
|
||||
private String customerName;
|
||||
private String currency;
|
||||
private String initializationKey;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ public class OverseasStorageFeeTierEntity extends BaseEntity {
|
||||
private Long ruleId;
|
||||
private Integer startDay;
|
||||
private Integer endDay;
|
||||
private String feeType;
|
||||
private String billingUnit;
|
||||
private BigDecimal unitPrice;
|
||||
private Integer sortNo;
|
||||
}
|
||||
|
||||
+5
@@ -38,6 +38,11 @@ public class OverseasStorageFeeRepository {
|
||||
public OverseasStorageFeeRuleEntity find(long id) {
|
||||
return ruleMapper.selectById(id);
|
||||
}
|
||||
public OverseasStorageFeeRuleEntity findByTenantAndKey(long tenantId, String key) {
|
||||
return ruleMapper.selectOne(new LambdaQueryWrapper<OverseasStorageFeeRuleEntity>()
|
||||
.eq(OverseasStorageFeeRuleEntity::getTenantId, tenantId)
|
||||
.eq(OverseasStorageFeeRuleEntity::getInitializationKey, key));
|
||||
}
|
||||
|
||||
public boolean existsCustomer(long customerId, Long excludedId) {
|
||||
return ruleMapper.selectCount(new LambdaQueryWrapper<OverseasStorageFeeRuleEntity>()
|
||||
|
||||
+83
-11
@@ -18,6 +18,7 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/** 海外仓阶梯费用管理服务。 */
|
||||
@Service
|
||||
@@ -40,6 +41,35 @@ public class OverseasStorageFeeService {
|
||||
return view(rule, repository.findTiers(List.of(id)));
|
||||
}
|
||||
|
||||
public OverseasStorageFeeDtos.View detailByTenant(long tenantId) {
|
||||
OverseasStorageFeeRuleEntity rule = repository.page(new OverseasStorageFeeDtos.PageQuery(1, 1, tenantId, null))
|
||||
.records().stream().findFirst().orElseThrow(() -> new BizException(ResultCode.NOT_FOUND, "海外仓费用不存在"));
|
||||
return detail(rule.getId());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OverseasStorageFeeDtos.DefaultInitializationResponse initializeDefaults(
|
||||
OverseasStorageFeeDtos.DefaultInitializationRequest request) {
|
||||
OverseasStorageFeeRuleEntity existing = repository.findByTenantAndKey(request.tenantId(), request.initializationKey());
|
||||
if (existing != null && !Boolean.TRUE.equals(existing.getDeleted())) {
|
||||
return new OverseasStorageFeeDtos.DefaultInitializationResponse(String.valueOf(existing.getId()), false);
|
||||
}
|
||||
OverseasStorageFeeRuleEntity rule = new OverseasStorageFeeRuleEntity();
|
||||
rule.setInitializationKey(request.initializationKey());
|
||||
copy(rule, request.tenantId(), request.customerName() == null ? "" : request.customerName(), "USD");
|
||||
repository.insertRule(rule);
|
||||
repository.insertTiers(tierEntities(rule.getId(), request.tenantId(), defaultTiers()));
|
||||
return new OverseasStorageFeeDtos.DefaultInitializationResponse(String.valueOf(rule.getId()), true);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void compensateDefaults(OverseasStorageFeeDtos.CompensationRequest request) {
|
||||
OverseasStorageFeeRuleEntity rule = repository.findByTenantAndKey(request.tenantId(), request.initializationKey());
|
||||
if (rule == null || rule.getVersion() != 0 || !isDefault(rule)) return;
|
||||
repository.deleteTiers(rule.getId());
|
||||
repository.deleteRule(rule.getId(), 0);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@BusinessAudit(event = "OVERSEAS_STORAGE_FEE_CREATE", resourceType = "OVERSEAS_STORAGE_FEE",
|
||||
description = "新增海外仓阶梯费用", captureBefore = false)
|
||||
@@ -110,19 +140,31 @@ public class OverseasStorageFeeService {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "阶梯费用明细必须为1至100组");
|
||||
}
|
||||
if (source.stream().anyMatch(tier -> tier == null || tier.startDay() == null
|
||||
|| tier.endDay() == null || tier.unitPrice() == null
|
||||
|| tier.unitPrice().signum() <= 0)) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "阶梯天数和仓储费标准必须为有效正数");
|
||||
|| tier.unitPrice() == null || tier.unitPrice().signum() < 0)) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "阶梯天数和仓储费标准必须有效");
|
||||
}
|
||||
if (source.stream().anyMatch(t -> !("STORAGE".equals(t.feeType()) && "CBM".equals(t.billingUnit()))
|
||||
&& !("RETURN_STORAGE".equals(t.feeType()) && "PALLET".equals(t.billingUnit())))) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "费用类型和计费单位不匹配");
|
||||
}
|
||||
List<OverseasStorageFeeDtos.TierRequest> tiers = new ArrayList<>(source);
|
||||
tiers.sort(Comparator.comparingInt(OverseasStorageFeeDtos.TierRequest::startDay));
|
||||
int expectedStart = 1;
|
||||
for (OverseasStorageFeeDtos.TierRequest tier : tiers) {
|
||||
if (tier.startDay() != expectedStart || tier.endDay() < tier.startDay()) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED,
|
||||
"阶梯必须从第1天开始,且区间连续、不能重叠或倒置");
|
||||
tiers.sort(Comparator.comparing(OverseasStorageFeeDtos.TierRequest::feeType)
|
||||
.thenComparingInt(OverseasStorageFeeDtos.TierRequest::startDay));
|
||||
for (String feeType : List.of("STORAGE", "RETURN_STORAGE")) {
|
||||
List<OverseasStorageFeeDtos.TierRequest> group = tiers.stream().filter(t -> feeType.equals(t.feeType())).toList();
|
||||
if (source.size() == 9 && (("STORAGE".equals(feeType) && group.size() != 5)
|
||||
|| ("RETURN_STORAGE".equals(feeType) && group.size() != 4)))
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "仓租必须5档,退货仓储费必须4档");
|
||||
int expectedStart = 1;
|
||||
for (int i = 0; i < group.size(); i++) {
|
||||
OverseasStorageFeeDtos.TierRequest tier = group.get(i);
|
||||
if (tier.startDay() != expectedStart || (tier.endDay() != null && tier.endDay() < tier.startDay()))
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "阶梯必须从第1天开始且连续");
|
||||
if (tier.endDay() == null && i != group.size() - 1)
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "无上限阶梯必须为最后一档");
|
||||
if (tier.endDay() == null) break;
|
||||
expectedStart = tier.endDay() + 1;
|
||||
}
|
||||
expectedStart = tier.endDay() + 1;
|
||||
}
|
||||
return List.copyOf(tiers);
|
||||
}
|
||||
@@ -144,6 +186,8 @@ public class OverseasStorageFeeService {
|
||||
tier.setRuleId(ruleId);
|
||||
tier.setStartDay(request.startDay());
|
||||
tier.setEndDay(request.endDay());
|
||||
tier.setFeeType(request.feeType());
|
||||
tier.setBillingUnit(request.billingUnit());
|
||||
tier.setUnitPrice(request.unitPrice());
|
||||
tier.setSortNo(index + 1);
|
||||
result.add(tier);
|
||||
@@ -155,7 +199,35 @@ public class OverseasStorageFeeService {
|
||||
List<OverseasStorageFeeTierEntity> tiers) {
|
||||
return new OverseasStorageFeeDtos.View(rule.getId(), rule.getTenantId(), rule.getCustomerName(),
|
||||
rule.getCurrency(), tiers.stream().map(tier -> new OverseasStorageFeeDtos.TierView(
|
||||
tier.getId(), tier.getStartDay(), tier.getEndDay(), tier.getUnitPrice(), tier.getSortNo()))
|
||||
tier.getId(), tier.getStartDay(), tier.getEndDay(), tier.getUnitPrice(), tier.getSortNo(),
|
||||
tier.getFeeType(), tier.getBillingUnit()))
|
||||
.toList(), rule.getVersion(), rule.getCreatedAt(), rule.getUpdatedAt());
|
||||
}
|
||||
|
||||
private static List<OverseasStorageFeeDtos.TierRequest> defaultTiers() {
|
||||
return List.of(
|
||||
new OverseasStorageFeeDtos.TierRequest("STORAGE", "CBM", 1, 7, BigDecimal.ZERO),
|
||||
new OverseasStorageFeeDtos.TierRequest("STORAGE", "CBM", 8, 30, new BigDecimal("0.8")),
|
||||
new OverseasStorageFeeDtos.TierRequest("STORAGE", "CBM", 31, 60, BigDecimal.ONE),
|
||||
new OverseasStorageFeeDtos.TierRequest("STORAGE", "CBM", 61, 90, new BigDecimal("1.5")),
|
||||
new OverseasStorageFeeDtos.TierRequest("STORAGE", "CBM", 91, null, new BigDecimal("2")),
|
||||
new OverseasStorageFeeDtos.TierRequest("RETURN_STORAGE", "PALLET", 1, 30, new BigDecimal("1.5")),
|
||||
new OverseasStorageFeeDtos.TierRequest("RETURN_STORAGE", "PALLET", 31, 60, new BigDecimal("2")),
|
||||
new OverseasStorageFeeDtos.TierRequest("RETURN_STORAGE", "PALLET", 61, 90, new BigDecimal("3")),
|
||||
new OverseasStorageFeeDtos.TierRequest("RETURN_STORAGE", "PALLET", 91, null, new BigDecimal("4")));
|
||||
}
|
||||
|
||||
private boolean isDefault(OverseasStorageFeeRuleEntity rule) {
|
||||
if (!"USD".equals(rule.getCurrency())) return false;
|
||||
List<OverseasStorageFeeTierEntity> actual = repository.findTiers(List.of(rule.getId()));
|
||||
List<OverseasStorageFeeDtos.TierRequest> expected = defaultTiers();
|
||||
if (actual.size() != expected.size()) return false;
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
var e = expected.get(i); var a = actual.get(i);
|
||||
if (!e.feeType().equals(a.getFeeType()) || !e.billingUnit().equals(a.getBillingUnit())
|
||||
|| !e.startDay().equals(a.getStartDay()) || !java.util.Objects.equals(e.endDay(), a.getEndDay())
|
||||
|| e.unitPrice().compareTo(a.getUnitPrice()) != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,22 @@ CALL add_column_if_missing('wh_warehouse', 'updated_by', 'BIGINT UNSIGNED NULL')
|
||||
CALL add_column_if_missing('wh_warehouse', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||||
CALL add_column_if_missing('wh_warehouse', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||||
CALL add_column_if_missing('wh_warehouse', 'version', 'INT NOT NULL DEFAULT 0');
|
||||
CALL add_column_if_missing('wh_overseas_storage_fee_rule', 'initialization_key', 'VARCHAR(191) NULL');
|
||||
CALL add_column_if_missing('wh_overseas_storage_fee_tier', 'fee_type', 'VARCHAR(32) NOT NULL DEFAULT ''STORAGE''');
|
||||
CALL add_column_if_missing('wh_overseas_storage_fee_tier', 'billing_unit', 'VARCHAR(16) NOT NULL DEFAULT ''CBM''');
|
||||
|
||||
-- 海外仓费用旧表兼容:允许免费档和“第 N 天以上”,并同步检查约束。
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
MODIFY COLUMN end_day SMALLINT UNSIGNED NULL COMMENT '结束天,含边界;NULL表示以上',
|
||||
MODIFY COLUMN unit_price DECIMAL(18,6) NOT NULL COMMENT '仓储费标准,可为0';
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
DROP CHECK chk_overseas_fee_tier_days,
|
||||
DROP CHECK chk_overseas_fee_tier_price,
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_days CHECK (start_day >= 1 AND (end_day IS NULL OR end_day >= start_day)),
|
||||
ADD CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price >= 0);
|
||||
ALTER TABLE wh_overseas_storage_fee_tier
|
||||
DROP INDEX uk_overseas_fee_tier_start,
|
||||
ADD UNIQUE KEY uk_overseas_fee_tier_start (rule_id, fee_type, active_start_day);
|
||||
|
||||
CALL add_column_if_missing('wh_warehouse_address', 'tenant_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id');
|
||||
CALL add_column_if_missing('wh_warehouse_address', 'warehouse_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER tenant_id');
|
||||
|
||||
@@ -75,3 +75,27 @@ CREATE TABLE IF NOT EXISTS wh_service_capability (
|
||||
) ENGINE=InnoDB COMMENT='仓库服务能力表:声明仓库可提供的收货、仓储、拣包、退货等能力及截单时间';
|
||||
|
||||
-- 不使用跨服务数据库外键;仓库聚合内的归属由服务代码通过 tenant_id 与 warehouse_id 共同校验。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_rule (
|
||||
id BIGINT UNSIGNED NOT NULL, tenant_id BIGINT UNSIGNED NOT NULL, customer_name VARCHAR(128) NOT NULL,
|
||||
currency CHAR(3) NOT NULL DEFAULT 'USD', initialization_key VARCHAR(191) NULL,
|
||||
created_by BIGINT UNSIGNED NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT UNSIGNED NULL, updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted TINYINT(1) NOT NULL DEFAULT 0, version INT NOT NULL DEFAULT 0,
|
||||
active_customer_id BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN deleted=0 THEN tenant_id ELSE NULL END) STORED,
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_overseas_fee_active_customer(active_customer_id), KEY idx_overseas_fee_page(deleted,updated_at,id)
|
||||
) ENGINE=InnoDB COMMENT='客户海外仓费用规则';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_tier (
|
||||
id BIGINT UNSIGNED NOT NULL, tenant_id BIGINT UNSIGNED NOT NULL, rule_id BIGINT UNSIGNED NOT NULL,
|
||||
start_day SMALLINT UNSIGNED NOT NULL, end_day SMALLINT UNSIGNED NULL,
|
||||
fee_type VARCHAR(32) NOT NULL DEFAULT 'STORAGE', billing_unit VARCHAR(16) NOT NULL DEFAULT 'CBM',
|
||||
unit_price DECIMAL(18,6) NOT NULL, sort_no SMALLINT UNSIGNED NOT NULL,
|
||||
created_by BIGINT UNSIGNED NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT UNSIGNED NULL, updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted TINYINT(1) NOT NULL DEFAULT 0, version INT NOT NULL DEFAULT 0,
|
||||
active_start_day SMALLINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN deleted=0 THEN start_day ELSE NULL END) STORED,
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_overseas_fee_tier_start(rule_id,fee_type,active_start_day), KEY idx_overseas_fee_tier_rule(tenant_id,rule_id,deleted,sort_no,id),
|
||||
CONSTRAINT chk_overseas_fee_tier_days CHECK (start_day>=1 AND (end_day IS NULL OR end_day>=start_day)),
|
||||
CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price>=0)
|
||||
) ENGINE=InnoDB COMMENT='客户海外仓费用阶梯';
|
||||
|
||||
+13
@@ -94,6 +94,19 @@ class OverseasStorageFeeServiceTest {
|
||||
verify(repository, never()).updateRule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializesNineDefaultTiersIdempotently() {
|
||||
doAnswer(invocation -> { OverseasStorageFeeRuleEntity rule = invocation.getArgument(0); rule.setId(77L); return null; })
|
||||
.when(repository).insertRule(any());
|
||||
var request = new OverseasStorageFeeDtos.DefaultInitializationRequest(10001L, "fee:10001", 1L, "客户");
|
||||
var first = service.initializeDefaults(request);
|
||||
assertThat(first.createdByThisInitialization()).isTrue();
|
||||
verify(repository).insertTiers(org.mockito.ArgumentMatchers.argThat(list -> list.size() == 9));
|
||||
when(repository.findByTenantAndKey(10001L, "fee:10001")).thenReturn(rule());
|
||||
var second = service.initializeDefaults(request);
|
||||
assertThat(second.createdByThisInitialization()).isFalse();
|
||||
}
|
||||
|
||||
private static OverseasStorageFeeDtos.TierRequest tier(int start, int end, String price) {
|
||||
return new OverseasStorageFeeDtos.TierRequest(start, end, new BigDecimal(price));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user