EasyExcel导出多列相同合并处理以及单列相同合并处理

文章介绍了如何使用EasyExcel3.1.1版本在仓库管理系统中实现多列和单列相同数据的合并功能,包括自定义合并策略、控制层API、业务逻辑处理和自定义导出类模板。作者详细描述了如何通过`ExcelCellByMultiHandler`和`ExcelMergeCustomerCellHandler`处理多列和单列的合并情况。

目录

一、多列相同合并

1、需求效果图

2、控制层

3、业务层

4、自定义导出类模板(InWarehouseSumExcel)

5、PoiUtils工具类

6、自定义多列相同合并策略处理类

二、单列相同合并

1、需求效果图

2、控制层

3、业务层

4、自定义单列相同合并策略

5、自定义导出类模板(RealTimeInventoryExcel)


 首先此次使用的是easyexcel3.1.1版本,需要引入如下依赖:

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

其次,基本思路就是,通过继承CellWriteHandler接口编写合并策略(主要就是实现该方法afterCellDispose,该方法在对单元格的所有操作完成后调用),再构建表格处理对象ExcelWriter时,将所需合并策略传入即可。

一、多列相同合并

1、需求效果图

      

        从效果图可以知道,需要将前五列信息相同的合并为一行,然后该五列合并为一行的信息对多个设备信息。实现过程,大致如下:

2、控制层
    @GetMapping(value = "/inWarehouseExport", name = "仓库管理,导出入库库存统计")
    public void inWarehouseExport(@RequestParam(value = "wareHouseId", required = false) Integer wareHouseId,
                                  @RequestParam(value = "inWarehouseType", required = false) String inWarehouseType,
                                  @RequestParam(value = "equipAttributeId", required = false) Integer equipAttributeId,
                                  @RequestParam(value = "keyWords", required = false) String keyWords,
                                  @RequestParam(value = "startDate", required = false) String startDate,
                                  @RequestParam(value = "endDate", required = false) String endDate,
                                  HttpServletResponse response) {

        wmReportSumService.inWarehouseExport(wareHouseId, inWarehouseType, equipAttributeId, keyWords, startDate, endDate, response);

    }
3、业务层

    

public void inWarehouseExport(Integer wareHouseId, String inWarehouseType, Integer equipAttributeId, String keyWords, String startDate, String endDate, HttpServletResponse response) {
        try {
            // 定义日期格式
            List<InWarehouseSumDto> list = inWarehouseMapper.queryInWarehouseSum(wareHouseId, inWarehouseType, equipAttributeId, keyWords, startDate, endDate);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            //遍历结果,进行处理
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            List<InWarehouseSumExcel> excelList = new ArrayList<>();
            list.forEach(e -> {
                List<InventoryEquipInfoDto> equipInfoDtoList = e.getEquipInfoDtoList();
                equipInfoDtoList.forEach(dto -> {
                    InWarehouseSumExcel sumExcel = new InWarehouseSumExcel();
                    BeanUtils.copyProperties(e, sumExcel);
                    BeanUtils.copyProperties(dto, sumExcel);
                    sumExcel.setInWarehouseTimeStr(formatter.format(e.getInWarehouseTime()));
                    excelList.add(sumExcel);
                });
            });
            //将处理好的结果导出
            PoiUtils.exportExcel(response, "入库库存统计", "入库库存", excelList, InWarehouseSumExcel.class, new ExcelCellByMultiHandler(6));
        } catch (Exception e) {
            log.error("入库库存报表导出失败", e);
        }
    }

       需要注意的是,这个传入的excelList结果,一定是要处理好的,要保证前五列相同的都是在一起的,中间不要隔着其他前五列不相同的数据。

4、自定义导出类模板(InWarehouseSumExcel)
import com.alibaba.excel.annotation.ExcelProperty;
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 com.alibaba.excel.annotation.write.style.HeadStyle;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@Accessors(chain = true)
@EqualsAndHashCode
@HeadRowHeight(60)  //设置表头行高度
@ContentRowHeight(25) //设置内容行高度
@ColumnWidth(15) //指定列宽大小为15
@HeadStyle(fillForegroundColor = 44) //设置表头背景颜色
public class InWarehouseSumExcel implements Serializable {

    @ExcelProperty({"仓库"})
    @ColumnWidth(20)
    private String warehouseName;

    @ExcelProperty({"入库类型"})
    private String inWarehouseTypeName;

    @ExcelProperty({"入库日期"})
    private String inWarehouseTimeStr;

    @ExcelProperty({"入库单号"})
    private String inWarehouseCode;

    @ExcelProperty({"入库人"})
    private String inWarehouseMan;

