This commit is contained in:
2026-08-07 20:07:30 +08:00
parent c698290106
commit c4b0972dc9
31 changed files with 1089 additions and 204 deletions
@@ -3,7 +3,7 @@ package com.baiyue.firstleg.controller;
import com.baiyue.common.audit.BusinessAudit;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.Response;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.GroupResponse;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.UpdateRequest;
import com.baiyue.firstleg.service.CustomerDimensionConfigService;
import com.baiyue.firstleg.service.FirstlegScopeGuard;
@@ -30,8 +30,8 @@ public class PlatformCustomerDimensionConfigController {
@GetMapping
@PreAuthorize("hasAuthority('firstleg:dimension-config:view')")
@Operation(summary = "查询客户尺寸计算配置")
public ApiResponse<Response> get(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam long tenantId) {
public ApiResponse<GroupResponse> get(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam long tenantId) {
return ApiResponse.success(service.get(FirstlegScopeGuard.platform(user, tenantId)));
}
@@ -41,9 +41,9 @@ public class PlatformCustomerDimensionConfigController {
resourceType = "DIMENSION_CONFIG", resourceId = "#tenantId",
description = "平台编辑客户尺寸计算配置")
@Operation(summary = "编辑客户尺寸计算配置")
public ApiResponse<Response> update(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam long tenantId,
@Valid @RequestBody UpdateRequest request) {
public ApiResponse<GroupResponse> update(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam long tenantId,
@Valid @RequestBody UpdateRequest request) {
long scope = FirstlegScopeGuard.platform(user, tenantId);
return ApiResponse.success(service.update(scope, user.userId(), request));
}
@@ -21,6 +21,7 @@ import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@@ -167,6 +168,22 @@ public class PlatformInboundMeasurementController {
tenantId, inboundId, resultId, user.userId(), user.username(), request));
}
@BusinessAudit(event = "INBOUND_WEIGHT_IMAGE_DELETE", resourceType = "INBOUND_WEIGHT_RESULT",
resourceId = "#resultId", description = "删除称重图片及重量结果")
@Operation(summary = "删除称重图片", description = "删除重量结果和文件;已确认图片删除后重新汇总实际总毛重。")
@DeleteMapping("/weight-results/{resultId}")
@PreAuthorize("hasAuthority('platform:firstleg:inbound:weight:edit')")
public ApiResponse<Void> deleteWeightResult(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam long tenantId,
@RequestParam int version,
@PathVariable long inboundId,
@PathVariable long resultId) {
requirePlatformTenant(user, tenantId);
service.deleteWeightResult(tenantId, inboundId, resultId, version, user.userId());
return ApiResponse.success(null);
}
private static void requirePlatformTenant(AuthenticatedUser user, long tenantId) {
FirstlegScopeGuard.platform(user, tenantId);
if (user.userId() == null || user.userId() <= 0) {
@@ -0,0 +1,46 @@
package com.baiyue.firstleg.domain;
import java.math.BigDecimal;
/** 平台固定维护的四种客户箱规,不允许扩展或改名。 */
public enum CustomerDimensionBoxType {
SMALL("小箱", "30.000", "20.000", "20.000", "标准小箱"),
MEDIUM("中箱", "40.000", "30.000", "30.000", "标准中箱"),
LARGE("大箱", "50.000", "40.000", "40.000", "标准大箱"),
EXTRA_LARGE("特大箱", "60.000", "50.000", "50.000", "特大箱");
private final String displayName;
private final BigDecimal defaultLengthCm;
private final BigDecimal defaultWidthCm;
private final BigDecimal defaultHeightCm;
private final String defaultRemark;
CustomerDimensionBoxType(String displayName, String defaultLengthCm, String defaultWidthCm,
String defaultHeightCm, String defaultRemark) {
this.displayName = displayName;
this.defaultLengthCm = new BigDecimal(defaultLengthCm);
this.defaultWidthCm = new BigDecimal(defaultWidthCm);
this.defaultHeightCm = new BigDecimal(defaultHeightCm);
this.defaultRemark = defaultRemark;
}
public String displayName() {
return displayName;
}
public BigDecimal defaultLengthCm() {
return defaultLengthCm;
}
public BigDecimal defaultWidthCm() {
return defaultWidthCm;
}
public BigDecimal defaultHeightCm() {
return defaultHeightCm;
}
public String defaultRemark() {
return defaultRemark;
}
}
@@ -1,5 +1,7 @@
package com.baiyue.firstleg.dto;
import com.baiyue.firstleg.domain.CustomerDimensionBoxType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
@@ -10,21 +12,40 @@ import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public final class CustomerDimensionConfigDtos {
private CustomerDimensionConfigDtos() { }
public record UpdateRequest(
@NotNull @Size(min = 4, max = 4) List<@Valid RuleUpdateRequest> rules
) { }
public record RuleUpdateRequest(
@NotNull CustomerDimensionBoxType boxType,
@NotNull @DecimalMin("0.001") @Digits(integer = 6, fraction = 3) BigDecimal innerLengthCm,
@NotNull @DecimalMin("0.001") @Digits(integer = 6, fraction = 3) BigDecimal innerWidthCm,
@NotNull @DecimalMin("0.001") @Digits(integer = 6, fraction = 3) BigDecimal innerHeightCm,
@NotNull @DecimalMin("0.001") @DecimalMax("100.000") @Digits(integer = 3, fraction = 3)
BigDecimal edgeAdditionCm,
@Size(max = 500) String remark,
@NotNull @Min(0) Integer version
) { }
public record Response(
public record GroupResponse(Long tenantId, List<RuleResponse> rules) { }
public record RuleResponse(
Long id,
Long tenantId,
CustomerDimensionBoxType boxType,
String boxTypeName,
BigDecimal innerLengthCm,
BigDecimal innerWidthCm,
BigDecimal innerHeightCm,
BigDecimal edgeAdditionCm,
BigDecimal outerLengthCm,
BigDecimal outerWidthCm,
BigDecimal outerHeightCm,
BigDecimal volumeCbm,
String remark,
Integer version,
LocalDateTime createdAt,
@@ -4,12 +4,13 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baiyue.common.core.model.BaseEntity;
import com.baiyue.firstleg.domain.CustomerDimensionBoxType;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
/** 客户填写真实尺寸后使用的每侧加宽配置。 */
/** 客户固定箱规的内径和每侧加宽配置。 */
@Getter
@Setter
@TableName("fl_customer_dimension_config")
@@ -17,6 +18,10 @@ public class CustomerDimensionConfigEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private Long tenantId;
private CustomerDimensionBoxType boxType;
private BigDecimal innerLengthCm;
private BigDecimal innerWidthCm;
private BigDecimal innerHeightCm;
private BigDecimal edgeAdditionCm;
private String remark;
private String initializationKey;
@@ -5,33 +5,62 @@ import com.baiyue.firstleg.entity.CustomerDimensionConfigEntity;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
import java.util.List;
public interface CustomerDimensionConfigMapper extends BaseMapper<CustomerDimensionConfigEntity> {
@Insert("""
<script>
INSERT IGNORE INTO fl_customer_dimension_config
(id, tenant_id, edge_addition_cm, remark, initialization_key, created_by, created_at,
(id, tenant_id, box_type, inner_length_cm, inner_width_cm, inner_height_cm,
edge_addition_cm, remark, initialization_key, created_by, created_at,
updated_by, updated_at, deleted, version)
VALUES
(#{id}, #{tenantId}, #{edgeAdditionCm}, #{remark}, #{initializationKey}, #{operatorId}, CURRENT_TIMESTAMP(3),
#{operatorId}, CURRENT_TIMESTAMP(3), 0, 0)
<foreach collection="rows" item="row" separator=",">
(#{row.id}, #{row.tenantId}, #{row.boxType}, #{row.innerLengthCm}, #{row.innerWidthCm},
#{row.innerHeightCm}, #{row.edgeAdditionCm}, #{row.remark}, #{row.initializationKey},
#{operatorId}, CURRENT_TIMESTAMP(3), #{operatorId}, CURRENT_TIMESTAMP(3), 0, 0)
</foreach>
</script>
""")
int insertDefault(@Param("id") long id, @Param("tenantId") long tenantId,
@Param("edgeAdditionCm") BigDecimal edgeAdditionCm,
@Param("remark") String remark, @Param("initializationKey") String initializationKey,
@Param("operatorId") long operatorId);
int insertDefaults(@Param("rows") List<CustomerDimensionConfigEntity> rows,
@Param("operatorId") long operatorId);
@Select("""
SELECT id, tenant_id, box_type, inner_length_cm, inner_width_cm, inner_height_cm,
edge_addition_cm, remark, initialization_key, created_by, created_at,
updated_by, updated_at, deleted, version
FROM fl_customer_dimension_config
WHERE tenant_id = #{tenantId}
AND initialization_key = #{initializationKey}
AND deleted = 0
FOR UPDATE
""")
List<CustomerDimensionConfigEntity> selectDefaultsForCompensation(
@Param("tenantId") long tenantId, @Param("initializationKey") String initializationKey);
@Delete("""
DELETE FROM fl_customer_dimension_config
WHERE id = #{id}
AND tenant_id = #{tenantId}
WHERE tenant_id = #{tenantId}
AND initialization_key = #{initializationKey}
AND edge_addition_cm = #{edgeAdditionCm}
AND version = 0
AND deleted = 0
AND box_type IN ('SMALL', 'MEDIUM', 'LARGE', 'EXTRA_LARGE')
AND inner_length_cm = CASE box_type
WHEN 'SMALL' THEN 30.000 WHEN 'MEDIUM' THEN 40.000
WHEN 'LARGE' THEN 50.000 WHEN 'EXTRA_LARGE' THEN 60.000 END
AND inner_width_cm = CASE box_type
WHEN 'SMALL' THEN 20.000 WHEN 'MEDIUM' THEN 30.000
WHEN 'LARGE' THEN 40.000 WHEN 'EXTRA_LARGE' THEN 50.000 END
AND inner_height_cm = CASE box_type
WHEN 'SMALL' THEN 20.000 WHEN 'MEDIUM' THEN 30.000
WHEN 'LARGE' THEN 40.000 WHEN 'EXTRA_LARGE' THEN 50.000 END
AND edge_addition_cm = 2.000
AND remark = CASE box_type
WHEN 'SMALL' THEN '标准小箱' WHEN 'MEDIUM' THEN '标准中箱'
WHEN 'LARGE' THEN '标准大箱' WHEN 'EXTRA_LARGE' THEN '特大箱' END
""")
int deleteUnchangedDefault(@Param("tenantId") long tenantId, @Param("id") long id,
@Param("initializationKey") String initializationKey,
@Param("edgeAdditionCm") BigDecimal edgeAdditionCm);
int deleteUnchangedDefaults(@Param("tenantId") long tenantId,
@Param("initializationKey") String initializationKey);
}
@@ -6,29 +6,32 @@ import com.baiyue.firstleg.mapper.CustomerDimensionConfigMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
@Repository
public class CustomerDimensionConfigRepository {
@Resource
private CustomerDimensionConfigMapper mapper;
public CustomerDimensionConfigEntity find(long tenantId) {
return mapper.selectOne(new LambdaQueryWrapper<CustomerDimensionConfigEntity>()
public List<CustomerDimensionConfigEntity> findAll(long tenantId) {
return mapper.selectList(new LambdaQueryWrapper<CustomerDimensionConfigEntity>()
.eq(CustomerDimensionConfigEntity::getTenantId, tenantId));
}
public boolean insertDefault(long id, long tenantId, BigDecimal edgeAdditionCm,
String remark, String initializationKey, long operatorId) {
return mapper.insertDefault(id, tenantId, edgeAdditionCm, remark, initializationKey, operatorId) == 1;
public int insertDefaults(List<CustomerDimensionConfigEntity> rows, long operatorId) {
return mapper.insertDefaults(rows, operatorId);
}
public int update(CustomerDimensionConfigEntity value) {
return mapper.updateById(value);
}
public int deleteUnchangedDefault(long tenantId, long id, String initializationKey,
BigDecimal edgeAdditionCm) {
return mapper.deleteUnchangedDefault(tenantId, id, initializationKey, edgeAdditionCm);
public List<CustomerDimensionConfigEntity> findDefaultsForCompensation(long tenantId,
String initializationKey) {
return mapper.selectDefaultsForCompensation(tenantId, initializationKey);
}
public int deleteUnchangedDefaults(long tenantId, String initializationKey) {
return mapper.deleteUnchangedDefaults(tenantId, initializationKey);
}
}
@@ -157,6 +157,14 @@ public class InboundMeasurementRepository {
.eq(InboundWeightResultEntity::getId, weight.getId())) == 1;
}
public boolean deleteWeight(long tenantId, long inboundId, long resultId, int expectedVersion) {
return weights.delete(new LambdaQueryWrapper<InboundWeightResultEntity>()
.eq(InboundWeightResultEntity::getTenantId, tenantId)
.eq(InboundWeightResultEntity::getInboundId, inboundId)
.eq(InboundWeightResultEntity::getId, resultId)
.eq(InboundWeightResultEntity::getVersion, expectedVersion)) == 1;
}
public void insertRevision(InboundWeightRevisionEntity revision) {
revisions.insert(revision);
@@ -3,8 +3,11 @@ 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.domain.CustomerDimensionBoxType;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.GroupResponse;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.InitializeResponse;
import com.baiyue.firstleg.dto.CustomerDimensionConfigDtos.Response;
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.repository.CustomerDimensionConfigRepository;
@@ -13,75 +16,215 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/** 管理每个客户唯一的尺寸加边配置,不参与当前订单或计费计算。 */
/** 管理每个客户固定四种箱规的尺寸配置,不参与当前订单或计费计算。 */
@Service
public class CustomerDimensionConfigService {
public static final BigDecimal DEFAULT_EDGE_ADDITION_CM = new BigDecimal("2.000");
private static final String DEFAULT_REMARK = "默认每侧加宽 2cm";
private static final BigDecimal TWO = new BigDecimal("2");
private static final BigDecimal CUBIC_CENTIMETERS_PER_CBM = new BigDecimal("1000000");
@Resource
private CustomerDimensionConfigRepository repository;
public Response get(long tenantId) {
CustomerDimensionConfigEntity value = repository.find(tenantId);
if (value == null) {
throw new BizException(ResultCode.NOT_FOUND, "客户尺寸计算配置不存在");
}
return response(value);
public GroupResponse get(long tenantId) {
return response(tenantId, requireCompleteRules(repository.findAll(tenantId)));
}
@Transactional
public Response update(long tenantId, long operatorId, UpdateRequest request) {
CustomerDimensionConfigEntity value = repository.find(tenantId);
if (value == null) {
throw new BizException(ResultCode.NOT_FOUND, "客户尺寸计算配置不存在");
public GroupResponse update(long tenantId, long operatorId, UpdateRequest request) {
Map<CustomerDimensionBoxType, RuleUpdateRequest> requested = requireFixedRuleSet(request);
List<CustomerDimensionConfigEntity> existing = requireCompleteRules(repository.findAll(tenantId));
Map<CustomerDimensionBoxType, CustomerDimensionConfigEntity> persisted = index(existing);
for (CustomerDimensionBoxType type : CustomerDimensionBoxType.values()) {
CustomerDimensionConfigEntity value = persisted.get(type);
if (!value.getVersion().equals(requested.get(type).version())) {
throw staleConfiguration();
}
}
if (!value.getVersion().equals(request.version())) {
throw new BizException(ResultCode.CONFLICT, "尺寸计算配置已被修改,请刷新后重试");
for (CustomerDimensionBoxType type : CustomerDimensionBoxType.values()) {
CustomerDimensionConfigEntity value = persisted.get(type);
RuleUpdateRequest update = requested.get(type);
value.setInnerLengthCm(update.innerLengthCm());
value.setInnerWidthCm(update.innerWidthCm());
value.setInnerHeightCm(update.innerHeightCm());
value.setEdgeAdditionCm(update.edgeAdditionCm());
value.setRemark(normalizeRemark(update.remark()));
value.setUpdatedBy(operatorId);
if (repository.update(value) != 1) {
throw staleConfiguration();
}
}
value.setEdgeAdditionCm(request.edgeAdditionCm());
value.setRemark(normalizeRemark(request.remark()));
value.setUpdatedBy(operatorId);
if (repository.update(value) != 1) {
throw new BizException(ResultCode.CONFLICT, "尺寸计算配置已被修改,请刷新后重试");
}
return response(value);
return response(tenantId, existing);
}
@Transactional
public InitializeResponse initializeDefault(long tenantId, long operatorId, String initializationKey) {
requireTenant(tenantId);
String normalizedKey = requireInitializationKey(initializationKey);
CustomerDimensionConfigEntity existing = repository.find(tenantId);
if (existing != null) {
return new InitializeResponse(existing.getId(),
normalizedKey.equals(existing.getInitializationKey()));
List<CustomerDimensionConfigEntity> existing = repository.findAll(tenantId);
if (!existing.isEmpty()) {
List<CustomerDimensionConfigEntity> complete = requireCompleteRules(existing);
return initializationResponse(complete, normalizedKey);
}
long id = IdWorker.getId();
boolean created = repository.insertDefault(id, tenantId, DEFAULT_EDGE_ADDITION_CM,
DEFAULT_REMARK, normalizedKey, operatorId);
CustomerDimensionConfigEntity persisted = repository.find(tenantId);
if (persisted == null) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户尺寸计算配置初始化失败");
}
boolean ownedByKey = normalizedKey.equals(persisted.getInitializationKey());
return new InitializeResponse(persisted.getId(), created || ownedByKey);
repository.insertDefaults(defaultRows(tenantId, normalizedKey), operatorId);
List<CustomerDimensionConfigEntity> persisted = requireCompleteRules(repository.findAll(tenantId));
return initializationResponse(persisted, normalizedKey);
}
@Transactional
public void compensateDefault(long tenantId, String initializationKey) {
requireTenant(tenantId);
String normalizedKey = requireInitializationKey(initializationKey);
CustomerDimensionConfigEntity existing = repository.find(tenantId);
if (existing == null || !normalizedKey.equals(existing.getInitializationKey())) return;
repository.deleteUnchangedDefault(tenantId, existing.getId(), normalizedKey, DEFAULT_EDGE_ADDITION_CM);
List<CustomerDimensionConfigEntity> locked =
repository.findDefaultsForCompensation(tenantId, normalizedKey);
if (!isUnchangedDefaultGroup(locked, normalizedKey)) return;
if (repository.deleteUnchangedDefaults(tenantId, normalizedKey) != CustomerDimensionBoxType.values().length) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户箱规默认配置补偿删除失败");
}
}
private static Response response(CustomerDimensionConfigEntity value) {
return new Response(value.getId(), value.getTenantId(), value.getEdgeAdditionCm(), value.getRemark(),
value.getVersion(), value.getCreatedAt(), value.getUpdatedAt());
private static Map<CustomerDimensionBoxType, RuleUpdateRequest> requireFixedRuleSet(UpdateRequest request) {
if (request == null || request.rules() == null || request.rules().size() != CustomerDimensionBoxType.values().length) {
throw new BizException(ResultCode.BAD_REQUEST, "必须完整提交固定四种箱规");
}
Map<CustomerDimensionBoxType, RuleUpdateRequest> values = new EnumMap<>(CustomerDimensionBoxType.class);
for (RuleUpdateRequest rule : request.rules()) {
if (rule == null || rule.boxType() == null || values.put(rule.boxType(), rule) != null) {
throw new BizException(ResultCode.BAD_REQUEST, "箱规类型重复或无效");
}
requirePositive(rule.innerLengthCm(), "箱规内长");
requirePositive(rule.innerWidthCm(), "箱规内宽");
requirePositive(rule.innerHeightCm(), "箱规内高");
requirePositive(rule.edgeAdditionCm(), "箱规单侧加宽");
if (rule.version() == null || rule.version() < 0) {
throw new BizException(ResultCode.BAD_REQUEST, "箱规版本号无效");
}
}
if (values.size() != CustomerDimensionBoxType.values().length) {
throw new BizException(ResultCode.BAD_REQUEST, "必须完整提交固定四种箱规");
}
return values;
}
private static List<CustomerDimensionConfigEntity> requireCompleteRules(
List<CustomerDimensionConfigEntity> values) {
if (values == null || values.isEmpty()) {
throw new BizException(ResultCode.NOT_FOUND, "客户箱规尺寸配置不存在");
}
Map<CustomerDimensionBoxType, CustomerDimensionConfigEntity> indexed = index(values);
if (values.size() != CustomerDimensionBoxType.values().length
|| indexed.size() != CustomerDimensionBoxType.values().length) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户箱规尺寸配置不完整");
}
List<CustomerDimensionConfigEntity> ordered = new ArrayList<>(CustomerDimensionBoxType.values().length);
for (CustomerDimensionBoxType type : CustomerDimensionBoxType.values()) {
CustomerDimensionConfigEntity value = indexed.get(type);
if (value == null) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户箱规尺寸配置不完整");
}
ordered.add(value);
}
return ordered;
}
private static Map<CustomerDimensionBoxType, CustomerDimensionConfigEntity> index(
List<CustomerDimensionConfigEntity> values) {
Map<CustomerDimensionBoxType, CustomerDimensionConfigEntity> indexed =
new EnumMap<>(CustomerDimensionBoxType.class);
for (CustomerDimensionConfigEntity value : values) {
if (value == null || value.getBoxType() == null || indexed.put(value.getBoxType(), value) != null) {
throw new BizException(ResultCode.INTERNAL_ERROR, "客户箱规尺寸配置不完整");
}
}
return indexed;
}
private static List<CustomerDimensionConfigEntity> defaultRows(long tenantId, String initializationKey) {
List<CustomerDimensionConfigEntity> rows = new ArrayList<>(CustomerDimensionBoxType.values().length);
for (CustomerDimensionBoxType type : CustomerDimensionBoxType.values()) {
CustomerDimensionConfigEntity value = new CustomerDimensionConfigEntity();
value.setId(IdWorker.getId());
value.setTenantId(tenantId);
value.setBoxType(type);
value.setInnerLengthCm(type.defaultLengthCm());
value.setInnerWidthCm(type.defaultWidthCm());
value.setInnerHeightCm(type.defaultHeightCm());
value.setEdgeAdditionCm(DEFAULT_EDGE_ADDITION_CM);
value.setRemark(type.defaultRemark());
value.setInitializationKey(initializationKey);
rows.add(value);
}
return rows;
}
private static boolean isUnchangedDefaultGroup(List<CustomerDimensionConfigEntity> values,
String initializationKey) {
if (values == null || values.size() != CustomerDimensionBoxType.values().length) return false;
Map<CustomerDimensionBoxType, CustomerDimensionConfigEntity> indexed;
try {
indexed = index(values);
} catch (BizException ignored) {
return false;
}
for (CustomerDimensionBoxType type : CustomerDimensionBoxType.values()) {
CustomerDimensionConfigEntity value = indexed.get(type);
if (value == null || !initializationKey.equals(value.getInitializationKey())
|| value.getVersion() == null || value.getVersion() != 0
|| value.getInnerLengthCm() == null || value.getInnerWidthCm() == null
|| value.getInnerHeightCm() == null || value.getEdgeAdditionCm() == null
|| value.getInnerLengthCm().compareTo(type.defaultLengthCm()) != 0
|| value.getInnerWidthCm().compareTo(type.defaultWidthCm()) != 0
|| value.getInnerHeightCm().compareTo(type.defaultHeightCm()) != 0
|| value.getEdgeAdditionCm().compareTo(DEFAULT_EDGE_ADDITION_CM) != 0
|| !type.defaultRemark().equals(value.getRemark())) {
return false;
}
}
return true;
}
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);
}
private static GroupResponse response(long tenantId, List<CustomerDimensionConfigEntity> values) {
return new GroupResponse(tenantId, values.stream().map(CustomerDimensionConfigService::ruleResponse).toList());
}
private static RuleResponse ruleResponse(CustomerDimensionConfigEntity value) {
BigDecimal edgePair = value.getEdgeAdditionCm().multiply(TWO);
BigDecimal outerLength = value.getInnerLengthCm().add(edgePair);
BigDecimal outerWidth = value.getInnerWidthCm().add(edgePair);
BigDecimal outerHeight = value.getInnerHeightCm().add(edgePair);
BigDecimal volume = outerLength.multiply(outerWidth).multiply(outerHeight)
.divide(CUBIC_CENTIMETERS_PER_CBM)
.setScale(3, RoundingMode.HALF_UP);
return new RuleResponse(value.getId(), value.getBoxType(), value.getBoxType().displayName(),
value.getInnerLengthCm(), value.getInnerWidthCm(), value.getInnerHeightCm(),
value.getEdgeAdditionCm(), outerLength, outerWidth, outerHeight, volume,
value.getRemark(), value.getVersion(), value.getCreatedAt(), value.getUpdatedAt());
}
private static void requirePositive(BigDecimal value, String fieldName) {
if (value == null || value.compareTo(new BigDecimal("0.001")) < 0) {
throw new BizException(ResultCode.BAD_REQUEST, fieldName + "必须大于0");
}
}
private static BizException staleConfiguration() {
return new BizException(ResultCode.CONFLICT, "箱规尺寸配置已被修改,请刷新后重试");
}
private static String normalizeRemark(String value) {
@@ -3,7 +3,9 @@ package com.baiyue.firstleg.service;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.client.MeasurementImageClient;
import com.baiyue.firstleg.dto.InboundMeasurementRequests;
import com.baiyue.firstleg.dto.InboundMeasurementResponses;
import com.baiyue.firstleg.dto.MeasurementBatchCommand;
@@ -17,6 +19,8 @@ import com.baiyue.firstleg.repository.InboundMeasurementRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import feign.FeignException;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -32,6 +36,8 @@ public class InboundMeasurementService {
private static final String WEIGHT_CONFIRMED = "CONFIRMED";
private static final String WEIGHT_PENDING_CONFIRM = "PENDING_CONFIRM";
private static final String WEIGHT_NEEDS_MANUAL = "NEEDS_MANUAL";
private static final Set<String> WEIGHT_EDITABLE_STATUSES = Set.of(
"WAIT_MEASURE", "MEASURING", "MEASURE_REJECTED");
/** 测量完成后的默认普通托盘类型;实际拆托和库位占用由提交编排服务执行。 */
private static final String RECOMMENDED_PALLET_TYPE = "STANDARD";
private static final BigDecimal LB_TO_KG = new BigDecimal("0.45359237");
@@ -40,6 +46,10 @@ public class InboundMeasurementService {
private InboundMeasurementRepository repository;
@Resource
private MeasurementBatchService batchService;
@Resource
private MeasurementImageClient imageClient;
@Value("${baiyue.security.internal-service-token}")
private String internalToken;
public InboundMeasurementResponses.Detail detail(long tenantId, long inboundId) {
InboundOrderEntity inbound = requireInbound(tenantId, inboundId);
@@ -231,13 +241,45 @@ public class InboundMeasurementService {
return toWeight(weight);
}
@Transactional
public void deleteWeightResult(long tenantId, long inboundId, long resultId,
int version, long operatorId) {
InboundOrderEntity inbound = requireInbound(tenantId, inboundId);
if (!WEIGHT_EDITABLE_STATUSES.contains(inbound.getProcessStatus())) {
throw new BizException(ResultCode.CONFLICT, "当前入库状态不允许删除称重图片");
}
InboundWeightResultEntity weight = requireWeight(tenantId, inboundId, resultId);
requireVersion(version, weight.getVersion());
if (!repository.deleteWeight(tenantId, inboundId, resultId, version)) {
throw concurrentModification();
}
if (WEIGHT_CONFIRMED.equals(weight.getRecognitionStatus())) {
recalculateConfirmedWeight(inbound, operatorId);
}
try {
ApiResponse<Void> response = imageClient.delete(
internalToken, operatorId, tenantId, inboundId, weight.getFileId());
if (response == null || !response.successful()) {
String message = response == null || !StringUtils.hasText(response.message())
? "称重图片删除失败" : response.message();
throw new BizException(response == null ? ResultCode.SERVICE_UNAVAILABLE
: ResultCode.of(response.code()), message);
}
} catch (FeignException exception) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "文件服务暂不可用,称重图片未删除");
}
}
private void recalculateConfirmedWeight(InboundOrderEntity inbound, long operatorId) {
BigDecimal total = repository.listWeights(inbound.getTenantId(), inbound.getId()).stream()
List<InboundWeightResultEntity> confirmedWeights = repository
.listWeights(inbound.getTenantId(), inbound.getId()).stream()
.filter(weight -> WEIGHT_CONFIRMED.equals(weight.getRecognitionStatus()))
.toList();
BigDecimal total = confirmedWeights.stream()
.map(InboundWeightResultEntity::getFinalWeightKg)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(3, RoundingMode.HALF_UP);
inbound.setActualGrossWeightKg(total);
inbound.setActualGrossWeightKg(confirmedWeights.isEmpty() ? null : total);
if ("WAIT_MEASURE".equals(inbound.getProcessStatus())) {
inbound.setProcessStatus("MEASURING");
}