物联网新零售项目 合作商分账结算

第7章 合作商分账结算

1.分成金额计算

1.1 需求分析

根据订单金额和合作商的分成比例,进行分成金额的计算

1.2 实现思路

在创建订单时添加逻辑:

(1)根据售货机所在点位的合作商ID查询合作商信息,其中包括分成比例

(2)将商品的真实价格乘以分成比例 ,商业运算中一般实用BigDecimal的multiply(乘法运算)方法和divide(除法运算)方法。

1.3 代码实现

修改订单微服务OrderServiceImpl的createOrder方法,在this.save(orderEntity); 之前添加代码

//设置合作商账单分账金额
PartnerViewModel partner = userService.getPartner(vm.getOwnerId());
BigDecimal bg = new BigDecimal(sku.getRealPrice());
//遵循四舍五入的规则
int bill = bg.multiply(new BigDecimal(partner.getRatio())).divide(new BigDecimal(100),0,RoundingMode.HALF_UP).intValue();

orderEntity.setBill(bill);

BigDecimal中的divide主要就是用来做除法的运算。

public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode);

第一个参数是除数,第二个参数代表保留几位小数,第三个代表的是保留模式:

RoundingMode.CEILING:取右边最近的整数

RoundingMode.DOWN:去掉小数部分取整,也就是正数取左边,负数取右边,相当于向原点靠近的方向取整

RoundingMode.FLOOR:取左边最近的正数

RoundingMode.HALF_DOWN:五舍六入,负数先取绝对值再五舍六入再负数

RoundingMode.HALF_UP:四舍五入,负数原理同上

RoundingMode.HALF_EVEN:这个比较绕,整数位若是奇数则四舍五入,若是偶数则五舍六入

2.订单数据定时汇总

2.1 需求分析

在立可得管理后台里,管理员要能够根据合作商、日期条件能够查看一定日期范围之内的分成情况:

《物联网新零售项目 合作商分账结算》

还需要在合作商后台里,合作商能够根据点位和一定时间范围查看自己的分账情况:

《物联网新零售项目 合作商分账结算》

2.2 实现思路

如果每次都从订单表汇总统计,必定是查询效率低下。所以我们需要每天定期对数据进行汇总,并将汇总的结果保存到一张订单汇总表中。这样订单汇总表的数据不会过多,再次进行聚合统计时效率也会比较高。

订单库- 订单汇总表 tb_order_collect :

《物联网新零售项目 合作商分账结算》

任务描述:

(1)完成定时任务类处理订单汇总逻辑,按合作商和点位汇总

(2)通过xxl-job配置任务

2.3 代码实现

(1)订单微服务添加xxl-job依赖

<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.2.0</version>
</dependency>

(2)添加配置

xxl:
  job:
    accessToken:
    admin:
      addresses: http://192.168.200.128:8080/xxl-job-admin
    executor:
      appname: order-service
      address:
      ip:
      port: 9982
      logretentiondays: 30

(3)创建com.lkd.job包,添加xxl-job的配置类(从售货机微服务拷贝过来)

(4)订单微服务编写任务类,处理订单合并请求

@Component
@RequiredArgsConstructor
@Slf4j
public class OrderCollectJob { 

    private final OrderService orderService;
    private final OrderCollectService orderCollectService;
    private final VMService vmService;
    private final UserService userService;