    /**
     * 以下是一个多层标头,为了达到这种效果easyExcel会把入库设备明细该列进行合并,然后上面的单层标头为了和这个多层标头
     * 对应上,也会进行行合并,所以最后合并区域大小就为6,再使用合并策略的时候就需要指定初始合并区域大小为6
     */
    @ExcelProperty({"入库设备明细","设备属性"})
    private String equipAttributeName;

    @ExcelProperty({"入库设备明细","设备名称"})
    @ColumnWidth(20)
    private String equipName;

    @ExcelProperty({"入库设备明细","品牌"})
    private String equipBrand;

    @ExcelProperty({"入库设备明细","型号"})
    @ColumnWidth(20)
    private String equipModel;

    @ExcelProperty({"入库设备明细","设备序列号"})
    private String equipSerial;

    @ExcelProperty({"入库设备明细","设备编码"})
    private String equipCode;

    @ExcelProperty({"入库设备明细","入库数量"})
    private String num;

}
5、PoiUtils工具类
package vip.dtcloud.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;

/**
 * POI相关操作
 *
 * @author Louis
 * @date Jan 14, 2019
 */
public class PoiUtils {

  
    public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, List excelList, Class<?> clazz, CellWriteHandler mergeStrategy) throws IOException {
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        // 设置表头样式
        WriteCellStyle headStyle = new WriteCellStyle();
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置表格内容样式
        WriteCellStyle bodyStyle = new WriteCellStyle();
        bodyStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 拿到表格处理对象
        ExcelWriter writer = EasyExcel.write(response.getOutputStream())
                .needHead(true)
                .excelType(ExcelTypeEnum.XLSX)
                // 设置合并策略
                .registerWriteHandler(mergeStrategy)
                .registerWriteHandler(new HorizontalCellStyleStrategy(headStyle, bodyStyle))
                .build();
        // 设置表格sheet样式,并写入excel
        WriteSheet sheet = EasyExcel.writerSheet(sheetName).head(clazz).sheetNo(1).build();
        writer.write(excelList, sheet);
        writer.finish();
    }
}
6、自定义多列相同合并策略处理类

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @ClassName:ExcelMergeCustomerCellHandler
 * @Description:easyexcel合并策略处理类,多列相同合并策略
 * @Version:1.0
 * @Date:2023/9/7 09:36:52
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class ExcelCellByMultiHandler implements CellWriteHandler {

    /**
     * 用第几行开始合并 ,默认为1,因为第0行是标题,EasyExcel 的默认也是
     */
    private int mergeRowIndex = 1;


    /**
     * 参与合并的列,起始列位置,默认为0
     */
    private int mergeColumnStart = 0;


    /**
     * 参与合并的列,最后一列的位置,默认为4
     */
    private int mergeColumnEnd = 4;


    /**
     * 默认初始合并区域大小,标头有合并的,则需要指定标头合并后的合并区域大小,没有的话就是0
     */
    private int defaultMergeRegionSize = 0;


    public ExcelCellByMultiHandler(int defaultMergeRegionSize) {
        this.defaultMergeRegionSize = defaultMergeRegionSize;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }

    /*
     * 在对单元格的所有操作完成后调用
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list,
                                 Cell cell, Head head, Integer integer, Boolean aBoolean) {
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();
        if (curRowIndex > mergeRowIndex && curColIndex >= mergeColumnStart) {
            // 读取到mergeColumnEnd列,开始合并
            if (curColIndex == mergeColumnEnd) {
                mergeWithPrevRow(writeSheetHolder, cell, curRowIndex);
            }
        }

    }

    /**
     * @Description: 合并比较多列值是否相同
     * @Date: 2023/9/8  16:31
     * @Param writeSheetHolder: 通过此对象获取sheet等信息
     * @Param cell: 当前单元格
     * @Param curRowIndex: 当前行
     * @return: void
     **/
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex) {
        StringBuilder curString = new StringBuilder();
        StringBuilder preString = new StringBuilder();
        try {
            for (int i = mergeColumnStart; i <= mergeColumnEnd; i++) {
                Cell curCell = cell.getSheet().getRow(curRowIndex).getCell(i);
                Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(i);
                //根据单元格类型获取单元格的值
                Object curData = curCell.getCellType() == CellType.STRING ? curCell.getStringCellValue() : curCell.getNumericCellValue();
                Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
                curString.append(curData);
                preString.append(preData);
            }
            // 比较当前行的单元格与上一行是否相同,相同合并当前单元格与上一行
            Sheet sheet = writeSheetHolder.getSheet();
            // 获取合并信息
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            if (curString.toString().equals(preString.toString())) {
                boolean isMerged = false;
                // 下标
                int index = 0;
                for (int i = 0; i < mergeRegions.size(); i++) {
                    CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                    // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                    if (cellRangeAddr.isInRange(curRowIndex - 1, index)) {
                        //这个移除的要保证是也被合并的那个
                        sheet.removeMergedRegion(defaultMergeRegionSize);
                        cellRangeAddr.setLastRow(curRowIndex);
                        sheet.addMergedRegion(cellRangeAddr);
                        isMerged = true;
                    }
                    index++;
                    // 这个是为了在默认合并单元大小后,再重置index值,index表示的就是列,这样才能保证进行isInRange判断的时候能够对应上
                    if (index > defaultMergeRegionSize-1) {
                        index = 0;
                    }
                }
                if (!isMerged) {
                    for (int i = 0; i <= mergeColumnEnd; i++) {
                        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, i, i);
                        sheet.addMergedRegion(cellRangeAddress);
                    }
                }
            }else {  //不相等则更新默认合并区域大小
                defaultMergeRegionSize = mergeRegions.size();
            }
        } catch (Exception e) {
            log.error("仓库管理设备出入库统计合并单元格当前str==========" + curString, e);
            log.error("仓库管理设备出入库统计合并单元格上一次str==========" + preString, e);
        }

    }
}


