商品单位转换及计量单位换算
进销存系统开发时,商品货物流转会遇到商品计量单位及计量单位的换算,如:输入货物700吨,7000千克。一个厂商的产品一般不大可能既有重量单位又有体积单位,一个电子商务平台,货物来自多个厂商,种类就繁多了。如果平台国际化了,甚至是国家的都会出自己的标准。如何在一个系统中可以方便地兼容各种计量单位呢?这也是在设计之初必须考虑到的事情,会对后面的开发、维护产生重大的影响,不容忽视。
系统开发单位换算的实现的几种方案:
1、个人维护换算标准,缺点:不利于团队开发,如出现修改,维护量就大了。
2、公共类或公共枚举维护换算标准,统一维护换算标准管理,将多人的工作量变成了一个人。缺点:后期系统做大国际化了,需要加入其他的换算标准,就需要修改,不够灵活。
3、数据库中约定规则,这种方案,增加灵活性、可扩展性,甚至添加计量单位和计量单位的换算都不用修改代码,直接在后台添加即可,单位表用于记录全部可能使用的计量单位。
数据库中约定规则,设计表:
随时可以添加计量单位和计量单位之间的换算因子,可以满足大部分需求。
Java单位换算功能
商品的库存单位需求
同一个商品一般情况下有4个级别的单位:一级单位、二级单位、三级单位、四级单位,由小到大为:一级单位 < 二级单位 < 三级单位 < 四级单位。根据上表,单位转换值:上级转下级,下级 = 数量*上级的单位转换值,单位兑换值:下级转上级,从最高级自上而下进行折算。
匹配的模型类建立
package com.what21.app.service.unit;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class Unit {
// ID
private Long id;
// 商品的ID
private Long goodsId;
// 单位名称
private String unitName;
// 单位类型:1、一级单位,2、二级单位,3、三级单位 4、四级单位
private Integer unitType;
// 是否基础,同级单位可以有多个, 是否基础 0 否 1是
private Integer unitBasics;
// 单位转换值
private Integer unitValue;
// 单位兑换值
private BigDecimal unitExchangeRate;
// 单位规格
private String unitSpecs;
}
准备商品的单位
package com.what21.app.service.unit;
import java.math.BigDecimal;
import java.util.*;
public class UnitConvertService {
/**
* @param goodsId
* @return
*/
public static List<Unit> getUnitList(Long goodsId) {
List<Unit> unitList = new ArrayList<>();
// 一级单位
Unit unit1 = new Unit();
unit1.setId(1L);
unit1.setGoodsId(goodsId);
unit1.setUnitType(1);
unit1.setUnitName("克");
unit1.setUnitBasics(1);
unit1.setUnitValue(1);
unit1.setUnitExchangeRate(new BigDecimal(1));
unit1.setUnitSpecs("1g/克");
unitList.add(unit1);
// 二级单位
Unit unit2 = new Unit();
unit2.setId(2L);
unit2.setGoodsId(goodsId);
unit2.setUnitType(2);
unit2.setUnitName("包");
unit2.setUnitBasics(1);
unit2.setUnitValue(40);
unit2.setUnitExchangeRate(new BigDecimal(40));
unit2.setUnitSpecs("40克/包");
unitList.add(unit2);
// 三级单位
Unit unit3 = new Unit();
unit3.setId(3L);
unit3.setGoodsId(goodsId);
unit3.setUnitType(3);
unit3.setUnitName("盒");
unit3.setUnitBasics(1);
unit3.setUnitValue(10);
unit3.setUnitExchangeRate(new BigDecimal(400));
unit3.setUnitSpecs("(40g×10包)/盒");
unitList.add(unit3);
// 四级单位
Unit unit4 = new Unit();
unit4.setId(4L);
unit4.setGoodsId(goodsId);
unit4.setUnitType(4);
unit4.setUnitName("箱");
unit4.setUnitBasics(1);
unit4.setUnitValue(20);
unit4.setUnitExchangeRate(new BigDecimal(8000));
unit4.setUnitSpecs("(40g×10包)/盒");
unitList.add(unit4);
return unitList;
}
}
单位向下级/上级单位转换案例
package com.what21.app.service.unit;
import java.util.LinkedHashMap;
import java.util.List;
public class UnitConvertDemo {
public static void main(String[] args) {
// 准备商品
Long goodsId = 1000L;
// 准备商品单位列表
List<Unit> unitList = UnitConvertService.getUnitList(goodsId);
// 准备商品单位分组(根据类型)
LinkedHashMap<Integer, Unit> unitMap = UnitConvertTools.toUnitMapWithType(goodsId, unitList);
System.out.println("unitMap = " + unitMap);
// 向下级单位转,数量乘以自身属性UnitValue
int fourLevelNum = 1;
Unit unit4 = unitMap.get(4);
int threeLevelNum = fourLevelNum * unit4.getUnitValue();
Unit unit3 = unitMap.get(3);
int twoLevelNum = threeLevelNum * unit3.getUnitValue();
Unit unit2 = unitMap.get(2);
int oneLevelNum = twoLevelNum * unit2.getUnitValue();
Unit unit1 = unitMap.get(1);
System.out.print(fourLevelNum + unit4.getUnitName() + "======" + threeLevelNum + unit3.getUnitName() + "======");
System.out.print(twoLevelNum + unit2.getUnitName() + "======" + oneLevelNum + unit1.getUnitName());
System.out.println(); // 1箱======20盒======200包======8000克
// 向上级单位转,数量除以上级属性UnitValue
oneLevelNum = 8000;
twoLevelNum = oneLevelNum / unit2.getUnitValue();
threeLevelNum = twoLevelNum / unit3.getUnitValue();
fourLevelNum = threeLevelNum / unit4.getUnitValue();
System.out.print(oneLevelNum + unit1.getUnitName() + "======" + twoLevelNum + unit2.getUnitName() + "======");
System.out.print(threeLevelNum + unit3.getUnitName() + "======" + fourLevelNum + unit4.getUnitName());
System.out.println(); // 8000克======200包======20盒======1箱
}
}
常用的工具类实现
实现功能:1、按单位类型分组商品单位;2、计算每个商品单位对应的单位兑换值,按商品单位类型分组返回;3、上级单位类型及上级单位数量转换为最基本的单位的数量;4、上级单位类型及上级单位数量转换为指定的单位的数量;5、商品单位最小单位数量估算/折算成大单位(上级单位),按商品单位类型分组返回Map;6、商品单位最小单位数量估算/折算成大单位(上级单位),返回显示文本。
package com.what21.app.service.unit;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class UnitConvertTools {
/**
* 按类型分组返回保证顺序的Map(根据单位类型)
*
* @param goodsId
* @param unitList
* @return
*/
public static LinkedHashMap<Integer, Unit> toUnitMapWithType(Long goodsId, List<Unit> unitList) {
LinkedHashMap<Integer, Unit> unitMap = new LinkedHashMap<>();
for (Unit unit : unitList) {
if (goodsId == unit.getGoodsId()) {
unitMap.put(unit.getUnitType(), unit);
}
}
return unitMap;
}
/**
* 计算每个单位类型对应的的兑换率(根据单位类型)
*
* @param unitGroupMap
* @return
*/
public static LinkedHashMap<Integer, BigDecimal> toExchangeRateMapWithType(LinkedHashMap<Integer, Unit> unitGroupMap) {
LinkedHashMap<Integer, BigDecimal> exchangeRateMap = new LinkedHashMap<>();
for (int unitType = unitGroupMap.size(); unitType >= 1; unitType--) {
BigDecimal exchangeRate = new BigDecimal(1);
for (int toUnitType = unitType - 1; toUnitType >= 1; toUnitType--) {
Unit toUnit = unitGroupMap.get(toUnitType + 1);
exchangeRate = exchangeRate.multiply(new BigDecimal(toUnit.getUnitValue()));
}
exchangeRateMap.put(unitType, exchangeRate);
}
return exchangeRateMap;
}
/**
* 根据上级单位类型及单位数量转换为最基本的单位
*
* @param goodsId
* @param unitList
* @param fromType
* @param quantity
* @return
*/
public static Integer toUnitStandardQuantity(Long goodsId, List<Unit> unitList, int fromType, int quantity) {
int standardQuantity = 0;
LinkedHashMap<Integer, Unit> unitGroupMap = toUnitMapWithType(goodsId, unitList);
for (int unitType = unitGroupMap.size(); unitType >= 1; unitType--) {
if (unitType == fromType) {
if (fromType == 1) {
standardQuantity = quantity;
} else {
Unit unit = unitGroupMap.get(unitType);
for (int toUnitType = unitType - 1; toUnitType >= 1; toUnitType--) {
if (standardQuantity == 0) {
standardQuantity = unit.getUnitValue() * quantity;
} else {
standardQuantity = standardQuantity * unit.getUnitValue() * quantity;
}
}
}
break;
}
}
return standardQuantity;
}
/**
* 根据上级单位类型及单位数量转换为最指定的单位
*
* @param goodsId
* @param unitList
* @param fromType
* @param quantity
* @param toType
* @return
*/
public static Integer toUnitStandardQuantity(Long goodsId, List<Unit> unitList, int fromType, int quantity, int toType) {
int standardQuantity = 0;
LinkedHashMap<Integer, Unit> unitGroupMap = toUnitMapWithType(goodsId, unitList);
for (int unitType = unitGroupMap.size(); unitType >= 1; unitType--) {
if (unitType == fromType) {
if (fromType == 1) {
standardQuantity = quantity;
} else {
for (int toUnitType = unitType; toUnitType >= 1; toUnitType--) {
Unit unit = unitGroupMap.get(toUnitType);
if (unit.getUnitType() == toType) {
break;
}
if (standardQuantity == 0) {
standardQuantity = unit.getUnitValue() * quantity;
} else {
standardQuantity = standardQuantity * unit.getUnitValue() * quantity;
}
}
}
break;
}
}
return standardQuantity;
}
/**
* 最小单位【估算/折算】大单位,按类型分组返回Map
*
* @param goodsId
* @param unitList
* @param quantity
* @return
*/
public static LinkedHashMap<Integer, Integer> toUnitEquivalentQuantityWithType(Long goodsId, List<Unit> unitList, int quantity) {
LinkedHashMap<Integer, Integer> unitEquivalentQuantity = new LinkedHashMap<>();
LinkedHashMap<Integer, Unit> unitGroupMap = toUnitMapWithType(goodsId, unitList);
Map<Integer, BigDecimal> exchangeRateMap = UnitConvertTools.toExchangeRateMapWithType(unitGroupMap);
for (int unitType = unitGroupMap.size(); unitType >= 1; unitType--) {
unitEquivalentQuantity.put(unitType, 0);
BigDecimal exchangeRate = exchangeRateMap.get(unitType);
if ((quantity % exchangeRate.intValue()) == 0) {
int number = (quantity / exchangeRate.intValue());
if (number > 0) {
unitEquivalentQuantity.put(unitType, number);
}
break;
} else {
int number = (quantity / exchangeRate.intValue());
if (number > 0) {
unitEquivalentQuantity.put(unitType, number);
}
quantity = quantity % exchangeRate.intValue();
}
}
return unitEquivalentQuantity;
}
/**
* 最小单位【估算/折算】大单位,返回文本
*
* @param goodsId====>商品的ID
* @param unitList====>商品的单位列表
* @param quantity====>最小单位的数量
* @return
*/
public static String toUnitEquivalentQuantityText(Long goodsId, List<Unit> unitList, int quantity) {
LinkedHashMap<Integer, Unit> unitGroupMap = toUnitMapWithType(goodsId, unitList);
Map<Integer, BigDecimal> exchangeRateMap = UnitConvertTools.toExchangeRateMapWithType(unitGroupMap);
StringBuilder stringBuilder = new StringBuilder();
for (int unitType = unitGroupMap.size(); unitType >= 1; unitType--) {
Unit computeUnit = unitGroupMap.get(unitType);
BigDecimal exchangeRate = exchangeRateMap.get(unitType);
if ((quantity % exchangeRate.intValue()) == 0) {
int number = (quantity / exchangeRate.intValue());
if (number > 0) {
stringBuilder.append((quantity / exchangeRate.intValue())).append(computeUnit.getUnitName());
}
break;
} else {
int number = (quantity / exchangeRate.intValue());
if (number > 0) {
stringBuilder.append(number).append(computeUnit.getUnitName());
}
quantity = quantity % exchangeRate.intValue();
}
}
return stringBuilder.toString();
}
}
常用的工具类实现测试
package com.what21.app.service.unit;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class UnitConvertToolsTest {
/**
* @param args
*/
public static void main(String[] args) {
// 准备商品
Long goodsId = 1000L;
// 准备商品单位列表
List<Unit> unitList = UnitConvertService.getUnitList(goodsId);
// =======================================================================================//
// >>>>>>>>>>>> 按类型分组返回保证顺序的Map(根据单位类型)
LinkedHashMap<Integer, Unit> unitGroupMap = UnitConvertTools.toUnitMapWithType(goodsId, unitList);
System.out.println("按类型分组返回保证顺序的Map(根据单位类型) = " + unitGroupMap);
// =======================================================================================//
// >>>>>>>>>>>> 计算每个单位类型对应的的兑换率(根据单位类型)
Map<Integer, BigDecimal> exchangeRateMap = UnitConvertTools.toExchangeRateMapWithType(unitGroupMap);
System.out.println("计算每个单位类型对应的的兑换率(根据单位类型)= " + exchangeRateMap);
// {4=8000, 3=400, 2=40, 1=1}
// =======================================================================================//
// >>>>>>>>>>>> 根据上级单位类型及单位数量转换为最基本的单位
int fourLevelQuantity = 1;
int fourLevelTotal = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 4, fourLevelQuantity);
System.out.println("四级单位转一级单位,1" + unitGroupMap.get(4).getUnitName() + "=" + fourLevelTotal + unitGroupMap.get(1).getUnitName());
// 四级单位转一级单位,1箱=8000克
int threeLevelQuantity = 1;
int threeLevelTotal = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 3, threeLevelQuantity);
System.out.println("三级单位转一级单位,1" + unitGroupMap.get(3).getUnitName() + "=" + threeLevelTotal + unitGroupMap.get(1).getUnitName());
// 三级单位转一级单位,1盒=100克
int twoLevelQuantity = 1;
int twoLevelTotal = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 2, twoLevelQuantity);
System.out.println("二级单位转一级单位,1" + unitGroupMap.get(2).getUnitName() + "=" + twoLevelTotal + unitGroupMap.get(1).getUnitName());
// 二级单位转一级单位,1包=40克
int oneLevelQuantity = 1;
int oneLevelTotal = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 1, oneLevelQuantity);
System.out.println("一级单位转一级单位,1" + unitGroupMap.get(1).getUnitName() + "=" + oneLevelTotal + unitGroupMap.get(1).getUnitName());
// 一级单位转一级单位,1克=1克
// =======================================================================================//
// >>>>>>>>>>>> 根据上级单位类型及单位数量转换为最指定的单位
int fourLevelQuantity21 = 1;
int fourLevelTotal21 = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 4, fourLevelQuantity21, 2);
System.out.println("四级单位转二级单位,1" + unitGroupMap.get(4).getUnitName() + "=" + fourLevelTotal21 + unitGroupMap.get(2).getUnitName());
// 四级单位转二级单位,1箱=200包
int fourLevelQuantity22 = 1;
int fourLevelTotal22 = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 4, fourLevelQuantity22, 3);
System.out.println("四级单位转三级单位,1" + unitGroupMap.get(4).getUnitName() + "=" + fourLevelTotal22 + unitGroupMap.get(3).getUnitName());
// 四级单位转三级单位,1箱=20盒
int threeLevelQuantity21 = 1;
int threeLevelTotal21 = UnitConvertTools.toUnitStandardQuantity(goodsId, unitList, 3, threeLevelQuantity21, 2);
System.out.println("三级单位转二级单位,1" + unitGroupMap.get(3).getUnitName() + "=" + threeLevelTotal21 + unitGroupMap.get(2).getUnitName());
// 三级单位转二级单位,1盒=10包
// =======================================================================================//
// >>>>>>>>>>>> 最小单位【估算/折算】大单位,按类型分组返回Map
int quantity = 8441;
LinkedHashMap<Integer, Integer> unitEquivalentQuantityMap = UnitConvertTools.toUnitEquivalentQuantityWithType(goodsId, unitList, quantity);
System.out.println("最小单位【估算/折算】大单位,按类型分组返回Map = " + unitEquivalentQuantityMap);
// {4=1, 3=1, 2=1, 1=1}
// =======================================================================================//
// >>>>>>>>>>>> 最小单位【估算/折算】大单位,返回文本
int quantity2 = 8441;
String unitEquivalentQuantity = UnitConvertTools.toUnitEquivalentQuantityText(goodsId, unitList, quantity2);
System.out.println("计算单位向上折算返回文本 = " + unitEquivalentQuantity);
// 1箱1盒1包1克
}
}
输出内容:
按类型分组返回保证顺序的Map(根据单位类型) = {1=Unit(id=1, goodsId=1000, unitName=克, unitType=1, unitBasics=1, unitValue=1, unitExchangeRate=1, unitSpecs=1g/克), 2=Unit(id=2, goodsId=1000, unitName=包, unitType=2, unitBasics=1, unitValue=40, unitExchangeRate=40, unitSpecs=40克/包), 3=Unit(id=3, goodsId=1000, unitName=盒, unitType=3, unitBasics=1, unitValue=10, unitExchangeRate=400, unitSpecs=(40g×10包)/盒), 4=Unit(id=4, goodsId=1000, unitName=箱, unitType=4, unitBasics=1, unitValue=20, unitExchangeRate=8000, unitSpecs=(40g×10包)/盒)}
计算每个单位类型对应的的兑换率(根据单位类型)= {4=8000, 3=400, 2=40, 1=1}
四级单位转一级单位,1箱=8000克
三级单位转一级单位,1盒=100克
二级单位转一级单位,1包=40克
一级单位转一级单位,1克=1克
四级单位转二级单位,1箱=200包
四级单位转三级单位,1箱=20盒
三级单位转二级单位,1盒=10包
最小单位【估算/折算】大单位,按类型分组返回Map = {4=1, 3=1, 2=1, 1=1}
计算单位向上折算返回文本 = 1箱1盒1包1克