    /** * 订单合并 * @param param * @return */
    @XxlJob("orderCollectJobHandler")
    public ReturnT<String> collectTask(String param){ 

        var yesterday = LocalDate.now().plusDays(-1);

        var qw=new QueryWrapper<OrderEntity>();
        qw.select( "owner_id","node_id", "IFNULL(sum(amount),0) as amount ","IFNULL(sum(bill),0) as bill" ,"IFNULL(count(1),0) as price"     )
                .lambda().ge( OrderEntity::getCreateTime,yesterday  )
                .lt( OrderEntity::getCreateTime,LocalDate.now() )
                .eq(OrderEntity::getPayStatus , VMSystem.PAY_STATUS_PAYED)
                .groupBy(OrderEntity::getOwnerId,OrderEntity::getNodeId );


        orderService.list(qw).forEach( order->{ 
            var orderCollect=new OrderCollectEntity();
            orderCollect.setDate( yesterday );
            orderCollect.setNodeId( order.getNodeId() );
            orderCollect.setNodeName( vmService.getNodeName( order.getNodeId() ) );

            orderCollect.setOwnerId( order.getOwnerId() );
            var partner = userService.getPartner(order.getOwnerId());
            orderCollect.setOwnerName( partner.getName()   );

            orderCollect.setOrderTotalMoney( order.getAmount() );//金额
            orderCollect.setTotalBill( order.getBill() );//分成
            orderCollect.setOrderCount( order.getPrice() );//订单数量(借用了价格字段)
            orderCollect.setRatio( partner.getRatio() );//分成比例
            orderCollectService.save(orderCollect);

        } );
        return ReturnT.SUCCESS;
    }
}

代码解释:

Lombok在spring中的特殊注解 @RequiredArgsConstructor , 写在类上可以代替@AutoWired注解,需要注意的是在注入时需要用final定义

(5)通过xxl-job配置任务

新增执行器 order-service (订单微服务)

新增任务 “订单汇总”

《物联网新零售项目 合作商分账结算》

3.合作商分账查询

3.1 需求分析

在立可得管理后台里,管理员要能够根据合作商、日期条件能够查看一定日期范围之内的分成情况:

《物联网新零售项目 合作商分账结算》

3.2 实现思路

对订单汇总表 tb_order_collect进行聚合统计

API:管理后台-对账管理-获取一定日期范围之内的合作商分成汇总数据

3.3 代码实现

(1)新建ReportService,并新增方法

/** * 获取合作商分账汇总信息 * @param pageIndex * @param pageSize * @param name * @param start * @param end * @return */
Pager<OrderCollectEntity> getPartnerCollect(Long pageIndex,Long pageSize,String name,LocalDate start, LocalDate end);

实现类 ReportServiceImpl

@Override
public Pager<OrderCollectEntity> getPartnerCollect(Long pageIndex, Long pageSize, String name, LocalDate start, LocalDate end) { 
    Page<OrderCollectEntity> page = new Page<>(pageIndex,pageSize);
    var qw = new QueryWrapper<OrderCollectEntity>();
    qw.select(
            "IFNULL(sum(order_count),0) as order_count",
            "IFNULL(sum(total_bill),0) as total_bill",
            "IFNULL(sum(order_total_money),0) as order_total_money",
            "IFNULL(min(ratio),0) as ratio",
            "owner_name",
            "date"
    );
    if(!Strings.isNullOrEmpty(name)){ 
        qw.lambda().like(OrderCollectEntity::getOwnerName,name);
    }
    qw
            .lambda()
            .ge(OrderCollectEntity::getDate,start)
            .le(OrderCollectEntity::getDate,end)
            .groupBy(OrderCollectEntity::getOwnerName,OrderCollectEntity::getDate)
            .orderByDesc(OrderCollectEntity::getDate);

    return Pager.build(orderCollectService.page(page,qw));
}

按照日期和合作商对OrderCollect进行聚合统计

(2)订单微服务新建 ReportController ,并新增方法

/** * 获取一定日期范围之内的合作商分成汇总数据 * @param pageIndex * @param pageSize * @param partnerName * @param start * @param end * @return */
@GetMapping("/partnerCollect")
public Pager<OrderCollectEntity> getPartnerCollect(
        @RequestParam(value = "pageIndex",required = false,defaultValue = "1") Long pageIndex,
        @RequestParam(value = "pageSize",required = false,defaultValue = "10") Long pageSize,
        @RequestParam(value = "partnerName",required = false,defaultValue = "") String partnerName,
        @RequestParam(value = "start",required = true,defaultValue = "") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate start,
        @RequestParam(value = "end",required = true,defaultValue = "") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
){ 
    return reportService.getPartnerCollect(pageIndex,pageSize,partnerName,start,end);
}

