赞
踩
周期时长、周期单位、叠加次数
商品 | 周期时长 | 周期单位 | 叠加次数 |
A | 4 | 月 | 2 |
B | 1 | 月 | 6 |
用量大于0GB, 流量总价30元 |
用量大于90GB, 流量单价0.19元/GB |
用量大于100GB, 流量单价0.12元/GB |
B商品 | |||||||||
1月 | 2月 | 3月 | 4月 | 5月 | 6月 | 7月 | 8月 | 9月 | |
用量 | 10KB | 50KB | 90KB | 91KB | 101KB | 102KB | |||
>0 | 30元 | 30元 | 30元 | 30元 | 30元 | 30元 | |||
>90 | 0元 | 0元 | 0元 | 0.19元 | 1.9元 | 1.9元 | |||
>100 | 0元 | 0元 | 0元 | 0元 | 0.12元 | 0.24元 |
A商品 | |||||||||
1月 | 2月 | 3月 | 4月 | 5月 | 6月 | 7月 | 8月 | 9月 | |
用量 | 10KB | 50KB | 31KB | 10KB | 10KB | 50KB | 31KB | 10KB | |
>0 | 30元 | 0元 | 0元 | 0元 | 30元 | 0元 | 0元 | 0元 | |
>90 | 0元 | 0元 | 0.19元 | 1.71元 | 0元 | 0元 | 0.19元 | 1.71元 | |
>100 | 0元 | 0元 | 0元 | 0.12元 | 0元 | 0元 | 0元 | 0.12元 |
- @Slf4j
- public class FlowSegmentGoodsClearingAlgorithm {
-
- private static String flowSegmentRuleStr = "{\"flowLadderList\":" + "[" +
- "{\"beginLadder\":0,\"calculationType\":1,\"unit\":\"KB\",\"unitPrice\":30.0000000000}," +
- "{\"beginLadder\":90,\"calculationType\":0,\"unit\":\"KB\",\"unitPrice\":0.1900000000}," +
- "{\"beginLadder\":100,\"calculationType\":0,\"unit\":\"KB\",\"unitPrice\":0.1200000000}" +
- "]}";
-
- public static void main(String[] args) {
- Map<String, BigDecimal> quantityMap = new HashMap<>();
- // quantityMap.put("2023-01", new BigDecimal("10"));
- // quantityMap.put("2023-02", new BigDecimal("50"));
- // quantityMap.put("2023-03", new BigDecimal("90"));
- // quantityMap.put("2023-04", new BigDecimal("91"));
- // quantityMap.put("2023-05", new BigDecimal("101"));
- // quantityMap.put("2023-06", new BigDecimal("102"));
-
- quantityMap.put("2023-01", new BigDecimal("10"));
- quantityMap.put("2023-02", new BigDecimal("50"));
- quantityMap.put("2023-03", new BigDecimal("31"));
- quantityMap.put("2023-04", new BigDecimal("10"));
- // quantityMap.put("2023-05", new BigDecimal("101"));
- // quantityMap.put("2023-06", new BigDecimal("102"));
-
- FlowSegmentRule flowSegmentRule = JSON.parseObject(flowSegmentRuleStr, FlowSegmentRule.class);
- List<FlowLadder> flowLadderList = flowSegmentRule.getFlowLadderList();
-
- // 将分段阶梯转换成算法阶梯
- List<FlowSegmentGoodsClearingAlgorithm.QuantityLadder> ladderList = new ArrayList<>();
- for (int i = 0; i < flowLadderList.size(); i++) {
- FlowLadder flowLadder = flowLadderList.get(i);
- if (i < flowLadderList.size() - 1) {
- ladderList.add(new FlowSegmentGoodsClearingAlgorithm.QuantityLadder(flowLadder.getBeginLadder(), flowLadderList.get(i + 1).getBeginLadder(), flowLadder.getUnitPrice(), flowLadder.getCalculationType(), flowLadder.getUnit()));
- } else {
- ladderList.add(new FlowSegmentGoodsClearingAlgorithm.QuantityLadder(flowLadder.getBeginLadder(), new BigDecimal(Long.MAX_VALUE), flowLadder.getUnitPrice(), flowLadder.getCalculationType(), flowLadder.getUnit()));
- }
- }
-
- List<QuantityLadderAmountResult> calc = calc(quantityMap, ladderList);
- calc.forEach(System.out::println);
- }
-
-
- /**
- * 账期内数量(流量)分段计算
- *
- * @param quantityMap key 账期 value 账期使用流量
- * @param quantityLadderList 分段配置
- * @return
- */
- public static List<QuantityLadderAmountResult> calc(Map<String, BigDecimal> quantityMap, List<QuantityLadder> quantityLadderList) {
- Assert.notEmpty(quantityMap, "quantityMap cannot empty");
- Assert.notEmpty(quantityLadderList, "quantityLadderList cannot empty");
-
- List<String> billDateSortList = quantityMap.keySet().stream().sorted(String::compareTo).collect(Collectors.toList());
- BigDecimal totalFlowMB = BigDecimal.ZERO;
-
- List<QuantityLadderAmountResult> resultList = new ArrayList<>();
-
- String unit = quantityLadderList.get(0).getUnit();
-
- int index = 0;
- for (String billDate : billDateSortList) {
- if (null == quantityMap.get(billDate) || BigDecimal.ZERO.equals(quantityMap.get(billDate))) {
- log.debug("calc 账期 {} 内流量为O,直接返回0元账单", billDate);
- quantityLadderList.forEach(i -> resultList.add(assembleZeroBill(billDate, i)));
- continue;
- }
-
- totalFlowMB = totalFlowMB.add(quantityMap.get(billDate));
- Map<Integer, QuantityLadderAmountResult> resultMap = new HashMap<>();
- BigDecimal converterUsage;
- if (FlowUnitEnum.GB.name().equals(unit)) {
- converterUsage = FlowUnitConverter.toGB(FlowUnitEnum.KB.name(), totalFlowMB);
- } else if (FlowUnitEnum.MB.name().equals(unit)) {
- converterUsage = FlowUnitConverter.toMB(FlowUnitEnum.KB.name(), totalFlowMB);
- } else {
- converterUsage = totalFlowMB;
- }
- // 补已达标废弃的阶梯
- if (index > 0) {
- for (int i = 0; i < index; i++) {
- if (!resultMap.containsKey(i)) {
- resultMap.put(i, assembleZeroBill(billDate, quantityLadderList.get(i)));
- }
- }
- }
-
- for (int i = index; i < quantityLadderList.size(); i++) {
-
- QuantityLadder e = quantityLadderList.get(i);
-
- BigDecimal matchFlowMB = e.match(converterUsage, quantityMap.get(billDate));
- if (null == matchFlowMB) {
- index = i;
- break;
- }
- if (i == quantityLadderList.size() - 1) {
- index = i;
- }
- log.info("calc 账期 {} 总流量 {} 当期流量 {} 中有 {} 命中 {} - ({} , {}]", billDate, converterUsage, quantityMap.get(billDate), matchFlowMB, i, e.getBeginQuantity(), e.getEndQuantity());
-
- QuantityLadderAmountResult result = new QuantityLadderAmountResult();
- result.setBillDate(billDate);
- result.setQuantity(quantityMap.get(billDate));
- result.setMatchQuantity(matchFlowMB);
- result.setAmount(e.calcAmount(matchFlowMB));
-
- result.setPriceType(e.getPriceType());
- result.setPrice(e.getPrice());
- result.setTotalQuantity(converterUsage);
- result.setLadderDesc(e.getLadderDesc());
- result.setLadderIndex(i);
- result.setUnit(e.getUnit());
- resultMap.put(i, result);
- if (converterUsage.compareTo(e.getEndQuantity()) < 0) {
- index = i;
- break;
- }
- }
-
- // 补其他阶梯
- for (int i = index; i < quantityLadderList.size(); i++) {
- if (!resultMap.containsKey(i)) {
- resultMap.put(i, assembleZeroBill(billDate, quantityLadderList.get(i)));
- }
- }
-
- resultList.addAll(resultMap.values());
-
- }
- return resultList;
- }
-
- private static QuantityLadderAmountResult assembleZeroBill(String billDate, QuantityLadder ladder) {
- QuantityLadderAmountResult result = new QuantityLadderAmountResult();
- result.setBillDate(billDate);
- result.setQuantity(BigDecimal.ZERO);
- result.setMatchQuantity(BigDecimal.ZERO);
- result.setAmount(BigDecimal.ZERO);
- result.setPrice(ladder.getPrice());
- result.setPriceType(ladder.priceType);
- result.setUnit(ladder.getUnit());
- result.setLadderDesc(ladder.getLadderDesc());
- return result;
- }
-
-
- @Data
- public static class QuantityLadder {
- private static final Integer TOTAL_PRICE_FLAG = 1;
- private boolean isTotalPriceLock = false;
-
- /**
- * 分段开始数量
- */
- private BigDecimal beginQuantity;
- /**
- * 分段结束数量
- */
- private BigDecimal endQuantity;
- /**
- * 分段价格
- */
- private BigDecimal price;
- /**
- * 单位
- */
- private String unit;
- /**
- * 价格类型: 0-单价(默认),1-总价
- */
- private Integer priceType;
-
- public QuantityLadder() {
- }
-
- public QuantityLadder(BigDecimal beginQuantity, BigDecimal endQuantity, BigDecimal price, Integer priceType, String unit) {
- this.beginQuantity = beginQuantity;
- this.endQuantity = endQuantity;
- this.price = price;
- this.priceType = priceType;
- this.unit = unit;
- }
-
- /**
- * 分段阶梯匹配
- *
- * @param totalQuantity 账期在整改计算周期内总数量
- * @param quantity 账期的数量
- * @return 账期内匹配该阶梯的数量 null 则标识未匹配该阶梯
- */
- public BigDecimal match(BigDecimal totalQuantity, BigDecimal quantity) {
- boolean isMatch = beginQuantity.compareTo(totalQuantity) < 0;
- if (!isMatch) {
- return null;
- }
-
- if (endQuantity.compareTo(totalQuantity) < 0) {
- return min(endQuantity.subtract(totalQuantity.subtract(quantity)), endQuantity.subtract(beginQuantity));
- } else {
- return min(totalQuantity.subtract(beginQuantity), quantity);
- }
- }
-
- /**
- * 阶梯内金额计算
- * 1 当阶梯配置是总价,如果该阶梯没有被计算过,直接返回总价格; 如果被计算过直接返回0
- * 2 当阶梯计算是单价,
- *
- * @param matchQuantity
- * @return
- */
- public BigDecimal calcAmount(BigDecimal matchQuantity) {
- if (TOTAL_PRICE_FLAG.equals(priceType)) {
- BigDecimal amount = isTotalPriceLock ? BigDecimal.ZERO : price;
- isTotalPriceLock = true;
- return amount;
- } else {
- return price.multiply(matchQuantity).setScale(2, RoundingMode.HALF_UP);
- }
- }
-
- /**
- * 阶梯描述
- *
- * @return
- */
- public String getLadderDesc() {
- if (String.valueOf(Long.MAX_VALUE).equals(endQuantity.toString())) {
- return MessageFormat.format("({0},{1})", beginQuantity, "+∞");
- }
- return MessageFormat.format("({0},{1}]", beginQuantity.toString(), endQuantity.toString());
- }
-
- }
-
- private static BigDecimal min(BigDecimal a, BigDecimal b) {
- return (a.compareTo(b) <= 0) ? a : b;
- }
-
- @Data
- public static class QuantityLadderAmountResult {
-
- /**
- * 账期 实际是业务账期
- */
- private String billDate;
- /**
- * 账期内数量
- */
- private BigDecimal quantity;
- /**
- * 账期内计算数量
- */
- private BigDecimal matchQuantity;
- /**
- * 价格类型: 0-单价(默认),1-总价
- */
- private Integer priceType;
- /**
- * 分段价格
- */
- private BigDecimal price;
- /**
- * 计算金额
- */
- private BigDecimal amount;
- /**
- * 账期在周期内总数量
- */
- private BigDecimal totalQuantity;
- /**
- * 匹配分段描述
- */
- private String ladderDesc;
- /**
- * 命中阶梯
- */
- private Integer ladderIndex;
- /**
- * 流量单位 KB MB GB
- */
- private String unit;
- /**
- * 扩展信息
- */
- private String extendInfo;
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。