树形结构递归初始化(父节点,统计字段等)

1.核心思想:

1.先将每个节点按层级进行分组成map,并记录最大层级;
2.层级自下而上的递归,赋值父节点和统计金额类的字段;

2.核心方法实现:

    //层级分组map (key:层级;value:该层级下的节点集合)
    private Map<Integer, List<TreeNodeBO>> hierarchyMap = new HashMap<>();
   /**
     * 初始化父节点、金额、数量 <br>
     *
     * @param hierarchy: 最大层级
     * @return void
     * @see
     */
    private void initTreeNodeBO(Integer hierarchy) {
        if (hierarchy == 0) { //0层级无父级层级 、跳出递归
            return;
        }
        List<TreeNodeBO> currentHierarchy = hierarchyMap.get(hierarchy);//当前层级
        List<TreeNodeBO> parentHierarchy = hierarchyMap.get(--hierarchy);//当前层级的 父级层级
        if (CollectionUtils.isNotEmpty(currentHierarchy)) {
            parentHierarchy.stream().forEach(parent -> {
                //当前父节点 的子节点集合
                List<TreeNodeBO> childNode = currentHierarchy.stream().filter(child -> child.getCode().indexOf(parent.getCode()) == 0).collect(Collectors.toList());
                childNode.stream().forEach(child -> child.setParentCode(parent.getCode()));//子节点集合设置 parentCode
                BigDecimal childAmount = childNode.stream().map(TreeNodeBO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);//子节点金额和
                BigDecimal childQty = childNode.stream().map(TreeNodeBO::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);//子节点数量和
                parent.setAmount(parent.getAmount().add(childAmount));
                parent.setQty(parent.getQty().add(childQty));
            });
        }
        initTreeNodeBO(hierarchy);
    }

3.完整代码如下