4.合作商后台-点位分账查询

4.1 需求分析

在合作商工作台页面要展示近期12条分账信息,点击查看更多跳转到对账管理页面。

对账管理页面是对对账详情进行查看,合作商可以分页搜索查看自己的点位下每天的分账情况。

最近12条合作商点位分账信息展示效果:

《物联网新零售项目 合作商分账结算》

合作商点位分账列表展示:

《物联网新零售项目 合作商分账结算》

4.2 实现思路

对订单汇总表 tb_order_collect进行查询

API: 合作商后台-分账管理- 合作商获分账信息查询

​ 合作商后台-分账管理-合作商获取最近12条分账信息

4.3 代码实现

(1)ReportService新增方法定义

/** * 获取合作商前12条点位分账数据 * @param partnerId * @return */
List<OrderCollectEntity> getTop12(Integer partnerId);

/** * 合作商点位分账搜索 * @param partnerId * @param nodeName * @param start * @param end * @return */
Pager<OrderCollectEntity> search(Long pageIndex,Long pageSize,Integer partnerId, String nodeName, LocalDate start, LocalDate end);

ReportServiceImpl实现方法

@Override
public List<OrderCollectEntity> getTop12(Integer partnerId) { 
    var qw = new LambdaQueryWrapper<OrderCollectEntity>();
    qw   .select(OrderCollectEntity::getDate,OrderCollectEntity::getNodeName,OrderCollectEntity::getOrderCount,OrderCollectEntity::getTotalBill)
            .eq(OrderCollectEntity::getOwnerId,partnerId)
            .orderByDesc(OrderCollectEntity::getDate)
            .last("limit 12");

    return orderCollectService.list(qw);
}

@Override
public Pager<OrderCollectEntity> search(Long pageIndex,Long pageSize,Integer partnerId, String nodeName, LocalDate start, LocalDate end) { 
    var qw = new LambdaQueryWrapper<OrderCollectEntity>();
    qw
 .select(OrderCollectEntity::getDate,OrderCollectEntity::getNodeName,OrderCollectEntity::getOrderCount,OrderCollectEntity::getTotalBill)
            .eq(OrderCollectEntity::getOwnerId,partnerId);
    if(!Strings.isNullOrEmpty(nodeName)){ 
        qw.like(OrderCollectEntity::getNodeName,nodeName);
    }
    if(start !=null && end != null){ 
        qw
                .ge(OrderCollectEntity::getDate,start)
                .le(OrderCollectEntity::getDate,end);
    }
    qw.orderByDesc(OrderCollectEntity::getDate);
    var page = new Page<OrderCollectEntity>(pageIndex,pageSize);
    return Pager.build(orderCollectService.page(page,qw));
}

(2)ReportController新增方法

/** * 获取最近12条分账信息 * @param partnerId * @return */
@GetMapping("/top12Collect/{partnerId}")
public List<OrderCollectEntity> getTop12Collect(@PathVariable Integer partnerId){ 
    return reportService.getTop12(partnerId);
}

/** * 合作商搜索分账信息 * @param partnerId * @param pageIndex * @param pageSize * @param nodeName * @param start * @param end * @return */
@GetMapping("/search/{partnerId}")
public Pager<OrderCollectEntity> search(
        @PathVariable Integer partnerId,
        @RequestParam(value = "pageIndex",required = false,defaultValue = "1") Long pageIndex,
        @RequestParam(value = "pageSize",required = false,defaultValue = "10") Long pageSize,
        @RequestParam(value = "nodeName",required = false,defaultValue = "") String nodeName,
        @RequestParam(value = "start",required = true,defaultValue = "") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate start,
        @RequestParam(value = "end",required = true,defaultValue = "") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){ 
    return reportService.search(
            pageIndex,
            pageSize,
            partnerId,
            nodeName,
            start,
            end);
}

