This commit is contained in:
2026-09-10 12:18:33 +08:00
parent 443b01c8a8
commit 98f61ba3ed
22 changed files with 371 additions and 56 deletions
@@ -41,7 +41,7 @@ public class InternalCustomerDimensionConfigController {
@Valid @RequestBody InitializeRequest request) {
requireToken(token);
return ApiResponse.success(service.initializeDefault(
request.tenantId(), request.operatorId(), request.initializationKey()));
request.tenantId(), request.operatorId(), request.initializationKey(), request.customerName()));
}
@DeleteMapping("/defaults")
@@ -60,8 +60,10 @@ public final class CustomerDimensionConfigDtos {
public record InitializeRequest(
@NotNull @Min(1) Long tenantId,
@NotNull @Min(0) Long operatorId,
@NotBlank @Size(max = 128) String initializationKey
@NotBlank @Size(max = 128) String initializationKey,
@NotBlank @Size(max = 128) String customerName
) { }
public record InitializeResponse(Long configId, boolean createdByThisInitialization) { }
public record InitializeResponse(Long configId, Long chargeRuleId,
boolean createdByThisInitialization) { }
}
@@ -38,4 +38,6 @@ public class CustomerChargeRuleEntity extends BaseEntity {
private LocalDateTime effectiveTo;
private String status;
private String remark;
/** 客户创建执行器的幂等键;人工创建的规则为空。 */
private String initializationKey;
}
@@ -1,7 +1,50 @@
package com.baiyue.firstleg.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
@Mapper
public interface CustomerChargeRuleMapper extends BaseMapper<CustomerChargeRuleEntity> {}
public interface CustomerChargeRuleMapper extends BaseMapper<CustomerChargeRuleEntity> {
@Select("""
SELECT id, tenant_id, rule_code, rule_version, customer_id, customer_name, business_type,
charge_mode, tax_mode, conversion_factor, length_factor, width_factor, height_factor,
default_rule, rounding_scale, rounding_mode, currency, effective_from, effective_to,
status, remark, initialization_key, created_by, created_at, updated_by, updated_at,
deleted, version
FROM fl_customer_charge_rule
WHERE tenant_id = #{tenantId} AND initialization_key = #{initializationKey} AND deleted = 0
LIMIT 1
""")
CustomerChargeRuleEntity selectByInitializationKey(
@Param("tenantId") long tenantId, @Param("initializationKey") String initializationKey);
@Select("""
SELECT id, tenant_id, rule_code, rule_version, customer_id, customer_name, business_type,
charge_mode, tax_mode, conversion_factor, length_factor, width_factor, height_factor,
default_rule, rounding_scale, rounding_mode, currency, effective_from, effective_to,
status, remark, initialization_key, created_by, created_at, updated_by, updated_at,
deleted, version
FROM fl_customer_charge_rule
WHERE tenant_id = #{tenantId} AND initialization_key = #{initializationKey} AND deleted = 0
LIMIT 1 FOR UPDATE
""")
CustomerChargeRuleEntity selectByInitializationKeyForUpdate(
@Param("tenantId") long tenantId, @Param("initializationKey") String initializationKey);
@Delete("""
DELETE FROM fl_customer_charge_rule
WHERE tenant_id = #{tenantId} AND initialization_key = #{initializationKey}
AND rule_code = 'DEFAULT-DIMENSION-WEIGHT' AND rule_version = 1
AND customer_id = #{tenantId} AND business_type = 'FBA' AND charge_mode = 'CBM'
AND tax_mode = 'TAX_INCLUDED' AND conversion_factor = 167.000000
AND length_factor = 1.100000 AND width_factor = 1.100000 AND height_factor = 1.100000
AND default_rule = 1 AND rounding_scale = 2 AND rounding_mode = 'HALF_UP'
AND currency = 'CNY' AND effective_to IS NULL AND status = 'ACTIVE'
AND deleted = 0 AND version = 0
""")
int deleteUnchangedInitializedRule(
@Param("tenantId") long tenantId, @Param("initializationKey") String initializationKey);
}
@@ -55,6 +55,19 @@ public class FbaPricingRepository {
public void insertRule(CustomerChargeRuleEntity rule) { rules.insert(rule); }
public CustomerChargeRuleEntity findRuleByInitializationKey(long tenantId, String initializationKey) {
return rules.selectByInitializationKey(tenantId, initializationKey);
}
public CustomerChargeRuleEntity findRuleByInitializationKeyForUpdate(
long tenantId, String initializationKey) {
return rules.selectByInitializationKeyForUpdate(tenantId, initializationKey);
}
public int deleteUnchangedInitializedRule(long tenantId, String initializationKey) {
return rules.deleteUnchangedInitializedRule(tenantId, initializationKey);
}
public int clearDefaultRule(long tenantId, long customerId, long operatorId) {
return rules.update(null, new LambdaUpdateWrapper<CustomerChargeRuleEntity>()
.eq(CustomerChargeRuleEntity::getTenantId, tenantId)
@@ -0,0 +1,95 @@
package com.baiyue.firstleg.service;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
import com.baiyue.firstleg.repository.FbaPricingRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/** 在客户创建执行器中幂等生成“箱规按权重系数计算”的默认规则。 */
@Service
public class CustomerChargeRuleInitializationService {
static final String DEFAULT_RULE_CODE = "DEFAULT-DIMENSION-WEIGHT";
static final BigDecimal DEFAULT_COEFFICIENT = new BigDecimal("1.100000");
static final BigDecimal DEFAULT_CONVERSION_FACTOR = new BigDecimal("167.000000");
static final String DEFAULT_REMARK = "SMALL:30,20,20;系数:1.1;重量:5;小箱:标准小箱;"
+ "MEDIUM:40,30,30;系数:1.1;重量:8;中箱:标准中箱;"
+ "LARGE:50,40,40;系数:1.1;重量:12;大箱:标准大箱;"
+ "EXTRA_LARGE:60,50,50;系数:1.1;重量:15;特大箱:特大箱";
@Resource private FbaPricingRepository repository;
public CustomerChargeRuleEntity initializeDefault(long tenantId, long operatorId,
String initializationKey, String customerName) {
CustomerChargeRuleEntity existing = repository.findRuleByInitializationKey(
tenantId, initializationKey);
if (existing != null) return existing;
CustomerChargeRuleEntity value = new CustomerChargeRuleEntity();
value.setId(IdWorker.getId());
value.setTenantId(tenantId);
value.setRuleCode(DEFAULT_RULE_CODE);
value.setRuleVersion(1);
value.setCustomerId(tenantId);
value.setCustomerName(requireCustomerName(customerName));
value.setBusinessType("FBA");
value.setChargeMode("CBM");
value.setTaxMode("TAX_INCLUDED");
value.setConversionFactor(DEFAULT_CONVERSION_FACTOR);
value.setLengthFactor(DEFAULT_COEFFICIENT);
value.setWidthFactor(DEFAULT_COEFFICIENT);
value.setHeightFactor(DEFAULT_COEFFICIENT);
value.setDefaultRule(true);
value.setRoundingScale(2);
value.setRoundingMode("HALF_UP");
value.setCurrency("CNY");
value.setEffectiveFrom(LocalDateTime.now());
value.setEffectiveTo(null);
value.setStatus("ACTIVE");
value.setRemark(DEFAULT_REMARK);
value.setInitializationKey(initializationKey);
value.setCreatedBy(operatorId);
value.setUpdatedBy(operatorId);
repository.insertRule(value);
return value;
}
CustomerChargeRuleEntity lockForCompensation(long tenantId, String initializationKey) {
return repository.findRuleByInitializationKeyForUpdate(tenantId, initializationKey);
}
boolean isUnchangedDefault(CustomerChargeRuleEntity value) {
return value != null
&& value.getVersion() != null && value.getVersion() == 0
&& DEFAULT_RULE_CODE.equals(value.getRuleCode()) && Integer.valueOf(1).equals(value.getRuleVersion())
&& value.getTenantId().equals(value.getCustomerId())
&& "FBA".equals(value.getBusinessType()) && "CBM".equals(value.getChargeMode())
&& "TAX_INCLUDED".equals(value.getTaxMode())
&& DEFAULT_CONVERSION_FACTOR.compareTo(value.getConversionFactor()) == 0
&& DEFAULT_COEFFICIENT.compareTo(value.getLengthFactor()) == 0
&& DEFAULT_COEFFICIENT.compareTo(value.getWidthFactor()) == 0
&& DEFAULT_COEFFICIENT.compareTo(value.getHeightFactor()) == 0
&& Boolean.TRUE.equals(value.getDefaultRule()) && Integer.valueOf(2).equals(value.getRoundingScale())
&& "HALF_UP".equals(value.getRoundingMode()) && "CNY".equals(value.getCurrency())
&& value.getEffectiveTo() == null && "ACTIVE".equals(value.getStatus())
&& DEFAULT_REMARK.equals(value.getRemark());
}
void deleteUnchangedDefault(long tenantId, String initializationKey) {
if (repository.deleteUnchangedInitializedRule(tenantId, initializationKey) != 1) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户权重系数默认规则补偿删除失败");
}
}
private static String requireCustomerName(String value) {
if (value == null || value.isBlank() || value.trim().length() > 128) {
throw new BizException(ResultCode.BAD_REQUEST, "客户名称无效");
}
return value.trim();
}
}
@@ -10,6 +10,7 @@ import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.RuleResponse;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.RuleUpdateRequest;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.UpdateRequest;
import com.baiyue.firstleg.entity.CustomerDimensionConfigEntity;
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
import com.baiyue.firstleg.repository.CustomerDimensionConfigRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -32,6 +33,8 @@ public class CustomerDimensionConfigService {
@Resource
private CustomerDimensionConfigRepository repository;
@Resource
private CustomerChargeRuleInitializationService chargeRuleInitialization;
public GroupResponse get(long tenantId) {
return response(tenantId, requireCompleteRules(repository.findAll(tenantId)));
@@ -78,18 +81,26 @@ public class CustomerDimensionConfigService {
}
@Transactional
public InitializeResponse initializeDefault(long tenantId, long operatorId, String initializationKey) {
public InitializeResponse initializeDefault(long tenantId, long operatorId, String initializationKey,
String customerName) {
requireTenant(tenantId);
String normalizedKey = requireInitializationKey(initializationKey);
List<CustomerDimensionConfigEntity> existing = repository.findAll(tenantId);
if (!existing.isEmpty()) {
List<CustomerDimensionConfigEntity> complete = requireCompleteRules(existing);
return initializationResponse(complete, normalizedKey);
boolean ownedByKey = ownedByInitialization(complete, normalizedKey);
CustomerChargeRuleEntity chargeRule = ownedByKey
? chargeRuleInitialization.initializeDefault(
tenantId, operatorId, normalizedKey, customerName)
: null;
return initializationResponse(complete, chargeRule, ownedByKey);
}
repository.insertDefaults(defaultRows(tenantId, normalizedKey), operatorId);
List<CustomerDimensionConfigEntity> persisted = requireCompleteRules(repository.findAll(tenantId));
return initializationResponse(persisted, normalizedKey);
CustomerChargeRuleEntity chargeRule = chargeRuleInitialization.initializeDefault(
tenantId, operatorId, normalizedKey, customerName);
return initializationResponse(persisted, chargeRule, true);
}
@Transactional
@@ -99,6 +110,12 @@ public class CustomerDimensionConfigService {
List<CustomerDimensionConfigEntity> locked =
repository.findDefaultsForCompensation(tenantId, normalizedKey);
if (!isUnchangedDefaultGroup(locked, normalizedKey)) return;
CustomerChargeRuleEntity chargeRule = chargeRuleInitialization.lockForCompensation(
tenantId, normalizedKey);
if (chargeRule != null && !chargeRuleInitialization.isUnchangedDefault(chargeRule)) return;
if (chargeRule != null) {
chargeRuleInitialization.deleteUnchangedDefault(tenantId, normalizedKey);
}
if (repository.deleteUnchangedDefaults(tenantId, normalizedKey) != CustomerDimensionBoxType.values().length) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户箱规默认配置补偿删除失败");
}
@@ -206,11 +223,16 @@ public class CustomerDimensionConfigService {
return true;
}
private static boolean ownedByInitialization(List<CustomerDimensionConfigEntity> values,
String initializationKey) {
return values.stream().allMatch(value -> initializationKey.equals(value.getInitializationKey()));
}
private static InitializeResponse initializationResponse(List<CustomerDimensionConfigEntity> values,
String initializationKey) {
boolean ownedByKey = values.stream()
.allMatch(value -> initializationKey.equals(value.getInitializationKey()));
return new InitializeResponse(values.get(0).getId(), ownedByKey);
CustomerChargeRuleEntity chargeRule,
boolean ownedByKey) {
return new InitializeResponse(values.get(0).getId(),
chargeRule == null ? null : chargeRule.getId(), ownedByKey);
}
private static GroupResponse response(long tenantId, List<CustomerDimensionConfigEntity> values) {