|
|
@@ -24,6 +24,11 @@ import org.springframework.validation.annotation.Validated;
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.YearMonth;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.temporal.TemporalAdjusters;
|
|
|
import java.util.*;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -264,4 +269,187 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public Map<String, BigDecimal> inventoryTrend(IotProdInventoryAgingPageReqVO pageReqVO) {
|
|
|
+ // 库龄数据量大 必须选择日期
|
|
|
+ String fDate = pageReqVO.getFdate();
|
|
|
+ if (StrUtil.isBlank(fDate)) {
|
|
|
+ throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ String deptIdStr = pageReqVO.getDept();
|
|
|
+ if (StrUtil.isBlank(deptIdStr)) {
|
|
|
+ throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ // 根据 fDate 生成最近6个月的的日期 fDate所在的月保持现状
|
|
|
+ // 比如 fDate = 2026年8月15日 则最终生成的 6个日期是
|
|
|
+ // 2026年8月15日
|
|
|
+ // 2026年7月31日
|
|
|
+ // 2026年6月30日
|
|
|
+ // 2026年5月31日
|
|
|
+ // 2026年4月30日
|
|
|
+ // 2026年3月31日
|
|
|
+ // 解析传入的日期(假设格式为 yyyy-MM-dd)
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ LocalDate startDate = LocalDate.parse(fDate, formatter);
|
|
|
+ // 生成6个日期字符串
|
|
|
+ List<String> latestSixDateStr = new ArrayList<>(6);
|
|
|
+ // 第一个日期就是 fDate 本身
|
|
|
+ latestSixDateStr.add(startDate.format(formatter));
|
|
|
+
|
|
|
+ // 从当前月份开始,依次向前推5个月,取每个月的最后一天
|
|
|
+ YearMonth currentMonth = YearMonth.from(startDate);
|
|
|
+ for (int i = 1; i < 6; i++) {
|
|
|
+ // 上一个月
|
|
|
+ currentMonth = currentMonth.minusMonths(1);
|
|
|
+ LocalDate lastDayOfMonth = currentMonth.atEndOfMonth();
|
|
|
+ latestSixDateStr.add(lastDayOfMonth.format(formatter));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询指定公司在这6个日期节点上的 总库存 1年内的总库存 积压库存
|
|
|
+ // 查询帆软数仓中的库龄数据
|
|
|
+ Map<String, List<IotProdInventoryAgingDO>> dateInventoryAgesPair = new HashMap<>();
|
|
|
+ pageReqVO.setDept(deptIdStr);
|
|
|
+ pageReqVO.setFdate(StrUtil.EMPTY);
|
|
|
+ pageReqVO.setDateStrs(latestSixDateStr);
|
|
|
+ List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
|
|
|
+ if (CollUtil.isNotEmpty(stockAges)) {
|
|
|
+ dateInventoryAgesPair = stockAges.stream()
|
|
|
+ .collect(Collectors.groupingBy(age -> age.getFdate()));
|
|
|
+ }
|
|
|
+ // 各公司的积压库存 key日期yyyy-MM-dd value积压库存金额
|
|
|
+ Map<String, BigDecimal> overStockPair = new HashMap<>();
|
|
|
+ // 循环分组统计总库存、积压库存 按照库存地点统计
|
|
|
+ dateInventoryAgesPair.forEach((groupDate, list) -> {
|
|
|
+ BigDecimal sumTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmttotal)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ BigDecimal sumOneYear = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmt1)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
|
|
|
+
|
|
|
+ overStockPair.put(groupDate, sumOverStock);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 各公司的积压库存 key日期yyyy-MM value积压库存金额
|
|
|
+ Map<String, BigDecimal> resultOverStockPair = latestSixDateStr.stream()
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ date -> date.substring(0, 7), // 提取 yyyy-MM
|
|
|
+ date -> overStockPair.getOrDefault(date, BigDecimal.ZERO),
|
|
|
+ (v1, v2) -> v1, // 不会出现重复 key,占位
|
|
|
+ LinkedHashMap::new // 保持顺序
|
|
|
+ ));
|
|
|
+ // 按照6个日期的顺序 返回 积压库存
|
|
|
+
|
|
|
+ return resultOverStockPair;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, BigDecimal> inventoryAgeDetail(IotProdInventoryAgingPageReqVO pageReqVO) {
|
|
|
+ // 库龄数据量大 必须选择日期
|
|
|
+ String fDate = pageReqVO.getFdate();
|
|
|
+ if (StrUtil.isBlank(fDate)) {
|
|
|
+ throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ String deptIdStr = pageReqVO.getDept();
|
|
|
+ if (StrUtil.isBlank(deptIdStr)) {
|
|
|
+ throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ // 存货及变动情况 账龄分析
|
|
|
+ // 年初存货 年初积压 期末库存 期末积压
|
|
|
+ // 年初存货 当前查询日期所在年的第一天
|
|
|
+ // 1年以内 1-2年 2-3年 3年以上 积压比
|
|
|
+ // 需要查询2个时间节点的数据 fDate所在年的第一天yyyy-MM-dd fDate
|
|
|
+ LocalDate fDateLocal = LocalDate.parse(fDate);
|
|
|
+ LocalDate firstDayOfYear = fDateLocal.with(TemporalAdjusters.firstDayOfYear());
|
|
|
+ String yearStartDate = firstDayOfYear.toString(); // 年初日期字符串
|
|
|
+ List<String> queryDates = new ArrayList<>();
|
|
|
+ queryDates.add(yearStartDate);
|
|
|
+ queryDates.add(fDate);
|
|
|
+
|
|
|
+ // 查询帆软数仓中的库龄数据
|
|
|
+ Map<String, List<IotProdInventoryAgingDO>> dateInventoryAgesPair = new HashMap<>();
|
|
|
+
|
|
|
+ pageReqVO.setDept(deptIdStr);
|
|
|
+ pageReqVO.setFdate(StrUtil.EMPTY);
|
|
|
+ pageReqVO.setDateStrs(queryDates);
|
|
|
+ List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
|
|
|
+ if (CollUtil.isNotEmpty(stockAges)) {
|
|
|
+ dateInventoryAgesPair = stockAges.stream()
|
|
|
+ .collect(Collectors.groupingBy(age -> age.getFdate()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 期末 期初 的库龄数据 key期末期初标识 value 总库存 积压库存
|
|
|
+ Map<String, BigDecimal> stockAgePair = new HashMap<>();
|
|
|
+ // 统计期初 期末库龄数据
|
|
|
+ // 循环分组统计 期初 期末 数据 总库存、积压库存
|
|
|
+ dateInventoryAgesPair.forEach((groupDate, list) -> {
|
|
|
+ // 总库存
|
|
|
+ BigDecimal sumTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmttotal)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ // 1年以内库存
|
|
|
+ BigDecimal sumOneYear = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmt1)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ // 1-2年库存
|
|
|
+ BigDecimal sumTwoTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmt2)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ // 2-3年库存
|
|
|
+ BigDecimal sumThreeTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmt3)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ // 4年库存
|
|
|
+ BigDecimal sumFourTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmt4)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ // 其它库存
|
|
|
+ BigDecimal sumOtherTotal = list.stream()
|
|
|
+ .map(IotProdInventoryAgingDO::getAmtn)
|
|
|
+ .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
|
|
|
+ if (yearStartDate.equals(groupDate)) {
|
|
|
+ // 期初库龄数据
|
|
|
+ stockAgePair.put("年初存货", sumTotal);
|
|
|
+ // 期初库龄数据
|
|
|
+ stockAgePair.put("年初积压", sumOverStock);
|
|
|
+ }
|
|
|
+ if (fDate.equals(groupDate)) {
|
|
|
+ // 期末库龄数据
|
|
|
+ stockAgePair.put("期末库存", sumTotal);
|
|
|
+ // 期末库龄数据
|
|
|
+ stockAgePair.put("期末积压", sumOverStock);
|
|
|
+
|
|
|
+ // 1年以内 SUM(AMT1)
|
|
|
+ // 1-2年 SUM(AMT2)
|
|
|
+ // 2-3年 SUM(AMT3)
|
|
|
+ // 3年以上 SUM(AMT4 + AMTN)
|
|
|
+ stockAgePair.put("1年以内", sumOneYear);
|
|
|
+ stockAgePair.put("1-2年", sumTwoTotal);
|
|
|
+ stockAgePair.put("2-3年", sumThreeTotal);
|
|
|
+ stockAgePair.put("3年以上", sumFourTotal.add(sumOtherTotal));
|
|
|
+
|
|
|
+ // 积压比 期末积压 / 期末库存
|
|
|
+ if (sumTotal.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ BigDecimal overStockRate = sumOverStock.divide(sumTotal, 4, RoundingMode.HALF_UP);
|
|
|
+ stockAgePair.put("积压比", overStockRate);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return stockAgePair;
|
|
|
+ }
|
|
|
+
|
|
|
}
|