5.对账数据导出

5.1 需求分析

在合作商后台,当合作商根据点位搜索出分账列表之后,还可以将数据以Excel格式导出。

《物联网新零售项目 合作商分账结算》

导出的数据格式如下:

《物联网新零售项目 合作商分账结算》

5.2 实现思路

(1)根据合作商输入的条件从数据库中查询结果(不分页)

(2)运用EasyExcel将查询到的结果转换成可识别的结构化对象,调用其内部封装好的方法转成Excel文件流。

(3)通过http的方式将文件流response到前端

之前我们想到Excel解析一般是使用POI,但POI存在一个严重的问题,就是非常消耗内存。所以阿里人员对它进行了重写从而诞生了easyexcel,它解决了过于消耗内存问题,也对它进行了封装让使用者使用更加便利

5.3 代码实现

5.3.1 合作商分账列表查询

(1)ReportService新增方法定义

/** * 获取分账数据列表 * @param partnerId * @param nodeName * @param start * @param end * @return */
List<OrderCollectEntity> getList(Integer partnerId,String nodeName,LocalDate start, LocalDate end);

ReportServiceImpl实现方法

@Override
public List<OrderCollectEntity> getList(Integer partnerId, String nodeName, LocalDate start, LocalDate end) { 
    var qw = new LambdaQueryWrapper<OrderCollectEntity>();
    qw
            .eq(OrderCollectEntity::getOwnerId,partnerId)
            .ge(OrderCollectEntity::getDate,start)
            .le(OrderCollectEntity::getDate,end);
    if(!Strings.isNullOrEmpty(nodeName)){ 
        qw.like(OrderCollectEntity::getNodeName,nodeName);
    }
    return orderCollectService.list(qw);
}

5.3.2 EasyExcel实现数据导出

(1)在service_common项目中添加解析Excel的依赖程序包,我们这里采用的是阿里提供的easyexcel

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>

(2) 在lkd_order_service项目中定义到导出的数据对象类:

package com.lkd.http.viewModel;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;

import java.util.Date;

/** * 账单导出数据对象 */
@Data
@ContentRowHeight(15)
@HeadRowHeight(20)
@ColumnWidth(25)
public class BillExportDataVO { 
    @DateTimeFormat("yyyy年MM月dd日")
    @ExcelProperty(value = "分账日期",index = 0)
    private Date date;
    @ColumnWidth(30)
    @ExcelProperty(value = "分账点位",index = 1)
    private String nodeName;
    @ExcelProperty(value = "订单数",index = 2)
    private Integer orderCount;
    @ExcelProperty(value = "分账金额",index = 3)
    private Integer amount;
}

说明:

  • ExcelProperty注解是用来定义Excel表头数据的,index参数用来定义表头排列的顺序。
  • ColumnWidth用来定义表头的宽度
  • DateTimeFormat用来定义日期时间格式数据在Excel中生成的格式

(3)ReportController新增方法,参照API 立可得2.0合作商后台- 分账管理- 合作商获分账信息导出

/** * 数据导出 * @param partnerId * @param start * @param end */
@GetMapping("/export/{partnerId}/{start}/{end}")
public void export(
        HttpServletResponse response,
        @PathVariable Integer partnerId,
        @PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate start,
        @PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end,
        @RequestParam(value = "nodeName",required = false,defaultValue = "") String nodeName) throws IOException { 
    var exportData = reportService
            .getList(partnerId,nodeName,start,end)
            .stream()
            .map(item->{ 
                var vo = new BillExportDataVO();
                vo.setAmount(item.getTotalBill());             vo.setDate(Date.from(item.getDate().atTime(0,0).atZone(ZoneId.systemDefault()).toInstant()));//atZone指定时区
                vo.setOrderCount(item.getOrderCount());
                vo.setNodeName(item.getNodeName());
                return vo;
            }).collect(Collectors.toList());
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding("utf-8");
    response.setHeader("Content-disposition", "attachment;filename=bill.xlsx");
    EasyExcel.write(response.getOutputStream(), BillExportDataVO.class).sheet("分账数据").doWrite(exportData);
}