我的节点数据来自excel的导入(excel的导入导出可以看:Springboot 下 EasyExcel 的数据导入导出

节点数据按层级分组后的是 hierarchyMap ,是在 invoke() 方法中初始化的;

核心思想的实现,只需要看 doAfterAllAnalysed()方法

3.1. 树形实体

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ContentRowHeight(15)//内容单元格高度
@HeadRowHeight(15)//表头单元格高度
@ColumnWidth(10)//单元格宽度
public class ExcelBO {
    //编码
    @NotEmpty
    @ExcelProperty(index = 0,value = {"编码"})
    private String code;
    //名称
    @NotEmpty
    @ExcelProperty(index = 1,value = {"名称"})
    private String name;
    //单位
    @NotEmpty
    @ExcelProperty(index = 2,value = "单位")
    private String unit;
    //数量
    @NotNull
    @ExcelProperty(index = 3,value = "数量")
    private BigDecimal qty;
    //单价
    @NotNull
    @ExcelProperty(index = 4,value = "单价")
    private BigDecimal price;
    //生产日期
    @NotNull
    @ExcelProperty(index = 5,value = "生产日期")
    private LocalDateTime dateInProduced;
    //备注
    @ExcelProperty(index = 6,value = "备注")
    private String remake;

}
@Data
@ToString(callSuper = true)
public class TreeNodeBO extends ExcelBO {
    //父级编码
    private String parentCode;

    //总价值
    private BigDecimal amount;

    //层级( 0:第一层  ; 1:第二层 ; 3:第三层)
    private Integer hierarchy ;

    //子节点集合
    private List<TreeNodeBO> childs;

}

3.2. 完整操作

/**
 * 解析监听类 <br>
 *
 * @author lls
 * @version 1.0.0
 * @date 2021/5/19
 */
@Slf4j
public class ExcelReadListener extends AnalysisEventListener<ExcelBO> {

    private Gson gson = new Gson();

    //层级分组map (key:层级;value:该层级下的节点集合)
    private Map<Integer, List<TreeNodeBO>> hierarchyMap = new HashMap<>();

    //最大层级
    private Integer maxHierarchy = -1;

    //编码层级分隔符
    private static String splitStr = "-";

    /**
     * 每行解析动作 <br>
     *
     * @param data:
     * @param context:
     * @return void
     * @see
     */
    @Override
    public void invoke(ExcelBO data, AnalysisContext context) {
        context.readWorkbookHolder().getReadWorkbook().setAutoTrim(true);//读取进data
        log.info("当前解析行数据,data :{} ", gson.toJson(data));
        //todo 业务字段校验(长度,非空等) =》(service层 进行@valid校验)
        TreeNodeBO treeNodeBO = new TreeNodeBO();
        BeanUtils.copyProperties(data, treeNodeBO);
        Integer hierarchy = countHierarchy(treeNodeBO.getCode()); //t层级结构 = code中 splitStr 出现的次数
        maxHierarchy = Math.max(maxHierarchy, hierarchy);//最大层级
        treeNodeBO.setHierarchy(hierarchy);
        //todo  赋值父节点的空属性,用于 递归计算 (想办法去掉这段,太傻了)↓↓↓↓↓↓↓↓↓
        if (null != treeNodeBO.getPrice() && null != treeNodeBO.getQty()) {
            treeNodeBO.setAmount(treeNodeBO.getPrice().multiply(treeNodeBO.getQty()));//总金额 = 单价 * 数量
        } else {
            treeNodeBO.setAmount(BigDecimal.ZERO);
        }
        if (null == treeNodeBO.getQty()) {
            treeNodeBO.setQty(BigDecimal.ZERO);
        }
        //todo  ↑↑↑↑↑↑↑
        List<TreeNodeBO> nodeList = Optional.ofNullable(hierarchyMap.get(hierarchy)).orElse(new ArrayList<>());
        nodeList.add(treeNodeBO);
        hierarchyMap.put(treeNodeBO.getHierarchy(), nodeList);
    }

    /**
     * 解析完成后动作 <br>
     *
     * @param context:
     * @return void
     * @see
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("解析到的所有数据集合,excelBOList :{} ", gson.toJson(hierarchyMap));
        initTreeNodeBO(maxHierarchy);
        log.info("解析后的所有数据集合,excelBOList :{} ", gson.toJson(hierarchyMap));
    }

    /**
     * 初始化父节点、金额、数量 <br>
     *
     * @param hierarchy: 最大层级
     * @return void
     * @see
     */
    private void initTreeNodeBO(Integer hierarchy) {
        if (hierarchy == 0) { //0层级无父级层级 、跳出递归
            return;
        }
        List<TreeNodeBO> currentHierarchy = hierarchyMap.get(hierarchy);//当前层级
        List<TreeNodeBO> parentHierarchy = hierarchyMap.get(--hierarchy);//当前层级的 父级层级
        if (CollectionUtils.isNotEmpty(currentHierarchy)) {
            parentHierarchy.stream().forEach(parent -> {
                //当前父节点 的子节点集合
                List<TreeNodeBO> childNode = currentHierarchy.stream().filter(child -> child.getCode().indexOf(parent.getCode()) == 0).collect(Collectors.toList());
                childNode.stream().forEach(child -> child.setParentCode(parent.getCode()));//子节点集合设置 parentCode
                BigDecimal childAmount = childNode.stream().map(TreeNodeBO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);//子节点金额和
                BigDecimal childQty = childNode.stream().map(TreeNodeBO::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);//子节点数量和
                parent.setAmount(parent.getAmount().add(childAmount));
                parent.setQty(parent.getQty().add(childQty));
            });
        }
        initTreeNodeBO(hierarchy);
    }

    /**
     * 计算节点层级结构(层级结构 = 分隔符出现的次数) <br>
     *
     * @param code: 编码
     * @return java.lang.Integer
     * @see
     */
    private static Integer countHierarchy(String code) {
        int before = code.length();
        int after = code.replace(splitStr, "").length();
        return before - after;
    }

    /**
     * 每个sheet页的头行触发函数 <br>
     *
     * @param headMap:
     * @param context:
     * @return void
     * @see
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("解析到一条头数据:{}", gson.toJson(headMap));
        //todo 校验excel模板的正确性
        //todo 通过 ExcelBO.class 获取field域上ExcelProperty注解。与headMap对比
    }

    /**
     * 发生异常时触发函数 <br>
     *
     * @param exception:
     * @param context:
     * @return void
     * @see
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.info("捕捉到一条异常:{}", exception);
        //todo 记录并跳过错误行,返回前端成功的条数
        throw exception;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值