二、单列相同合并

1、需求效果图

   

    根据效果图,得知,需要把第一列相同的进行合并。合并思路,同多列合并相同,只是传入不同的合并策略。

2、控制层

  

 @GetMapping(value = "/exportRealTimeInventory", name = "仓库管理,导出实时库存")
    public void exportRealTimeInventory(@RequestParam(value = "wareHouseId", required = false) Integer wareHouseId,
                                        @RequestParam(value = "equipAttributeId", required = false) Integer equipAttributeId,
                                        @RequestParam(value = "equipNameId", required = false) Integer equipNameId,
                                        @RequestParam(value = "keyWords", required = false) String keyWords,
                                        HttpServletResponse response) {

        wmReportSumService.exportRealTimeInventory(wareHouseId, equipAttributeId, equipNameId, keyWords, response);
    }
3、业务层
 @Override
    public void exportRealTimeInventory(Integer wareHouseId, Integer equipAttributeId, Integer equipNameId, String keyWords, HttpServletResponse response) {
        try {
            List<WarehouseInventoryDto> list = repertoryInfoMapper.queryByCondition(wareHouseId, equipAttributeId, equipNameId, keyWords);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            List<RealTimeInventoryExcel> excelList = new ArrayList<>();
            int inNumTotal = 0, outNumTotal = 0, numTotal = 0;
            //遍历结果,进行处理
            for (WarehouseInventoryDto dto : list) {
                RealTimeInventoryExcel inventoryExcel = new RealTimeInventoryExcel();
                BeanUtils.copyProperties(dto, inventoryExcel);
                excelList.add(inventoryExcel);
                //统计出汇总数据
                inNumTotal = inNumTotal + dto.getInNum();
                outNumTotal = outNumTotal + dto.getOutNum();
                numTotal = numTotal + dto.getNum();
            }
            //将结果排序,使相同仓库名称的数据在一起,为后面行合并提供条件
            Collator collator = Collator.getInstance(Locale.CHINA);
            excelList.sort(Comparator.comparing(RealTimeInventoryExcel::getWarehouseName, (s1, s2) -> collator.compare(s2, s1)));
            //追加汇总数据
            RealTimeInventoryExcel inventoryExcel = new RealTimeInventoryExcel();
            inventoryExcel.setWarehouseName(WmConstant.EXCEL_HEAD_SUMMARIZING).setInNum(inNumTotal)
                    .setOutNum(outNumTotal).setNum(numTotal);
            excelList.add(inventoryExcel);
            //将处理好的结果导出
            PoiUtils.exportExcel(response, "实时库存统计", "实时库存", excelList, RealTimeInventoryExcel.class, new ExcelMergeCustomerCellHandler(new int[]{0}, 0));
        } catch (Exception e) {
            log.error("实时库存报表导出失败", e);
        }
    }

     同多列合并一样,要保证excelList集合结果中仓库名称相同的连在一起,即 需要合并的列对应的字段相同的记录需要在一起。