java.time.ZoneId.systemDefault() 获取系统默认时区

测试:

(1)启动订单微服务

(2)打开浏览器,在地址栏输入 http://127.0.0.1:9008/report/export/1/2020-11-01/2021-12-31

6. 收益统计趋势图

6.1 需求分析

在合作商后台统计一段时间该合作商的收益趋势,前端以曲线图形式展现。

《物联网新零售项目 合作商分账结算》

6.2 实现思路

前端技术采用EChars图表工具实现。EChars官网: https://echarts.apache.org/zh/index.html

所以我们需要将查询出来的数据转换为EChars的曲线图所需的数据格式。

实现思路如下:

(1)构建用于封装EChars的曲线图实体类

(2)对订单合并表进行聚合查询,查询每天的收益,转换为EChars的曲线图实体类。

6.3 代码实现

6.3.1 折线图(柱状图)实体类构建

通过阅读EChars官网的示例,我们找到前端需要的数据格式。

option = { 
    xAxis: { 
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: { 
        type: 'value'
    },
    series: [{ 
        data: [150, 230, 224, 218, 135, 147, 260],
        type: 'line'
    }]
};

折线图和柱状图数据格式都是相同的, 我们根据上边的格式定义API :合作商后台-工作台-一定日期之内的营收统计

《物联网新零售项目 合作商分账结算》

创建实体类BarCharCollect,用于封装折线图和柱状图的图表数据。这里xAxis代表x轴数据。series代表图表的值。

/** * 柱状图 */
@Data
public class BarCharCollect implements Serializable { 
    /** * X轴 */
    private List<String> xAxis = Lists.newArrayList();
    /** * 数据 */
    private List<Integer> series = Lists.newArrayList();
}

该实体类用于封装图表数据。

6.3.2 趋势查询

(1)ReportService新增方法定义

/** * 获取一定日期内合作商的收益统计 * @param partnerId * @param start * @param end * @return */
BarCharCollect getCollect(Integer partnerId, LocalDate start, LocalDate end);

ReportServiceImpl实现该方法

@Override
public BarCharCollect getCollect(Integer partnerId, LocalDate start, LocalDate end) { 
    var qw = new QueryWrapper<OrderCollectEntity>();
    qw
            .select("IFNULL(sum(total_bill),0) as total_bill","date")
            .lambda()
            .ge(OrderCollectEntity::getDate,start)            
            .le(OrderCollectEntity::getDate,end)
            .eq(OrderCollectEntity::getOwnerId,partnerId)
            .orderByDesc(OrderCollectEntity::getDate)
            .groupBy(OrderCollectEntity::getDate);
    var mapCollect = orderCollectService
            .list(qw)
            .stream()
 .collect(Collectors.toMap(OrderCollectEntity::getDate,OrderCollectEntity::getTotalBill));
    var result = new BarCharCollect();
    start.datesUntil(end.plusDays(1), Period.ofDays(1))
            .forEach(date->{ 
                result.getXAxis().add(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
                if(mapCollect.containsKey(date)){ 
                    result.getSeries().add(mapCollect.get(date));
                }else { 
                    result.getSeries().add(0);
                }
            });
    return result;
}

LocalDate的datesUntil方法:用户希望返回两个给定端点之间的日期流

(2)ReportController新增方法

/** * 获取合作商一定日期范围的收益情况 * @param partnerId * @param start * @param end * @return */
@GetMapping("/collectReport/{partnerId}/{start}/{end}")
public BarCharCollect getCollectReport(@PathVariable Integer partnerId,
         @PathVariable  @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate start,
         @PathVariable  @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){ 
    return reportService.getCollect(partnerId,start,end);
}

7.数据批量导入

7.1 需求分析

在系统中除了可以通过商品添加的页面手动单个的添加商品之外,还能通过将商品数据按照Excel表格的固定格式填好之后批量导入到系统中。

Excle里填充得内容如下:

《物联网新零售项目 合作商分账结算》

管理人员通过在系统中将该文件上传到后台,后台程序通过识别Excel文件中得内容将里面每行数据解析成商品数据,存到商品表中。

7.2 实现思路

  • 在系统中开放文件上传的接口用来接收管理员上传的Excel文件
  • 使用easyExcel实现对Excel数据的解析,转换成要入库的商品数据
  • 为了提高程序的效率,将转换好的商品数据批量导入到数据库中

7.3 代码实现

7.3.1 EasyExcel实现商品批量导入

(1)在lkd_vms_service项目中添加对应的导入数据格式对象映射类:

package com.lkd.http.viewModel;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
public class SkuImport implements Serializable { 
    @ExcelProperty("商品名称")
    private String skuName;
    @ExcelProperty("图片url")
    private String skuImage;
    @ExcelProperty("价格")
    private Integer price;
    @ExcelProperty("类别")
    private Integer classId;
    @ExcelProperty("净含量")
    private String unit;
    @ExcelProperty("品牌")
    private String brandName;
}

说明:通过在类字段上加ExcelProperty注解,来让类的各个字段匹配Excel表中的表头信息,进行将表头和类字段做映射。

(2)在lkd_vms_service项目中添加ExcelDataListener

@Component
public class ExcelDataListener<E> extends AnalysisEventListener<E> { 

    private List<SkuEntity>  list= Lists.newArrayList();

    @Autowired
    private SkuService skuService;

    private static final int BATCH_COUNT=500;//每批存储条数

    /** * 提取数据 * @param e * @param analysisContext */
    @Override
    public void invoke(E e, AnalysisContext analysisContext) { 
        SkuEntity sku=new SkuEntity();
        BeanUtils.copyProperties( e,sku );
        list.add(sku);
        if( list.size()>=BATCH_COUNT ){ 
            doAfterAllAnalysed(null);
        }
    }


    /** * 读取完成 * @param analysisContext */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) { 

        skuService.saveBatch(list);
        list.clear();
    }
}

(3)在lkd_vms_service项目的SkuController中添加Excel文件接收和处理的方法:

/** * 上传商品解析 * @param file * @throws IOException */
@PostMapping("/upload")
public void upload(@RequestParam("fileName") MultipartFile file) throws IOException { 
    EasyExcel.read(file.getInputStream(), SkuImport.class,excelDataListener).sheet().doRead();
}

测试:

我们使用postman进行上传测试

《物联网新零售项目 合作商分账结算》

7.3.2 通用数据导入

上述代码是针对商品数据导入实现的逻辑,如果再有其他的导入数据的要求,还需要重新创建导入监听类。如果我们建立一个通用的导入监听类,岂不是一劳永逸吗?

实现思路:

(1)创建通用的数据监听类,可以处理任何数据的导入

(2)监听类中使用Function封装批量保存的操作。

(3)使用泛型表示具体要操作的实体类类型。

代码:

(1)在service_common项目中新建excel程序包,添加解析excel的架构类ExcelDataListener。

package com.lkd.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.google.common.collect.Lists;
import com.lkd.entity.AbstractEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/** * Excel解析批量存储 * @param <T> */
@Slf4j
public class ExcelDataListener<T extends AbstractEntity,E> extends AnalysisEventListener<E> { 
    private Class<T> clazz;
    /** * 每隔500条存储数据库 */
    private static final int BATCH_COUNT = 500;
    //存储的具体操作
    private Function<Collection<T>,Boolean> saveFunc;
    //批量存储的数据
    private List<T> list = Lists.newArrayList();

    /** * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据 */
    @Override
    public void invoke(E e, AnalysisContext analysisContext) { 
        try { 
           T t = clazz.getDeclaredConstructor(null).newInstance();
            BeanUtils.copyProperties(e,t);
            list.add(t);
            if (list.size() >= BATCH_COUNT) { 
                this.saveFunc.apply(list);
                // 存储完成清理 list
                list.clear();
            }
        } catch (Exception ex) { 
            log.error("create new instance error",ex);
        }


    }

    /** * 读取完之后的操作 */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) { 
        this.saveFunc.apply(list);
    }

    /** * * @param saveFunc 持久化数据到数据库的方法 * @param clazz 对应的实体类类型 */
    public ExcelDataListener(Function<Collection<T>,Boolean> saveFunc,Class<T> clazz){ 
        this.saveFunc = saveFunc;
        this.clazz = clazz;
    }
}

