feat:完善计量单位管理

This commit is contained in:
hewencai
2025-10-29 17:34:42 +08:00
parent d00aebb49d
commit d120479b12
20 changed files with 2031 additions and 1 deletions

View File

@@ -19,4 +19,13 @@ public interface UnitErrorCodeConstants {
ErrorCode UNT_INFO_NOT_EXISTS =
new ErrorCode(1_010_000_004, "单位信息记录不存在");
ErrorCode UNIT_NOT_FOUND =
new ErrorCode(1_010_000_005, "找不到单位: %s");
ErrorCode UNIT_CONVERSION_PATH_NOT_FOUND =
new ErrorCode(1_010_000_006, "无法找到从单位 [%s] 到单位 [%s] 的转换路径,请检查单位是否属于同一量纲或配置转换规则");
ErrorCode UNIT_DIFFERENT_QUANTITY =
new ErrorCode(1_010_000_007, "单位 [%s] 和单位 [%s] 不属于同一量纲,无法转换");
}

View File

@@ -38,7 +38,6 @@ import com.zt.plat.module.unitmanagement.service.UnitConversion.UnitConversionSe
@Validated
public class UnitConversionController implements BusinessControllerMarker {
@Resource
private UnitConversionService unitConversionService;
@@ -105,4 +104,54 @@ public class UnitConversionController implements BusinessControllerMarker {
BeanUtils.toBean(list, UnitConversionRespVO.class));
}
@PostMapping("/convert")
@Operation(summary = "单位转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<UnitConvertRespVO> convert(@Valid @RequestBody UnitConvertReqVO convertReqVO) {
return success(unitConversionService.convert(convertReqVO));
}
@PostMapping("/batch-convert")
@Operation(summary = "批量单位转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<BatchUnitConvertRespVO> batchConvert(@Valid @RequestBody BatchUnitConvertReqVO batchReqVO) {
return success(unitConversionService.batchConvert(batchReqVO));
}
@PostMapping("/convert-by-symbol")
@Operation(summary = "按单位符号转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<UnitConvertRespVO> convertBySymbol(@Valid @RequestBody UnitConvertBySymbolReqVO reqVO) {
return success(unitConversionService.convertBySymbol(reqVO));
}
@PostMapping("/convert-by-name")
@Operation(summary = "按单位名称转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<UnitConvertRespVO> convertByName(@Valid @RequestBody UnitConvertByNameReqVO reqVO) {
return success(unitConversionService.convertByName(reqVO));
}
@PostMapping("/batch-convert-by-symbol")
@Operation(summary = "批量按单位符号转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<BatchUnitConvertRespVO> batchConvertBySymbol(@Valid @RequestBody BatchUnitConvertBySymbolReqVO reqVO) {
return success(unitConversionService.batchConvertBySymbol(reqVO));
}
@PostMapping("/batch-convert-by-name")
@Operation(summary = "批量按单位名称转换")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<BatchUnitConvertRespVO> batchConvertByName(@Valid @RequestBody BatchUnitConvertByNameReqVO reqVO) {
return success(unitConversionService.batchConvertByName(reqVO));
}
@GetMapping("/validate-paths")
@Operation(summary = "校验量纲的转换路径")
@Parameter(name = "quantityId", description = "量纲ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-conversion:query')")
public CommonResult<UnitConversionValidationRespVO> validateConversionPaths(@RequestParam("quantityId") Long quantityId) {
return success(unitConversionService.validateConversionPaths(quantityId));
}
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - 批量按名称单位转换 Request VO")
@Data
public class BatchUnitConvertByNameReqVO {
@Schema(description = "源单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "千克")
@NotBlank(message = "源单位名称不能为空")
private String srcUnitName;
@Schema(description = "目标单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
@NotBlank(message = "目标单位名称不能为空")
private String tgtUnitName;
@Schema(description = "待转换的值列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "待转换的值列表不能为空")
private List<BigDecimal> values;
@Schema(description = "精度(小数位数)", example = "6")
private Integer precision = 6;
@Schema(description = "是否忽略错误(true:遇到错误继续执行, false:遇到错误立即停止)", example = "false")
private Boolean ignoreErrors = false;
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - 批量按符号单位转换 Request VO")
@Data
public class BatchUnitConvertBySymbolReqVO {
@Schema(description = "源单位符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "m")
@NotBlank(message = "源单位符号不能为空")
private String srcUnitSymbol;
@Schema(description = "目标单位符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "km")
@NotBlank(message = "目标单位符号不能为空")
private String tgtUnitSymbol;
@Schema(description = "待转换的值列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "待转换的值列表不能为空")
private List<BigDecimal> values;
@Schema(description = "精度(小数位数)", example = "6")
private Integer precision = 6;
@Schema(description = "是否忽略错误(true:遇到错误继续执行, false:遇到错误立即停止)", example = "false")
private Boolean ignoreErrors = false;
}

View File

@@ -0,0 +1,19 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
@Schema(description = "管理后台 - 批量单位转换 Request VO")
@Data
public class BatchUnitConvertReqVO {
@Schema(description = "转换项列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "转换项列表不能为空")
private List<UnitConvertReqVO> items;
@Schema(description = "是否忽略错误(true:遇到错误继续执行, false:遇到错误立即停止)", example = "false")
private Boolean ignoreErrors = false;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Builder;
import java.util.List;
@Schema(description = "管理后台 - 批量单位转换 Response VO")
@Data
@Builder
public class BatchUnitConvertRespVO {
@Schema(description = "转换结果列表")
private List<UnitConvertResultItem> results;
@Schema(description = "成功数量", example = "10")
private Integer successCount;
@Schema(description = "失败数量", example = "0")
private Integer failureCount;
@Schema(description = "总数量", example = "10")
private Integer totalCount;
@Schema(description = "转换结果项")
@Data
@Builder
public static class UnitConvertResultItem {
@Schema(description = "是否成功", example = "true")
private Boolean success;
@Schema(description = "转换结果(成功时返回)")
private UnitConvertRespVO data;
@Schema(description = "错误信息(失败时返回)", example = "找不到转换规则")
private String errorMessage;
@Schema(description = "原始请求")
private UnitConvertReqVO request;
}
}

View File

@@ -0,0 +1,110 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 单位转换路径校验响应 VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UnitConversionValidationRespVO {
@Schema(description = "量纲ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long quantityId;
@Schema(description = "量纲名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "长度")
private String quantityName;
@Schema(description = "量纲符号", example = "L")
private String quantitySymbol;
@Schema(description = "总单位数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
private Integer totalUnits;
@Schema(description = "可转换单位数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
private Integer convertibleUnits;
@Schema(description = "不可转换单位数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer unconvertibleUnits;
@Schema(description = "是否全部可转换", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean allConvertible;
@Schema(description = "基准单位信息")
private BaseUnitInfo baseUnit;
@Schema(description = "不可转换的单位列表")
private List<UnconvertibleUnitInfo> unconvertibleUnitList;
@Schema(description = "转换路径详情")
private List<ConversionPathInfo> conversionPaths;
@Schema(description = "基准单位信息")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class BaseUnitInfo {
@Schema(description = "单位ID", example = "1")
private Long unitId;
@Schema(description = "单位名称", example = "")
private String unitName;
@Schema(description = "单位符号", example = "m")
private String unitSymbol;
}
@Schema(description = "不可转换单位信息")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class UnconvertibleUnitInfo {
@Schema(description = "单位ID", example = "1")
private Long unitId;
@Schema(description = "单位名称", example = "光年")
private String unitName;
@Schema(description = "单位符号", example = "ly")
private String unitSymbol;
@Schema(description = "原因", example = "缺少到基准单位的转换规则")
private String reason;
}
@Schema(description = "转换路径信息")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ConversionPathInfo {
@Schema(description = "源单位ID", example = "1")
private Long srcUnitId;
@Schema(description = "源单位名称", example = "千米")
private String srcUnitName;
@Schema(description = "目标单位ID", example = "2")
private Long tgtUnitId;
@Schema(description = "目标单位名称", example = "")
private String tgtUnitName;
@Schema(description = "是否有直接转换", example = "true")
private Boolean hasDirect;
@Schema(description = "是否可通过基准单位转换", example = "true")
private Boolean hasViaBase;
@Schema(description = "转换路径描述", example = "千米 → 米 (直接转换)")
private String pathDescription;
}
}

View File

@@ -0,0 +1,28 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 按名称单位转换 Request VO")
@Data
public class UnitConvertByNameReqVO {
@Schema(description = "源单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "千克")
@NotBlank(message = "源单位名称不能为空")
private String srcUnitName;
@Schema(description = "目标单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
@NotBlank(message = "目标单位名称不能为空")
private String tgtUnitName;
@Schema(description = "待转换的值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
@NotNull(message = "待转换的值不能为空")
private BigDecimal value;
@Schema(description = "精度(小数位数)", example = "6")
private Integer precision = 6;
}

View File

@@ -0,0 +1,28 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 按符号单位转换 Request VO")
@Data
public class UnitConvertBySymbolReqVO {
@Schema(description = "源单位符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "kg")
@NotBlank(message = "源单位符号不能为空")
private String srcUnitSymbol;
@Schema(description = "目标单位符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "t")
@NotBlank(message = "目标单位符号不能为空")
private String tgtUnitSymbol;
@Schema(description = "待转换的值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
@NotNull(message = "待转换的值不能为空")
private BigDecimal value;
@Schema(description = "精度(小数位数)", example = "6")
private Integer precision = 6;
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 单位转换 Request VO")
@Data
public class UnitConvertReqVO {
@Schema(description = "源单位ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "源单位ID不能为空")
private Long srcUntId;
@Schema(description = "目标单位ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "目标单位ID不能为空")
private Long tgtUntId;
@Schema(description = "待转换的值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "待转换的值不能为空")
private BigDecimal value;
@Schema(description = "精度(小数位数)", example = "6")
private Integer precision = 6;
}

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitConversion.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Builder;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 单位转换 Response VO")
@Data
@Builder
public class UnitConvertRespVO {
@Schema(description = "源单位ID", example = "1")
private Long srcUntId;
@Schema(description = "源单位名称", example = "")
private String srcUntName;
@Schema(description = "源单位符号", example = "m")
private String srcUntSmb;
@Schema(description = "目标单位ID", example = "2")
private Long tgtUntId;
@Schema(description = "目标单位名称", example = "千米")
private String tgtUntName;
@Schema(description = "目标单位符号", example = "km")
private String tgtUntSmb;
@Schema(description = "原始值", example = "1000")
private BigDecimal originalValue;
@Schema(description = "转换后的值", example = "1")
private BigDecimal convertedValue;
@Schema(description = "转换因子", example = "0.001")
private BigDecimal factor;
@Schema(description = "转换公式", example = "1000m = 1km")
private String formula;
@Schema(description = "转换策略", example = "DIRECT")
private String strategy;
}

View File

@@ -105,4 +105,11 @@ public class UnitQuantityController implements BusinessControllerMarker {
BeanUtils.toBean(list, UnitQuantityRespVO.class));
}
@GetMapping("/tree")
@Operation(summary = "获取量纲及单位树")
@PreAuthorize("@ss.hasPermission('unitmanagement:unit-quantity:query')")
public CommonResult<List<UnitQuantityTreeRespVO>> getUnitQuantityTree() {
return success(unitQuantityService.getUnitQuantityTree());
}
}

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.unitmanagement.controller.admin.UnitQuantity.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 量纲及单位树 Response VO")
@Data
public class UnitQuantityTreeRespVO {
@Schema(description = "量纲ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "量纲名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "长度")
private String name;
@Schema(description = "量纲符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "L")
private String symbol;
@Schema(description = "量纲描述", example = "用于测量物体长短、距离的物理量")
private String dsp;
@Schema(description = "下属单位列表")
private List<UnitItemVO> units;
@Schema(description = "单位项")
@Data
public static class UnitItemVO {
@Schema(description = "单位ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private String name;
@Schema(description = "单位符号", requiredMode = Schema.RequiredMode.REQUIRED, example = "m")
private String smb;
@Schema(description = "是否基准单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer isBse;
@Schema(description = "关联关系ID", example = "1")
private Long relationId;
}
}

View File

@@ -0,0 +1,239 @@
package com.zt.plat.module.unitmanagement.service.UnitConversion;
import com.zt.plat.module.unitmanagement.dal.dao.UnitConversion.UnitConversionMapper;
import com.zt.plat.module.unitmanagement.dal.dao.QuantityUnitRelation.QuantityUnitRelationMapper;
import com.zt.plat.module.unitmanagement.dal.dao.UntInfo.UntInfoMapper;
import com.zt.plat.module.unitmanagement.dal.dataobject.UnitConversion.UnitConversionDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.QuantityUnitRelation.QuantityUnitRelationDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.UntInfo.UntInfoDO;
import com.zt.plat.module.unitmanagement.util.UnitConversionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
/**
* 单位转换辅助服务
*
* 为其他模块提供简单易用的单位转换方法
* 支持按单位符号和单位名称进行转换
*
* @author 系统
*/
@Service
@Slf4j
public class UnitConversionHelperService {
@Resource
private UnitConversionMapper unitConversionMapper;
@Resource
private QuantityUnitRelationMapper quantityUnitRelationMapper;
@Resource
private UntInfoMapper untInfoMapper;
/**
* 按单位符号转换(推荐使用)
*
* @param value 待转换的值
* @param srcSymbol 源单位符号(如: "kg", "m", "km"
* @param tgtSymbol 目标单位符号
* @return 转换后的值
*/
public BigDecimal convertBySymbol(BigDecimal value, String srcSymbol, String tgtSymbol) {
return convertBySymbol(value, srcSymbol, tgtSymbol, 6);
}
/**
* 按单位符号转换(推荐使用)
*
* @param value 待转换的值
* @param srcSymbol 源单位符号(如: "kg", "m", "km"
* @param tgtSymbol 目标单位符号
* @param precision 精度(小数位数)
* @return 转换后的值
*/
public BigDecimal convertBySymbol(BigDecimal value, String srcSymbol, String tgtSymbol, int precision) {
log.debug("按符号转换: {} {} → {}", value, srcSymbol, tgtSymbol);
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertBySymbol(
value, srcSymbol, tgtSymbol, conversions, relations, units, precision
);
log.debug("转换结果: {} (策略: {})", result.getValue(), result.getStrategy());
return result.getValue();
}
/**
* 按单位名称转换
*
* @param value 待转换的值
* @param srcName 源单位名称(如: "千克", "米", "千米"
* @param tgtName 目标单位名称
* @return 转换后的值
*/
public BigDecimal convertByName(BigDecimal value, String srcName, String tgtName) {
return convertByName(value, srcName, tgtName, 6);
}
/**
* 按单位名称转换
*
* @param value 待转换的值
* @param srcName 源单位名称(如: "千克", "米", "千米"
* @param tgtName 目标单位名称
* @param precision 精度(小数位数)
* @return 转换后的值
*/
public BigDecimal convertByName(BigDecimal value, String srcName, String tgtName, int precision) {
log.debug("按名称转换: {} {} → {}", value, srcName, tgtName);
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertByName(
value, srcName, tgtName, conversions, relations, units, precision
);
log.debug("转换结果: {} (策略: {})", result.getValue(), result.getStrategy());
return result.getValue();
}
/**
* 按单位符号批量转换
*
* @param values 待转换的值列表
* @param srcSymbol 源单位符号
* @param tgtSymbol 目标单位符号
* @return 转换后的值列表
*/
public List<BigDecimal> batchConvertBySymbol(List<BigDecimal> values, String srcSymbol, String tgtSymbol) {
return batchConvertBySymbol(values, srcSymbol, tgtSymbol, 6);
}
/**
* 按单位符号批量转换
*
* @param values 待转换的值列表
* @param srcSymbol 源单位符号
* @param tgtSymbol 目标单位符号
* @param precision 精度(小数位数)
* @return 转换后的值列表
*/
public List<BigDecimal> batchConvertBySymbol(List<BigDecimal> values, String srcSymbol, String tgtSymbol, int precision) {
log.debug("批量按符号转换: {} 个值, {} → {}", values.size(), srcSymbol, tgtSymbol);
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
List<UnitConversionUtil.ConversionResult> results = UnitConversionUtil.batchConvertBySymbol(
values, srcSymbol, tgtSymbol, conversions, relations, units, precision
);
// 提取转换后的值
return results.stream()
.map(UnitConversionUtil.ConversionResult::getValue)
.toList();
}
/**
* 按单位名称批量转换
*
* @param values 待转换的值列表
* @param srcName 源单位名称
* @param tgtName 目标单位名称
* @return 转换后的值列表
*/
public List<BigDecimal> batchConvertByName(List<BigDecimal> values, String srcName, String tgtName) {
return batchConvertByName(values, srcName, tgtName, 6);
}
/**
* 按单位名称批量转换
*
* @param values 待转换的值列表
* @param srcName 源单位名称
* @param tgtName 目标单位名称
* @param precision 精度(小数位数)
* @return 转换后的值列表
*/
public List<BigDecimal> batchConvertByName(List<BigDecimal> values, String srcName, String tgtName, int precision) {
log.debug("批量按名称转换: {} 个值, {} → {}", values.size(), srcName, tgtName);
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
List<UnitConversionUtil.ConversionResult> results = UnitConversionUtil.batchConvertByName(
values, srcName, tgtName, conversions, relations, units, precision
);
// 提取转换后的值
return results.stream()
.map(UnitConversionUtil.ConversionResult::getValue)
.toList();
}
/**
* 按单位符号转换(返回完整结果)
*
* @param value 待转换的值
* @param srcSymbol 源单位符号
* @param tgtSymbol 目标单位符号
* @param precision 精度(小数位数)
* @return 转换结果(包含值、因子、策略等)
*/
public UnitConversionUtil.ConversionResult convertBySymbolWithDetails(
BigDecimal value, String srcSymbol, String tgtSymbol, int precision) {
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
return UnitConversionUtil.convertBySymbol(
value, srcSymbol, tgtSymbol, conversions, relations, units, precision
);
}
/**
* 按单位名称转换(返回完整结果)
*
* @param value 待转换的值
* @param srcName 源单位名称
* @param tgtName 目标单位名称
* @param precision 精度(小数位数)
* @return 转换结果(包含值、因子、策略等)
*/
public UnitConversionUtil.ConversionResult convertByNameWithDetails(
BigDecimal value, String srcName, String tgtName, int precision) {
// 加载数据
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 调用工具类
return UnitConversionUtil.convertByName(
value, srcName, tgtName, conversions, relations, units, precision
);
}
}

View File

@@ -59,4 +59,60 @@ public interface UnitConversionService {
*/
PageResult<UnitConversionDO> getUnitConversionPage(UnitConversionPageReqVO pageReqVO);
/**
* 单位转换
*
* @param convertReqVO 转换请求
* @return 转换结果
*/
UnitConvertRespVO convert(@Valid UnitConvertReqVO convertReqVO);
/**
* 批量单位转换
*
* @param batchReqVO 批量转换请求
* @return 批量转换结果
*/
BatchUnitConvertRespVO batchConvert(@Valid BatchUnitConvertReqVO batchReqVO);
/**
* 按单位符号转换
*
* @param reqVO 转换请求
* @return 转换结果
*/
UnitConvertRespVO convertBySymbol(@Valid UnitConvertBySymbolReqVO reqVO);
/**
* 按单位名称转换
*
* @param reqVO 转换请求
* @return 转换结果
*/
UnitConvertRespVO convertByName(@Valid UnitConvertByNameReqVO reqVO);
/**
* 批量按单位符号转换
*
* @param reqVO 批量转换请求
* @return 批量转换结果
*/
BatchUnitConvertRespVO batchConvertBySymbol(@Valid BatchUnitConvertBySymbolReqVO reqVO);
/**
* 批量按单位名称转换
*
* @param reqVO 批量转换请求
* @return 批量转换结果
*/
BatchUnitConvertRespVO batchConvertByName(@Valid BatchUnitConvertByNameReqVO reqVO);
/**
* 校验量纲内所有单位的转换路径
*
* @param quantityId 量纲ID
* @return 校验结果
*/
UnitConversionValidationRespVO validateConversionPaths(Long quantityId);
}

View File

@@ -14,6 +14,18 @@ import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.unitmanagement.dal.dao.UnitConversion.UnitConversionMapper;
import com.zt.plat.module.unitmanagement.dal.dao.QuantityUnitRelation.QuantityUnitRelationMapper;
import com.zt.plat.module.unitmanagement.dal.dao.UntInfo.UntInfoMapper;
import com.zt.plat.module.unitmanagement.dal.dataobject.QuantityUnitRelation.QuantityUnitRelationDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.UntInfo.UntInfoDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.UnitQuantity.UnitQuantityDO;
import com.zt.plat.module.unitmanagement.service.UnitQuantity.UnitQuantityService;
import com.zt.plat.module.unitmanagement.util.UnitConversionUtil;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList;
@@ -27,11 +39,21 @@ import static com.zt.plat.module.unitmanagement.enums.UnitErrorCodeConstants.*;
*/
@Service
@Validated
@Slf4j
public class UnitConversionServiceImpl implements UnitConversionService {
@Resource
private UnitConversionMapper unitConversionMapper;
@Resource
private QuantityUnitRelationMapper quantityUnitRelationMapper;
@Resource
private UntInfoMapper untInfoMapper;
@Resource
private UnitQuantityService unitQuantityService;
@Override
public UnitConversionRespVO createUnitConversion(UnitConversionSaveReqVO createReqVO) {
// 插入
@@ -89,4 +111,508 @@ public class UnitConversionServiceImpl implements UnitConversionService {
return unitConversionMapper.selectPage(pageReqVO);
}
@Override
public UnitConvertRespVO convert(UnitConvertReqVO convertReqVO) {
log.info("开始单位转换: 源单位ID={}, 目标单位ID={}, 值={}",
convertReqVO.getSrcUntId(), convertReqVO.getTgtUntId(), convertReqVO.getValue());
// 1. 查询所有转换规则和关联关系
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
// 2. 使用工具类进行转换
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
convertReqVO.getValue(),
convertReqVO.getSrcUntId(),
convertReqVO.getTgtUntId(),
conversions,
relations,
convertReqVO.getPrecision() != null ? convertReqVO.getPrecision() : 6
);
// 3. 查询单位信息
UntInfoDO srcUnit = untInfoMapper.selectById(convertReqVO.getSrcUntId());
UntInfoDO tgtUnit = untInfoMapper.selectById(convertReqVO.getTgtUntId());
// 4. 构建响应
return UnitConvertRespVO.builder()
.srcUntId(convertReqVO.getSrcUntId())
.srcUntName(srcUnit != null ? srcUnit.getName() : null)
.srcUntSmb(srcUnit != null ? srcUnit.getSmb() : null)
.tgtUntId(convertReqVO.getTgtUntId())
.tgtUntName(tgtUnit != null ? tgtUnit.getName() : null)
.tgtUntSmb(tgtUnit != null ? tgtUnit.getSmb() : null)
.originalValue(convertReqVO.getValue())
.convertedValue(result.getValue())
.factor(result.getFactor())
.formula(result.getFormula())
.strategy(result.getStrategy().name())
.build();
}
@Override
public BatchUnitConvertRespVO batchConvert(BatchUnitConvertReqVO batchReqVO) {
log.info("开始批量单位转换: 转换项数量={}, 忽略错误={}",
batchReqVO.getItems().size(), batchReqVO.getIgnoreErrors());
List<BatchUnitConvertRespVO.UnitConvertResultItem> results = new ArrayList<>();
int successCount = 0;
int failureCount = 0;
for (UnitConvertReqVO item : batchReqVO.getItems()) {
try {
// 执行转换
UnitConvertRespVO convertResult = convert(item);
// 构建成功结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(true)
.data(convertResult)
.request(item)
.build());
successCount++;
} catch (Exception e) {
log.warn("单位转换失败: 源单位ID={}, 目标单位ID={}, 错误={}",
item.getSrcUntId(), item.getTgtUntId(), e.getMessage());
// 构建失败结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(false)
.errorMessage(e.getMessage())
.request(item)
.build());
failureCount++;
// 如果不忽略错误,直接抛出异常
if (!Boolean.TRUE.equals(batchReqVO.getIgnoreErrors())) {
throw e;
}
}
}
return BatchUnitConvertRespVO.builder()
.results(results)
.successCount(successCount)
.failureCount(failureCount)
.totalCount(batchReqVO.getItems().size())
.build();
}
@Override
public UnitConvertRespVO convertBySymbol(UnitConvertBySymbolReqVO reqVO) {
log.info("开始按符号转换: 源单位符号={}, 目标单位符号={}, 值={}",
reqVO.getSrcUnitSymbol(), reqVO.getTgtUnitSymbol(), reqVO.getValue());
// 1. 查询所有转换规则、关联关系和单位信息
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 2. 使用工具类进行转换
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertBySymbol(
reqVO.getValue(),
reqVO.getSrcUnitSymbol(),
reqVO.getTgtUnitSymbol(),
conversions,
relations,
units,
reqVO.getPrecision() != null ? reqVO.getPrecision() : 6
);
// 3. 构建响应
return UnitConvertRespVO.builder()
.srcUntName(reqVO.getSrcUnitSymbol())
.srcUntSmb(reqVO.getSrcUnitSymbol())
.tgtUntName(reqVO.getTgtUnitSymbol())
.tgtUntSmb(reqVO.getTgtUnitSymbol())
.originalValue(reqVO.getValue())
.convertedValue(result.getValue())
.factor(result.getFactor())
.formula(result.getFormula())
.strategy(result.getStrategy().name())
.build();
}
@Override
public UnitConvertRespVO convertByName(UnitConvertByNameReqVO reqVO) {
log.info("开始按名称转换: 源单位名称={}, 目标单位名称={}, 值={}",
reqVO.getSrcUnitName(), reqVO.getTgtUnitName(), reqVO.getValue());
// 1. 查询所有转换规则、关联关系和单位信息
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
// 2. 使用工具类进行转换
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertByName(
reqVO.getValue(),
reqVO.getSrcUnitName(),
reqVO.getTgtUnitName(),
conversions,
relations,
units,
reqVO.getPrecision() != null ? reqVO.getPrecision() : 6
);
// 3. 构建响应
return UnitConvertRespVO.builder()
.srcUntName(reqVO.getSrcUnitName())
.tgtUntName(reqVO.getTgtUnitName())
.originalValue(reqVO.getValue())
.convertedValue(result.getValue())
.factor(result.getFactor())
.formula(result.getFormula())
.strategy(result.getStrategy().name())
.build();
}
@Override
public BatchUnitConvertRespVO batchConvertBySymbol(BatchUnitConvertBySymbolReqVO reqVO) {
log.info("开始批量按符号转换: 源单位符号={}, 目标单位符号={}, 值数量={}",
reqVO.getSrcUnitSymbol(), reqVO.getTgtUnitSymbol(), reqVO.getValues().size());
List<BatchUnitConvertRespVO.UnitConvertResultItem> results = new ArrayList<>();
int successCount = 0;
int failureCount = 0;
// 1. 查询所有转换规则、关联关系和单位信息(只查询一次)
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
for (BigDecimal value : reqVO.getValues()) {
try {
// 2. 使用工具类进行转换
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertBySymbol(
value,
reqVO.getSrcUnitSymbol(),
reqVO.getTgtUnitSymbol(),
conversions,
relations,
units,
reqVO.getPrecision() != null ? reqVO.getPrecision() : 6
);
// 3. 构建成功结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(true)
.data(UnitConvertRespVO.builder()
.srcUntSmb(reqVO.getSrcUnitSymbol())
.tgtUntSmb(reqVO.getTgtUnitSymbol())
.originalValue(value)
.convertedValue(result.getValue())
.factor(result.getFactor())
.strategy(result.getStrategy().name())
.build())
.build());
successCount++;
} catch (Exception e) {
log.warn("按符号转换失败: 源单位符号={}, 目标单位符号={}, 值={}, 错误={}",
reqVO.getSrcUnitSymbol(), reqVO.getTgtUnitSymbol(), value, e.getMessage());
// 4. 构建失败结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(false)
.errorMessage(e.getMessage())
.build());
failureCount++;
// 5. 如果不忽略错误,直接抛出异常
if (!Boolean.TRUE.equals(reqVO.getIgnoreErrors())) {
throw e;
}
}
}
return BatchUnitConvertRespVO.builder()
.results(results)
.successCount(successCount)
.failureCount(failureCount)
.totalCount(reqVO.getValues().size())
.build();
}
@Override
public BatchUnitConvertRespVO batchConvertByName(BatchUnitConvertByNameReqVO reqVO) {
log.info("开始批量按名称转换: 源单位名称={}, 目标单位名称={}, 值数量={}",
reqVO.getSrcUnitName(), reqVO.getTgtUnitName(), reqVO.getValues().size());
List<BatchUnitConvertRespVO.UnitConvertResultItem> results = new ArrayList<>();
int successCount = 0;
int failureCount = 0;
// 1. 查询所有转换规则、关联关系和单位信息(只查询一次)
List<UnitConversionDO> conversions = unitConversionMapper.selectList();
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
List<UntInfoDO> units = untInfoMapper.selectList();
for (BigDecimal value : reqVO.getValues()) {
try {
// 2. 使用工具类进行转换
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convertByName(
value,
reqVO.getSrcUnitName(),
reqVO.getTgtUnitName(),
conversions,
relations,
units,
reqVO.getPrecision() != null ? reqVO.getPrecision() : 6
);
// 3. 构建成功结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(true)
.data(UnitConvertRespVO.builder()
.srcUntName(reqVO.getSrcUnitName())
.tgtUntName(reqVO.getTgtUnitName())
.originalValue(value)
.convertedValue(result.getValue())
.factor(result.getFactor())
.strategy(result.getStrategy().name())
.build())
.build());
successCount++;
} catch (Exception e) {
log.warn("按名称转换失败: 源单位名称={}, 目标单位名称={}, 值={}, 错误={}",
reqVO.getSrcUnitName(), reqVO.getTgtUnitName(), value, e.getMessage());
// 4. 构建失败结果
results.add(BatchUnitConvertRespVO.UnitConvertResultItem.builder()
.success(false)
.errorMessage(e.getMessage())
.build());
failureCount++;
// 5. 如果不忽略错误,直接抛出异常
if (!Boolean.TRUE.equals(reqVO.getIgnoreErrors())) {
throw e;
}
}
}
return BatchUnitConvertRespVO.builder()
.results(results)
.successCount(successCount)
.failureCount(failureCount)
.totalCount(reqVO.getValues().size())
.build();
}
@Override
public UnitConversionValidationRespVO validateConversionPaths(Long quantityId) {
log.info("开始校验量纲 {} 的转换路径", quantityId);
// 1. 查询量纲信息
UnitQuantityDO quantity = unitQuantityService.getUnitQuantity(quantityId);
if (quantity == null) {
throw exception(UNIT_QUANTITY_NOT_EXISTS);
}
// 2. 查询该量纲下的所有单位
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList(
new LambdaQueryWrapperX<QuantityUnitRelationDO>()
.eq(QuantityUnitRelationDO::getUntQtyId, quantityId)
);
if (CollUtil.isEmpty(relations)) {
return buildEmptyValidationResult(quantityId, quantity);
}
// 3. 获取单位详细信息
List<Long> unitIds = relations.stream()
.map(QuantityUnitRelationDO::getUntId)
.collect(Collectors.toList());
List<UntInfoDO> units = untInfoMapper.selectByIds(unitIds);
Map<Long, UntInfoDO> unitMap = units.stream()
.collect(Collectors.toMap(UntInfoDO::getId, u -> u));
// 4. 找到基准单位
QuantityUnitRelationDO baseRelation = relations.stream()
.filter(r -> r.getIsBse() == 1)
.findFirst()
.orElse(null);
UnitConversionValidationRespVO.BaseUnitInfo baseUnitInfo = null;
Long baseUnitId = null;
if (baseRelation != null) {
UntInfoDO baseUnit = unitMap.get(baseRelation.getUntId());
baseUnitId = baseRelation.getUntId();
if (baseUnit != null) {
baseUnitInfo = UnitConversionValidationRespVO.BaseUnitInfo.builder()
.unitId(baseUnit.getId())
.unitName(baseUnit.getName())
.unitSymbol(baseUnit.getSmb())
.build();
}
}
// 5. 查询该量纲下的所有转换规则
List<UnitConversionDO> conversions = unitConversionMapper.selectList(
new LambdaQueryWrapperX<UnitConversionDO>()
.eq(UnitConversionDO::getUntQtyId, quantityId)
);
// 6. 检查所有单位对之间的转换路径(两两互相转换)
List<UnitConversionValidationRespVO.UnconvertibleUnitInfo> unconvertibleUnits = new ArrayList<>();
List<UnitConversionValidationRespVO.ConversionPathInfo> conversionPaths = new ArrayList<>();
// 统计:能够互相转换的单位数量
Set<Long> fullyConvertibleUnits = new HashSet<>();
// 遍历所有单位对
for (int i = 0; i < units.size(); i++) {
UntInfoDO srcUnit = units.get(i);
boolean canConvertToAll = true;
for (int j = 0; j < units.size(); j++) {
if (i == j) continue; // 跳过自己
UntInfoDO tgtUnit = units.get(j);
// 尝试转换(使用工具类)
boolean canConvert = canConvert(srcUnit.getId(), tgtUnit.getId(), conversions, relations, baseUnitId);
if (!canConvert) {
canConvertToAll = false;
// 只记录第一个方向的失败避免重复A→B 和 B→A
if (i < j) {
unconvertibleUnits.add(UnitConversionValidationRespVO.UnconvertibleUnitInfo.builder()
.unitId(srcUnit.getId())
.unitName(srcUnit.getName())
.unitSymbol(srcUnit.getSmb())
.reason(String.format("无法转换到 %s", tgtUnit.getName()))
.build());
}
} else {
// 记录转换路径(只记录到基准单位的路径,避免太多)
if (baseUnitId != null && tgtUnit.getId().equals(baseUnitId)) {
String strategy = getConversionStrategy(srcUnit.getId(), tgtUnit.getId(), conversions, baseUnitId);
conversionPaths.add(UnitConversionValidationRespVO.ConversionPathInfo.builder()
.srcUnitId(srcUnit.getId())
.srcUnitName(srcUnit.getName())
.tgtUnitId(tgtUnit.getId())
.tgtUnitName(tgtUnit.getName())
.hasDirect(strategy.equals("DIRECT"))
.hasViaBase(strategy.equals("VIA_BASE_UNIT"))
.pathDescription(srcUnit.getName() + "" + tgtUnit.getName() +
(strategy.equals("DIRECT") ? " (直接转换)" : " (通过基准单位)"))
.build());
}
}
}
if (canConvertToAll) {
fullyConvertibleUnits.add(srcUnit.getId());
}
}
// 7. 构建响应
return UnitConversionValidationRespVO.builder()
.quantityId(quantityId)
.quantityName(quantity.getName())
.quantitySymbol(quantity.getSymbol())
.totalUnits(units.size())
.convertibleUnits(fullyConvertibleUnits.size())
.unconvertibleUnits(units.size() - fullyConvertibleUnits.size())
.allConvertible(fullyConvertibleUnits.size() == units.size())
.baseUnit(baseUnitInfo)
.unconvertibleUnitList(unconvertibleUnits)
.conversionPaths(conversionPaths)
.build();
}
/**
* 检查两个单位是否能够转换(考虑反向推导)
*/
private boolean canConvert(Long srcUnitId, Long tgtUnitId,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
Long baseUnitId) {
// 1. 检查直接转换(正向)
boolean hasDirectForward = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(srcUnitId) && c.getTgtUntId().equals(tgtUnitId));
if (hasDirectForward) {
return true;
}
// 2. 检查直接转换(反向,可以推导)
boolean hasDirectReverse = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(tgtUnitId) && c.getTgtUntId().equals(srcUnitId));
if (hasDirectReverse) {
return true;
}
// 3. 检查通过基准单位的间接转换
if (baseUnitId == null) {
return false;
}
// 如果源单位是基准单位
if (srcUnitId.equals(baseUnitId)) {
// 检查是否有 基准 → 目标 的转换规则(正向或反向)
boolean hasBaseToTgt = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(baseUnitId) && c.getTgtUntId().equals(tgtUnitId));
boolean hasTgtToBase = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(tgtUnitId) && c.getTgtUntId().equals(baseUnitId));
return hasBaseToTgt || hasTgtToBase;
}
// 如果目标单位是基准单位
if (tgtUnitId.equals(baseUnitId)) {
// 检查是否有 源 → 基准 的转换规则(正向或反向)
boolean hasSrcToBase = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(srcUnitId) && c.getTgtUntId().equals(baseUnitId));
boolean hasBaseToSrc = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(baseUnitId) && c.getTgtUntId().equals(srcUnitId));
return hasSrcToBase || hasBaseToSrc;
}
// 两者都不是基准单位,需要两步转换:源 → 基准 → 目标
// 检查 源 → 基准(正向或反向)
boolean srcToBase = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(srcUnitId) && c.getTgtUntId().equals(baseUnitId));
boolean baseToSrc = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(baseUnitId) && c.getTgtUntId().equals(srcUnitId));
// 检查 基准 → 目标(正向或反向)
boolean baseToTgt = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(baseUnitId) && c.getTgtUntId().equals(tgtUnitId));
boolean tgtToBase = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(tgtUnitId) && c.getTgtUntId().equals(baseUnitId));
return (srcToBase || baseToSrc) && (baseToTgt || tgtToBase);
}
/**
* 获取转换策略
*/
private String getConversionStrategy(Long srcUnitId, Long tgtUnitId,
List<UnitConversionDO> conversions,
Long baseUnitId) {
// 检查直接转换
boolean hasDirect = conversions.stream()
.anyMatch(c -> c.getSrcUntId().equals(srcUnitId) && c.getTgtUntId().equals(tgtUnitId));
if (hasDirect) {
return "DIRECT";
}
return "VIA_BASE_UNIT";
}
private UnitConversionValidationRespVO buildEmptyValidationResult(Long quantityId, UnitQuantityDO quantity) {
return UnitConversionValidationRespVO.builder()
.quantityId(quantityId)
.quantityName(quantity.getName())
.quantitySymbol(quantity.getSymbol())
.totalUnits(0)
.convertibleUnits(0)
.unconvertibleUnits(0)
.allConvertible(true)
.baseUnit(null)
.unconvertibleUnitList(new ArrayList<>())
.conversionPaths(new ArrayList<>())
.build();
}
}

View File

@@ -59,4 +59,11 @@ public interface UnitQuantityService {
*/
PageResult<UnitQuantityDO> getUnitQuantityPage(UnitQuantityPageReqVO pageReqVO);
/**
* 获取量纲及单位树
*
* @return 量纲及单位树列表
*/
List<UnitQuantityTreeRespVO> getUnitQuantityTree();
}

View File

@@ -14,6 +14,11 @@ import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.unitmanagement.dal.dao.UnitQuantity.UnitQuantityMapper;
import com.zt.plat.module.unitmanagement.dal.dao.QuantityUnitRelation.QuantityUnitRelationMapper;
import com.zt.plat.module.unitmanagement.dal.dao.UntInfo.UntInfoMapper;
import com.zt.plat.module.unitmanagement.dal.dataobject.QuantityUnitRelation.QuantityUnitRelationDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.UntInfo.UntInfoDO;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList;
@@ -32,6 +37,12 @@ public class UnitQuantityServiceImpl implements UnitQuantityService {
@Resource
private UnitQuantityMapper unitQuantityMapper;
@Resource
private QuantityUnitRelationMapper quantityUnitRelationMapper;
@Resource
private UntInfoMapper untInfoMapper;
@Override
public UnitQuantityRespVO createUnitQuantity(UnitQuantitySaveReqVO createReqVO) {
// 插入
@@ -89,4 +100,50 @@ public class UnitQuantityServiceImpl implements UnitQuantityService {
return unitQuantityMapper.selectPage(pageReqVO);
}
@Override
public List<UnitQuantityTreeRespVO> getUnitQuantityTree() {
// 1. 查询所有量纲
List<UnitQuantityDO> quantities = unitQuantityMapper.selectList();
// 2. 查询所有关联关系
List<QuantityUnitRelationDO> relations = quantityUnitRelationMapper.selectList();
// 3. 查询所有单位
List<UntInfoDO> units = untInfoMapper.selectList();
Map<Long, UntInfoDO> unitMap = units.stream()
.collect(Collectors.toMap(UntInfoDO::getId, u -> u));
// 4. 组装树形结构
return quantities.stream().map(quantity -> {
UnitQuantityTreeRespVO treeVO = new UnitQuantityTreeRespVO();
treeVO.setId(quantity.getId());
treeVO.setName(quantity.getName());
treeVO.setSymbol(quantity.getSymbol());
treeVO.setDsp(quantity.getDsp());
// 获取该量纲下的所有单位
List<UnitQuantityTreeRespVO.UnitItemVO> unitItems = relations.stream()
.filter(r -> r.getUntQtyId().equals(quantity.getId()))
.map(r -> {
UnitQuantityTreeRespVO.UnitItemVO unitItem = new UnitQuantityTreeRespVO.UnitItemVO();
unitItem.setRelationId(r.getId());
unitItem.setId(r.getUntId());
unitItem.setIsBse(r.getIsBse());
// 填充单位信息
UntInfoDO unit = unitMap.get(r.getUntId());
if (unit != null) {
unitItem.setName(unit.getName());
unitItem.setSmb(unit.getSmb());
}
return unitItem;
})
.collect(Collectors.toList());
treeVO.setUnits(unitItems);
return treeVO;
}).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,407 @@
package com.zt.plat.module.unitmanagement.util;
import com.zt.plat.module.unitmanagement.dal.dataobject.UnitConversion.UnitConversionDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.QuantityUnitRelation.QuantityUnitRelationDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.UntInfo.UntInfoDO;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.unitmanagement.enums.UnitErrorCodeConstants.*;
/**
* 单位转换工具类
*
* <p>支持三种转换策略:
* <ul>
* <li>直接转换:使用预定义的转换规则</li>
* <li>基准单位转换:通过基准单位进行中转</li>
* <li>公式转换:使用自定义转换公式</li>
* </ul>
*
* @author 系统
*/
@Slf4j
public class UnitConversionUtil {
/**
* 转换策略枚举
*/
public enum ConversionStrategy {
/** 直接转换 */
DIRECT,
/** 基准单位转换 */
VIA_BASE_UNIT,
/** 公式转换 */
FORMULA,
/** 无法转换 */
NONE
}
/**
* 转换结果
*/
public static class ConversionResult {
private final BigDecimal value;
private final BigDecimal factor;
private final ConversionStrategy strategy;
private final String formula;
public ConversionResult(BigDecimal value, BigDecimal factor, ConversionStrategy strategy, String formula) {
this.value = value;
this.factor = factor;
this.strategy = strategy;
this.formula = formula;
}
public BigDecimal getValue() {
return value;
}
public BigDecimal getFactor() {
return factor;
}
public ConversionStrategy getStrategy() {
return strategy;
}
public String getFormula() {
return formula;
}
}
/**
* 单位转换
*
* @param value 待转换的值
* @param srcUntId 源单位ID
* @param tgtUntId 目标单位ID
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param precision 精度(小数位数)
* @return 转换结果
*/
public static ConversionResult convert(
BigDecimal value,
Long srcUntId,
Long tgtUntId,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
int precision) {
// 1. 如果源单位和目标单位相同,直接返回
if (srcUntId.equals(tgtUntId)) {
return new ConversionResult(value, BigDecimal.ONE, ConversionStrategy.DIRECT, "相同单位,无需转换");
}
// 2. 尝试直接转换
ConversionResult directResult = tryDirectConversion(value, srcUntId, tgtUntId, conversions, precision);
if (directResult != null) {
return directResult;
}
// 3. 尝试通过基准单位转换
ConversionResult viaBaseResult = tryViaBaseUnitConversion(value, srcUntId, tgtUntId, conversions, relations, precision);
if (viaBaseResult != null) {
return viaBaseResult;
}
// 4. 无法转换 - 抛出业务异常
log.warn("无法找到从单位 {} 到单位 {} 的转换路径", srcUntId, tgtUntId);
throw exception(UNIT_CONVERSION_PATH_NOT_FOUND,
"ID:" + srcUntId, "ID:" + tgtUntId);
}
/**
* 尝试直接转换(支持自动反向推导)
*/
private static ConversionResult tryDirectConversion(
BigDecimal value,
Long srcUntId,
Long tgtUntId,
List<UnitConversionDO> conversions,
int precision) {
// 1. 查找正向转换规则:源 → 目标
UnitConversionDO forwardConversion = conversions.stream()
.filter(c -> c.getSrcUntId().equals(srcUntId) && c.getTgtUntId().equals(tgtUntId))
.findFirst()
.orElse(null);
if (forwardConversion != null) {
BigDecimal factor = forwardConversion.getFctr();
BigDecimal result = value.multiply(factor).setScale(precision, RoundingMode.HALF_UP);
String formula = forwardConversion.getFmu() != null ? forwardConversion.getFmu() :
String.format("value * %s", factor);
log.debug("直接转换(正向): {} (单位{}) * {} = {} (单位{})", value, srcUntId, factor, result, tgtUntId);
return new ConversionResult(result, factor, ConversionStrategy.DIRECT, formula);
}
// 2. 查找反向转换规则:目标 → 源,然后取倒数
UnitConversionDO reverseConversion = conversions.stream()
.filter(c -> c.getSrcUntId().equals(tgtUntId) && c.getTgtUntId().equals(srcUntId))
.findFirst()
.orElse(null);
if (reverseConversion != null) {
// 反向系数:如果 目标→源 的系数是 f则 源→目标 的系数是 1/f
BigDecimal reverseFactor = reverseConversion.getFctr();
BigDecimal factor = BigDecimal.ONE.divide(reverseFactor, Math.max(precision + 10, 20), RoundingMode.HALF_UP);
BigDecimal result = value.multiply(factor).setScale(precision, RoundingMode.HALF_UP);
String formula = String.format("value / %s (反向推导)", reverseFactor);
log.debug("直接转换(反向推导): {} (单位{}) / {} = {} (单位{})", value, srcUntId, reverseFactor, result, tgtUntId);
return new ConversionResult(result, factor, ConversionStrategy.DIRECT, formula);
}
return null;
}
/**
* 尝试通过基准单位转换
*/
private static ConversionResult tryViaBaseUnitConversion(
BigDecimal value,
Long srcUntId,
Long tgtUntId,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
int precision) {
// 1. 找到源单位和目标单位的量纲
QuantityUnitRelationDO srcRelation = relations.stream()
.filter(r -> r.getUntId().equals(srcUntId))
.findFirst()
.orElse(null);
QuantityUnitRelationDO tgtRelation = relations.stream()
.filter(r -> r.getUntId().equals(tgtUntId))
.findFirst()
.orElse(null);
if (srcRelation == null || tgtRelation == null) {
log.warn("找不到单位 {} 或 {} 的量纲关联关系", srcUntId, tgtUntId);
return null;
}
// 2. 检查是否属于同一量纲
if (!srcRelation.getUntQtyId().equals(tgtRelation.getUntQtyId())) {
log.warn("单位 {} 和 {} 不属于同一量纲", srcUntId, tgtUntId);
return null;
}
// 3. 找到基准单位
Long quantityId = srcRelation.getUntQtyId();
QuantityUnitRelationDO baseRelation = relations.stream()
.filter(r -> r.getUntQtyId().equals(quantityId) && r.getIsBse() == 1)
.findFirst()
.orElse(null);
if (baseRelation == null) {
log.warn("量纲 {} 没有设置基准单位", quantityId);
return null;
}
Long baseUnitId = baseRelation.getUntId();
// 4. 源单位 → 基准单位
BigDecimal toBaseValue;
BigDecimal toBaseFactor;
if (srcUntId.equals(baseUnitId)) {
toBaseValue = value;
toBaseFactor = BigDecimal.ONE;
} else {
ConversionResult toBaseResult = tryDirectConversion(value, srcUntId, baseUnitId, conversions, precision);
if (toBaseResult == null) {
log.warn("无法从源单位 {} 转换到基准单位 {}", srcUntId, baseUnitId);
return null;
}
toBaseValue = toBaseResult.getValue();
toBaseFactor = toBaseResult.getFactor();
}
// 5. 基准单位 → 目标单位
BigDecimal finalValue;
BigDecimal fromBaseFactor;
if (tgtUntId.equals(baseUnitId)) {
finalValue = toBaseValue;
fromBaseFactor = BigDecimal.ONE;
} else {
ConversionResult fromBaseResult = tryDirectConversion(toBaseValue, baseUnitId, tgtUntId, conversions, precision);
if (fromBaseResult == null) {
log.warn("无法从基准单位 {} 转换到目标单位 {}", baseUnitId, tgtUntId);
return null;
}
finalValue = fromBaseResult.getValue();
fromBaseFactor = fromBaseResult.getFactor();
}
// 6. 计算总转换因子
BigDecimal totalFactor = toBaseFactor.multiply(fromBaseFactor);
String formula = String.format("通过基准单位(ID:%d): value * %s * %s", baseUnitId, toBaseFactor, fromBaseFactor);
log.debug("基准单位转换: {} (单位{}) → {} (基准{}) → {} (单位{})",
value, srcUntId, toBaseValue, baseUnitId, finalValue, tgtUntId);
return new ConversionResult(finalValue, totalFactor, ConversionStrategy.VIA_BASE_UNIT, formula);
}
/**
* 批量转换
*
* @param values 待转换的值列表
* @param srcUntId 源单位ID
* @param tgtUntId 目标单位ID
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param precision 精度(小数位数)
* @return 转换结果列表
*/
public static List<ConversionResult> batchConvert(
List<BigDecimal> values,
Long srcUntId,
Long tgtUntId,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
int precision) {
return values.stream()
.map(value -> convert(value, srcUntId, tgtUntId, conversions, relations, precision))
.collect(Collectors.toList());
}
/**
* 按单位符号转换(推荐使用)
*
* @param value 待转换的值
* @param srcUnitSymbol 源单位符号(如: "kg", "m", "km"
* @param tgtUnitSymbol 目标单位符号
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param units 单位信息列表
* @param precision 精度(小数位数)
* @return 转换结果
*/
public static ConversionResult convertBySymbol(
BigDecimal value,
String srcUnitSymbol,
String tgtUnitSymbol,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
List<UntInfoDO> units,
int precision) {
// 1. 根据符号查找单位
UntInfoDO srcUnit = units.stream()
.filter(u -> u.getSmb() != null && u.getSmb().equals(srcUnitSymbol))
.findFirst()
.orElseThrow(() -> exception(UNIT_NOT_FOUND, "符号:" + srcUnitSymbol));
UntInfoDO tgtUnit = units.stream()
.filter(u -> u.getSmb() != null && u.getSmb().equals(tgtUnitSymbol))
.findFirst()
.orElseThrow(() -> exception(UNIT_NOT_FOUND, "符号:" + tgtUnitSymbol));
log.debug("按符号转换: {} ({}) → {} ({})", srcUnitSymbol, srcUnit.getId(), tgtUnitSymbol, tgtUnit.getId());
// 2. 调用原有方法
return convert(value, srcUnit.getId(), tgtUnit.getId(), conversions, relations, precision);
}
/**
* 按单位名称转换
*
* @param value 待转换的值
* @param srcUnitName 源单位名称(如: "千克", "米", "千米"
* @param tgtUnitName 目标单位名称
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param units 单位信息列表
* @param precision 精度(小数位数)
* @return 转换结果
*/
public static ConversionResult convertByName(
BigDecimal value,
String srcUnitName,
String tgtUnitName,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
List<UntInfoDO> units,
int precision) {
// 1. 根据名称查找单位
UntInfoDO srcUnit = units.stream()
.filter(u -> u.getName() != null && u.getName().equals(srcUnitName))
.findFirst()
.orElseThrow(() -> exception(UNIT_NOT_FOUND, "名称:" + srcUnitName));
UntInfoDO tgtUnit = units.stream()
.filter(u -> u.getName() != null && u.getName().equals(tgtUnitName))
.findFirst()
.orElseThrow(() -> exception(UNIT_NOT_FOUND, "名称:" + tgtUnitName));
log.debug("按名称转换: {} ({}) → {} ({})", srcUnitName, srcUnit.getId(), tgtUnitName, tgtUnit.getId());
// 2. 调用原有方法
return convert(value, srcUnit.getId(), tgtUnit.getId(), conversions, relations, precision);
}
/**
* 按单位符号批量转换
*
* @param values 待转换的值列表
* @param srcUnitSymbol 源单位符号
* @param tgtUnitSymbol 目标单位符号
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param units 单位信息列表
* @param precision 精度(小数位数)
* @return 转换结果列表
*/
public static List<ConversionResult> batchConvertBySymbol(
List<BigDecimal> values,
String srcUnitSymbol,
String tgtUnitSymbol,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
List<UntInfoDO> units,
int precision) {
return values.stream()
.map(value -> convertBySymbol(value, srcUnitSymbol, tgtUnitSymbol, conversions, relations, units, precision))
.collect(Collectors.toList());
}
/**
* 按单位名称批量转换
*
* @param values 待转换的值列表
* @param srcUnitName 源单位名称
* @param tgtUnitName 目标单位名称
* @param conversions 转换规则列表
* @param relations 量纲-单位关联关系列表
* @param units 单位信息列表
* @param precision 精度(小数位数)
* @return 转换结果列表
*/
public static List<ConversionResult> batchConvertByName(
List<BigDecimal> values,
String srcUnitName,
String tgtUnitName,
List<UnitConversionDO> conversions,
List<QuantityUnitRelationDO> relations,
List<UntInfoDO> units,
int precision) {
return values.stream()
.map(value -> convertByName(value, srcUnitName, tgtUnitName, conversions, relations, units, precision))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,264 @@
package com.zt.plat.module.unitmanagement.util;
import com.zt.plat.module.unitmanagement.dal.dataobject.UnitConversion.UnitConversionDO;
import com.zt.plat.module.unitmanagement.dal.dataobject.QuantityUnitRelation.QuantityUnitRelationDO;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* UnitConversionUtil 工具类测试
*
* 测试内容:
* 1. 相同单位转换
* 2. 直接转换策略
* 3. 基准单位转换策略
* 4. 精度控制
* 5. 批量转换
* 6. 错误场景
*/
class UnitConversionUtilTest {
/**
* 准备测试数据
*/
private TestData prepareTestData() {
TestData data = new TestData();
// 量纲ID
Long lengthQuantityId = 1L;
Long weightQuantityId = 2L;
// 单位ID
Long meterId = 101L; // 米(基准)
Long kilometerId = 102L; // 千米
Long centimeterId = 103L; // 厘米
Long millimeterId = 104L; // 毫米
Long kilogramId = 201L; // 千克(基准)
Long tonId = 202L; // 吨
// 构建量纲-单位关联关系
data.relations = new ArrayList<>();
data.relations.add(createRelation(1L, lengthQuantityId, meterId, 1)); // 米是基准
data.relations.add(createRelation(2L, lengthQuantityId, kilometerId, 0));
data.relations.add(createRelation(3L, lengthQuantityId, centimeterId, 0));
data.relations.add(createRelation(4L, lengthQuantityId, millimeterId, 0));
data.relations.add(createRelation(5L, weightQuantityId, kilogramId, 1)); // 千克是基准
data.relations.add(createRelation(6L, weightQuantityId, tonId, 0));
// 构建转换规则(只配置到基准单位的转换)
data.conversions = new ArrayList<>();
// 千米 <-> 米
data.conversions.add(createConversion(1L, kilometerId, meterId, new BigDecimal("1000"), "1km = 1000m"));
data.conversions.add(createConversion(2L, meterId, kilometerId, new BigDecimal("0.001"), "1m = 0.001km"));
// 厘米 <-> 米
data.conversions.add(createConversion(3L, centimeterId, meterId, new BigDecimal("0.01"), "1cm = 0.01m"));
data.conversions.add(createConversion(4L, meterId, centimeterId, new BigDecimal("100"), "1m = 100cm"));
// 毫米 <-> 米
data.conversions.add(createConversion(5L, millimeterId, meterId, new BigDecimal("0.001"), "1mm = 0.001m"));
data.conversions.add(createConversion(6L, meterId, millimeterId, new BigDecimal("1000"), "1m = 1000mm"));
// 吨 <-> 千克
data.conversions.add(createConversion(7L, tonId, kilogramId, new BigDecimal("1000"), "1t = 1000kg"));
data.conversions.add(createConversion(8L, kilogramId, tonId, new BigDecimal("0.001"), "1kg = 0.001t"));
// 保存单位ID供测试使用
data.meterId = meterId;
data.kilometerId = kilometerId;
data.centimeterId = centimeterId;
data.millimeterId = millimeterId;
data.kilogramId = kilogramId;
data.tonId = tonId;
return data;
}
private QuantityUnitRelationDO createRelation(Long id, Long quantityId, Long unitId, Integer isBase) {
QuantityUnitRelationDO relation = new QuantityUnitRelationDO();
relation.setId(id);
relation.setUntQtyId(quantityId);
relation.setUntId(unitId);
relation.setIsBse(isBase);
return relation;
}
private UnitConversionDO createConversion(Long id, Long srcId, Long tgtId, BigDecimal factor, String formula) {
UnitConversionDO conversion = new UnitConversionDO();
conversion.setId(id);
conversion.setSrcUntId(srcId);
conversion.setTgtUntId(tgtId);
conversion.setFctr(factor);
conversion.setFmu(formula);
return conversion;
}
@Test
@DisplayName("测试1: 相同单位转换")
void testSameUnitConversion() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("100");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.meterId, data.meterId,
data.conversions, data.relations, 6
);
assertEquals(value, result.getValue(), "相同单位转换,值应该不变");
assertEquals(BigDecimal.ONE, result.getFactor(), "相同单位转换因子应该为1");
assertEquals(UnitConversionUtil.ConversionStrategy.DIRECT, result.getStrategy(), "相同单位应使用DIRECT策略");
}
@Test
@DisplayName("测试2: 直接转换策略 - 千米到米")
void testDirectConversion_KmToM() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("5.5");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.kilometerId, data.meterId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("5500.000000"), result.getValue(), "5.5km应该等于5500m");
assertEquals(new BigDecimal("1000"), result.getFactor(), "千米到米的因子应该是1000");
assertEquals(UnitConversionUtil.ConversionStrategy.DIRECT, result.getStrategy(), "应使用DIRECT策略");
}
@Test
@DisplayName("测试3: 直接转换策略 - 米到千米")
void testDirectConversion_MToKm() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("5000");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.meterId, data.kilometerId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("5.000000"), result.getValue(), "5000m应该等于5km");
assertEquals(new BigDecimal("0.001"), result.getFactor(), "米到千米的因子应该是0.001");
assertEquals(UnitConversionUtil.ConversionStrategy.DIRECT, result.getStrategy(), "应使用DIRECT策略");
}
@Test
@DisplayName("测试4: 基准单位转换策略 - 厘米到千米")
void testViaBaseUnitConversion_CmToKm() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("100000");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.centimeterId, data.kilometerId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("1.000000"), result.getValue(), "100000cm应该等于1km");
assertEquals(UnitConversionUtil.ConversionStrategy.VIA_BASE_UNIT, result.getStrategy(),
"厘米到千米应使用VIA_BASE_UNIT策略");
assertTrue(result.getFormula().contains("基准单位"), "公式应该提到基准单位");
}
@Test
@DisplayName("测试5: 基准单位转换策略 - 毫米到千米")
void testViaBaseUnitConversion_MmToKm() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("1000000");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.millimeterId, data.kilometerId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("1.000000"), result.getValue(), "1000000mm应该等于1km");
assertEquals(UnitConversionUtil.ConversionStrategy.VIA_BASE_UNIT, result.getStrategy(),
"毫米到千米应使用VIA_BASE_UNIT策略");
}
@Test
@DisplayName("测试6: 精度控制 - 2位小数")
void testPrecisionControl_2Digits() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("1.23456789");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.kilometerId, data.meterId,
data.conversions, data.relations, 2
);
assertEquals(new BigDecimal("1234.57"), result.getValue(), "精度应该控制在2位小数");
}
@Test
@DisplayName("测试7: 精度控制 - 6位小数")
void testPrecisionControl_6Digits() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("1.23456789");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.kilometerId, data.meterId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("1234.567890"), result.getValue(), "精度应该控制在6位小数");
}
@Test
@DisplayName("测试8: 批量转换")
void testBatchConversion() {
TestData data = prepareTestData();
List<BigDecimal> values = new ArrayList<>();
values.add(new BigDecimal("1"));
values.add(new BigDecimal("2"));
values.add(new BigDecimal("3"));
List<UnitConversionUtil.ConversionResult> results = UnitConversionUtil.batchConvert(
values, data.kilometerId, data.meterId,
data.conversions, data.relations, 6
);
assertEquals(3, results.size(), "应该返回3个转换结果");
assertEquals(new BigDecimal("1000.000000"), results.get(0).getValue(), "1km = 1000m");
assertEquals(new BigDecimal("2000.000000"), results.get(1).getValue(), "2km = 2000m");
assertEquals(new BigDecimal("3000.000000"), results.get(2).getValue(), "3km = 3000m");
}
@Test
@DisplayName("测试10: 重量单位转换 - 千克到吨")
void testWeightConversion_KgToTon() {
TestData data = prepareTestData();
BigDecimal value = new BigDecimal("1000");
UnitConversionUtil.ConversionResult result = UnitConversionUtil.convert(
value, data.kilogramId, data.tonId,
data.conversions, data.relations, 6
);
assertEquals(new BigDecimal("1.000000"), result.getValue(), "1000kg应该等于1t");
assertEquals(new BigDecimal("0.001"), result.getFactor(), "千克到吨的因子应该是0.001");
assertEquals(UnitConversionUtil.ConversionStrategy.DIRECT, result.getStrategy());
}
/**
* 测试数据容器
*/
static class TestData {
List<UnitConversionDO> conversions;
List<QuantityUnitRelationDO> relations;
Long meterId;
Long kilometerId;
Long centimeterId;
Long millimeterId;
Long kilogramId;
Long tonId;
}
}