4、自定义单列相同合并策略
package vip.dtcloud.common;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @ClassName:ExcelMergeCustomerCellHandler
 * @Description:easyexcel合并策略处理类,单列相同合并策略
 * @Version:1.0
 * @Date:2023/9/7 09:36:52
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExcelMergeCustomerCellHandler implements CellWriteHandler {
    /**
     * 一级合并的列,从0开始算
     */
    private int[] mergeColIndex;

    /**
     * 从指定的行开始合并,从0开始算
     */
    private int mergeRowIndex;


    /**
     * 在单元格上的所有操作完成后调用,遍历每一个单元格,判断是否需要向上合并
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 获取当前单元格行下标
        int currRowIndex = cell.getRowIndex();
        // 获取当前单元格列下标
        int currColIndex = cell.getColumnIndex();
        // 判断是否大于指定行下标,如果大于则判断列是否也在指定的需要的合并单元列集合中
        if (currRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColIndex.length; i++) {
                if (currColIndex == mergeColIndex[i]) {
                    if (currColIndex <= 18) {
                        // 一级合并唯一标识
                        Object currLevelOneCode = cell.getRow().getCell(0).getStringCellValue();
                        Object preLevelOneCode = cell.getSheet().getRow(currRowIndex - 1).getCell(0).getStringCellValue();
                        // 判断两条数据的是否是同一集合,只有同一集合的数据才能合并单元格
                        if (preLevelOneCode.equals(currLevelOneCode)) {
                            // 如果都符合条件,则向上合并单元格
                            mergeWithPrevRow(writeSheetHolder, cell, currRowIndex, currColIndex);
                            break;
                        }
                    } else {
                        // 一级合并唯一标识
                        Object currLevelOneCode = cell.getRow().getCell(0).getStringCellValue();
                        Object preLevelOneCode = cell.getSheet().getRow(currRowIndex - 1).getCell(0).getStringCellValue();
                        // 二级合并唯一标识
                        Object currLevelTwoCode = cell.getRow().getCell(19).getStringCellValue();
                        Object preLevelTwoCode = cell.getSheet().getRow(currRowIndex - 1).getCell(19).getStringCellValue();
                        if (preLevelOneCode.equals(currLevelOneCode) && preLevelTwoCode.equals(currLevelTwoCode)) {
                            // 如果都符合条件,则向上合并单元格
                            mergeWithPrevRow(writeSheetHolder, cell, currRowIndex, currColIndex);
                            break;
                        }
                    }
                }
            }
        }


    }

    /**
     * 当前单元格向上合并
     *
     * @param writeSheetHolder 表格处理句柄
     * @param cell             当前单元格
     * @param currRowIndex     当前行
     * @param currColIndex     当前列
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int currRowIndex, int currColIndex) {
        // 获取当前单元格数值
        Object currData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        // 获取当前单元格正上方的单元格对象
        Cell preCell = cell.getSheet().getRow(currRowIndex - 1).getCell(currColIndex);
        // 获取当前单元格正上方的单元格的数值
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
        // 将当前单元格数值与其正上方单元格的数值比较
        if (preData.equals(currData)) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            // 当前单元格的正上方单元格是否是已合并单元格
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress address = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (address.isInRange(currRowIndex - 1, currColIndex)) {
                    sheet.removeMergedRegion(i);
                    address.setLastRow(currRowIndex);
                    sheet.addMergedRegion(address);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(currRowIndex - 1, currRowIndex, currColIndex, currColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

5、自定义导出类模板(RealTimeInventoryExcel)
package vip.dtcloud.domain.warehousemanage;

import com.alibaba.excel.annotation.ExcelProperty;
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 com.alibaba.excel.annotation.write.style.HeadStyle;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @ClassName:RealTimeInventoryExcel
 * @Description:实时库存导出类模板
 * @Version:1.0
 * @Date:2023/9/7 10:14:09
 */
@Data
@Accessors(chain = true)
@EqualsAndHashCode
@HeadRowHeight(60)  //设置表头行高度
@ContentRowHeight(25) //设置内容行高度
@ColumnWidth(15) //指定列宽大小为15
@HeadStyle(fillForegroundColor = 44) //设置表头背景颜色
public class RealTimeInventoryExcel implements Serializable {

    @ColumnWidth(20)
    @ExcelProperty({"仓库"})
    private String warehouseName;

    @ExcelProperty({"设备属性"})
    private String equipAttributeName;

    @ExcelProperty({"设备名称"})
    private String equipName;

    @ExcelProperty({"品牌"})
    private String equipBrand;

    @ExcelProperty({"型号"})
    private String equipModel;

    @ExcelProperty({"总入库数量"})
    private Integer inNum;

    @ExcelProperty({"总出库数量"})
    private Integer outNum;

    @ExcelProperty({"当前库存数量"})
    private Integer num;

}

评论 20
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值