**说明:**这里该实现类的构造函数里需要传入两个参数,第一个参数saveFunc是最终持久化的方法实现,第二个参数是对应的要持久化的实体对象类型。因为除了商品数据需要导入之外可能还有其他数据需要导入,这里只通过调用EasyExcel解析出数据之后,转化为最终需要持久化的类型,然后在调用持久化方法saveFunc来完成持久化,但是具体的持久化操作不是框架层关心的事,而是具体需要调用方关心的。

(2)接下来我们修改 Excel文件接收和处理的方法:

/** * 上传商品解析 * @param file * @throws IOException */
@PostMapping("/upload")
public void upload(@RequestParam("fileName") MultipartFile file) throws IOException { 
    Function<List<SkuEntity>,Boolean> insertFunc = list-> skuService.saveBatch(list);
    ExcelDataListener<SkuEntity,SkuImport> dataListener = new ExcelDataListener(insertFunc,SkuEntity.class);
    EasyExcel.read(file.getInputStream(), SkuImport.class,dataListener).sheet().doRead();
}

可以看到最终在应用的时候,我们只需要new一个ExcelDataListener实例,将批量入库的方法insertFunc及要写入表的实体类类型两个作为参数传入ExcelDataListener的构造函数,然后调用EasyExcel的read方法来读取并解析数据就完成了数据的导入工作。

}
}


**说明:**这里该实现类的构造函数里需要传入两个参数,第一个参数saveFunc是最终持久化的方法实现,第二个参数是对应的要持久化的实体对象类型。因为除了商品数据需要导入之外可能还有其他数据需要导入,这里只通过调用EasyExcel解析出数据之后,转化为最终需要持久化的类型,然后在调用持久化方法saveFunc来完成持久化,但是具体的持久化操作不是框架层关心的事,而是具体需要调用方关心的。

(2)接下来我们修改 Excel文件接收和处理的方法:

```java
/**
     * 上传商品解析
     * @param file
     * @throws IOException
     */
@PostMapping("/upload")
public void upload(@RequestParam("fileName") MultipartFile file) throws IOException {
    Function<List<SkuEntity>,Boolean> insertFunc = list-> skuService.saveBatch(list);
    ExcelDataListener<SkuEntity,SkuImport> dataListener = new ExcelDataListener(insertFunc,SkuEntity.class);
    EasyExcel.read(file.getInputStream(), SkuImport.class,dataListener).sheet().doRead();
}

可以看到最终在应用的时候,我们只需要new一个ExcelDataListener实例,将批量入库的方法insertFunc及要写入表的实体类类型两个作为参数传入ExcelDataListener的构造函数,然后调用EasyExcel的read方法来读取并解析数据就完成了数据的导入工作。

    原文作者:办公模板库 素材蛙
    原文地址: https://blog.csdn.net/fegus/article/details/124206181
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