实现 高性能分批导出+动态进度条
parent
dec6af6da1
commit
fc5f67d70f
|
|
@ -1,5 +1,6 @@
|
|||
package net.lab1024.sa.admin.module.business.wms.base.address.controller;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.entity.AddressEntity;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.form.AddressAddForm;
|
||||
|
|
@ -11,6 +12,7 @@ import net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO
|
|||
import net.lab1024.sa.admin.module.business.wms.base.address.service.AddressQueryService;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.service.AddressService;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.item.domain.vo.ItemsExcelVO;
|
||||
import net.lab1024.sa.admin.module.business.wms.excel.ExportTaskService;
|
||||
import net.lab1024.sa.base.common.domain.RequestUser;
|
||||
import net.lab1024.sa.base.common.domain.ValidateList;
|
||||
import net.lab1024.sa.base.common.util.SmartExcelUtil;
|
||||
|
|
@ -47,6 +49,9 @@ public class AddressController {
|
|||
@Resource
|
||||
private AddressQueryService addressQueryService;
|
||||
|
||||
@Resource
|
||||
private ExportTaskService exportTaskService;
|
||||
|
||||
@Operation(summary = "分页查询 @author hj")
|
||||
@PostMapping("/address/queryPage")
|
||||
@SaCheckPermission("address:query")
|
||||
|
|
@ -91,7 +96,7 @@ public class AddressController {
|
|||
|
||||
@Operation(summary = "地址下拉查询")
|
||||
@PostMapping("/address/queryAddress")
|
||||
public ResponseDTO<List<AddressEntity>> queryAddress(@RequestBody AddressSelect addressSelect) {
|
||||
public ResponseDTO<PageResult<AddressVO>> queryAddress(@RequestBody @Valid AddressSelect addressSelect) {
|
||||
return ResponseDTO.ok(addressQueryService.queryAddress(addressSelect));
|
||||
}
|
||||
|
||||
|
|
@ -103,15 +108,23 @@ public class AddressController {
|
|||
return addressService.importAddress(file);
|
||||
}
|
||||
|
||||
@Operation(summary = "导出 霍锦")
|
||||
@GetMapping("/address/exportAddress")
|
||||
@SaCheckPermission("address:exportAddress")
|
||||
public void exportAddress(HttpServletResponse response) throws IOException {
|
||||
List<AddressExcelVO> addressList = addressQueryService.queryAddressExcel();
|
||||
Long start = System.currentTimeMillis();
|
||||
SmartExcelUtil.exportExcel(response, "收货地址信息.xlsx", "收货地址", AddressExcelVO.class, addressList);
|
||||
Long end = System.currentTimeMillis();
|
||||
System.out.println("导出耗时:" + (end - start));
|
||||
@PostMapping("/address/createExportTask")
|
||||
public ResponseDTO<String> createExportTask() {
|
||||
String taskId = exportTaskService.createTask();
|
||||
return ResponseDTO.ok(taskId);
|
||||
}
|
||||
|
||||
@GetMapping("/address/progress/{taskId}")
|
||||
public ResponseDTO<Long> getExportProgress(@PathVariable String taskId) {
|
||||
Long progress = exportTaskService.getProgress(taskId);
|
||||
return ResponseDTO.ok(progress);
|
||||
}
|
||||
|
||||
@Operation(summary = "导出 霍锦")
|
||||
@GetMapping("/address/exportAddress/{taskId}")
|
||||
@SaCheckPermission("address:exportAddress")
|
||||
public void exportAddress(@PathVariable String taskId, HttpServletResponse response) {
|
||||
System.out.println(exportTaskService.isTaskExists(taskId));
|
||||
addressQueryService.exportAddress(taskId, response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package net.lab1024.sa.admin.module.business.wms.base.address.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.entity.AddressEntity;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.form.AddressQueryForm;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.form.AddressSelect;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
|
@ -34,10 +36,25 @@ public interface AddressDao extends BaseMapper<AddressEntity> {
|
|||
*/
|
||||
List<AddressVO> queryPage(Page page, @Param("queryForm") AddressQueryForm queryForm);
|
||||
|
||||
/**
|
||||
* 下拉分页查询
|
||||
*
|
||||
* @param page
|
||||
* @param addressSelect
|
||||
* @return
|
||||
*/
|
||||
List<AddressVO> queryAddress(Page page, @Param("addressSelect") AddressSelect addressSelect);
|
||||
|
||||
/**
|
||||
* 流式查询
|
||||
* @return
|
||||
*/
|
||||
@Select("SELECT name,person,telephone,address FROM t_address")
|
||||
@Options(fetchSize = 2000) // 每次拉取2000条
|
||||
@Options(fetchSize = 2000)
|
||||
Cursor<AddressVO> selectAllByCursor();
|
||||
|
||||
//游标分页
|
||||
List<AddressVO> listByCursor(@Param("lastId") Long lastId, @Param("pageSize") int pageSize);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
package net.lab1024.sa.admin.module.business.wms.base.address.domain.form;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.lab1024.sa.base.common.domain.PageParam;
|
||||
|
||||
@Data
|
||||
public class AddressSelect {
|
||||
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class AddressSelect extends PageParam {
|
||||
@Schema(description = "收货单位")
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package net.lab1024.sa.admin.module.business.wms.base.address.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.write.style.*;
|
||||
import cn.idev.excel.enums.poi.FillPatternTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
|
|
@ -13,6 +15,7 @@ import java.time.LocalDateTime;
|
|||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ColumnWidth(20)
|
||||
public class AddressExcelVO {
|
||||
@ExcelProperty("收货单位")
|
||||
private String name;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public interface AddressQueryService {
|
|||
* @param addressSelect 入参
|
||||
* @return List<AddressEntity>
|
||||
*/
|
||||
List<AddressEntity> queryAddress(AddressSelect addressSelect);
|
||||
PageResult<AddressVO> queryAddress(AddressSelect addressSelect);
|
||||
|
||||
/**
|
||||
* 根据地址id集合查询地址信息
|
||||
|
|
@ -60,4 +60,6 @@ public interface AddressQueryService {
|
|||
|
||||
List<AddressExcelVO> queryAddressExcel3();
|
||||
|
||||
void exportAddress(String taskId,HttpServletResponse response);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package net.lab1024.sa.admin.module.business.wms.base.address.service.impl;
|
|||
import cn.idev.excel.ExcelWriter;
|
||||
import cn.idev.excel.FastExcel;
|
||||
import cn.idev.excel.write.metadata.WriteSheet;
|
||||
import cn.idev.excel.write.metadata.style.WriteCellStyle;
|
||||
import cn.idev.excel.write.metadata.style.WriteFont;
|
||||
import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.Maps;
|
||||
|
|
@ -18,16 +21,20 @@ import net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO
|
|||
import net.lab1024.sa.admin.module.business.wms.base.address.manager.AddressManager;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.address.service.AddressQueryService;
|
||||
import net.lab1024.sa.admin.module.business.wms.base.item.domain.vo.ItemsExcelVO;
|
||||
import net.lab1024.sa.admin.module.business.wms.excel.ExportTaskService;
|
||||
import net.lab1024.sa.base.common.domain.PageResult;
|
||||
import net.lab1024.sa.base.common.exception.BusinessException;
|
||||
import net.lab1024.sa.base.common.util.SmartPageUtil;
|
||||
import net.lab1024.sa.base.common.util.SmartResponseUtil;
|
||||
import net.lab1024.sa.base.module.support.dict.constant.DictConst;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
|
|
@ -43,6 +50,9 @@ public class AddressQueryServiceImpl implements AddressQueryService {
|
|||
@Resource
|
||||
private AddressManager addressManager;
|
||||
|
||||
@Resource
|
||||
private ExportTaskService exportTaskService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
|
|
@ -56,13 +66,15 @@ public class AddressQueryServiceImpl implements AddressQueryService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 地址下拉查询
|
||||
* 地址下拉分页查询
|
||||
*
|
||||
* @param addressSelect 入参
|
||||
* @return List<AddressEntity>
|
||||
*/
|
||||
public List<AddressEntity> queryAddress(AddressSelect addressSelect) {
|
||||
return addressManager.list();
|
||||
public PageResult<AddressVO> queryAddress(AddressSelect addressSelect) {
|
||||
Page<?> page = SmartPageUtil.convert2PageQuery(addressSelect);
|
||||
List<AddressVO> list = addressDao.queryAddress(page, addressSelect);
|
||||
return SmartPageUtil.convert2PageResult(page, list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,5 +209,66 @@ public class AddressQueryServiceImpl implements AddressQueryService {
|
|||
return list;
|
||||
}
|
||||
|
||||
public void exportAddress(String taskId, HttpServletResponse response) {
|
||||
System.out.println("开始读取地址数据...");
|
||||
long startTime = System.currentTimeMillis();
|
||||
SmartResponseUtil.setDownloadFileHeader(response, "收货地址信息.xlsx", null);
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
|
||||
|
||||
WriteFont headWriteFont = new WriteFont();
|
||||
headWriteFont.setFontName("宋体");
|
||||
headWriteFont.setColor(IndexedColors.WHITE.getIndex());
|
||||
headWriteCellStyle.setWriteFont(headWriteFont);
|
||||
|
||||
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||
contentWriteCellStyle.setFillForegroundColor(IndexedColors.BLACK.getIndex());
|
||||
|
||||
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
|
||||
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
|
||||
|
||||
try (ExcelWriter excelWriter = FastExcel.write(response.getOutputStream(), AddressExcelVO.class).registerWriteHandler(horizontalCellStyleStrategy).build()) {
|
||||
WriteSheet writeSheet = FastExcel.writerSheet("收货地址信息").build();
|
||||
//总条数
|
||||
long total = addressManager.count();
|
||||
// 初始化为最小ID-1
|
||||
long lastId = 0;
|
||||
// 根据测试调整
|
||||
int pageSize = 2000;
|
||||
//进度条
|
||||
long processed = 0;
|
||||
while (true) {
|
||||
List<AddressVO> batch = addressDao.listByCursor(lastId, pageSize);
|
||||
if (batch.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
List<AddressExcelVO> excelData = new ArrayList<>();
|
||||
for (AddressVO address : batch) {
|
||||
AddressExcelVO excelVO = AddressExcelVO.builder()
|
||||
.name(address.getName())
|
||||
.person(address.getPerson())
|
||||
.telephone(address.getTelephone())
|
||||
.address(address.getAddress())
|
||||
.build();
|
||||
excelData.add(excelVO);
|
||||
}
|
||||
excelWriter.write(excelData, writeSheet);
|
||||
lastId = batch.stream().mapToLong(AddressVO::getAddressId).max().orElse(0);
|
||||
processed += batch.size();
|
||||
|
||||
//计算进度条
|
||||
long progress = processed * 100 / total;
|
||||
exportTaskService.updateProgress(taskId, progress);
|
||||
System.out.println("已处理:" + processed + "条数据,进度:" + progress + "%");
|
||||
}
|
||||
System.out.println("导出耗时:" + (System.currentTimeMillis() - startTime) + "ms");
|
||||
excelWriter.finish();
|
||||
exportTaskService.updateProgress(taskId, 100);
|
||||
exportTaskService.cleanupTask(taskId);
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("导出失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package net.lab1024.sa.admin.module.business.wms.excel;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ExportTaskService {
|
||||
private final ConcurrentMap<String, Long> progressMap = new ConcurrentHashMap<>();
|
||||
|
||||
public String createTask() {
|
||||
String taskId = System.currentTimeMillis() + "-" + new Random().nextInt(1000000);
|
||||
progressMap.put(taskId, 0L);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public boolean isTaskExists(String taskId) {
|
||||
return progressMap.containsKey(taskId);
|
||||
}
|
||||
|
||||
public void updateProgress(String taskId, long progress) {
|
||||
progressMap.put(taskId, progress);
|
||||
}
|
||||
|
||||
public Long getProgress(String taskId) {
|
||||
return progressMap.getOrDefault(taskId, -1L);
|
||||
}
|
||||
|
||||
public void cleanupTask(String taskId) {
|
||||
progressMap.remove(taskId);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
<!-- 查询结果列 -->
|
||||
<sql id="base_columns">
|
||||
t_address.address_id,
|
||||
t_address
|
||||
.
|
||||
address_id
|
||||
,
|
||||
t_address.name,
|
||||
t_address.person,
|
||||
t_address.telephone,
|
||||
|
|
@ -20,23 +23,34 @@
|
|||
<where>
|
||||
<!--收货单位-->
|
||||
<if test="queryForm.addressId != null ">
|
||||
AND t_address.address_id=#{queryForm.addressId}
|
||||
AND t_address.address_id=#{queryForm.addressId}
|
||||
</if>
|
||||
</where>
|
||||
order by t_address.address_id desc
|
||||
</select>
|
||||
|
||||
<select id="listByCursor" resultType="net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO">
|
||||
<select id="queryAddress" resultType="net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO">
|
||||
SELECT
|
||||
t_address.address_id,
|
||||
t_address.name,
|
||||
t_address.person,
|
||||
t_address.telephone,
|
||||
t_address.address
|
||||
<include refid="base_columns"/>
|
||||
FROM t_address
|
||||
<where>
|
||||
<!--收货单位-->
|
||||
<if test="addressSelect.name != null ">
|
||||
AND t_address.name like concat(#{addressSelect.name},'%')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listByCursor" resultType="net.lab1024.sa.admin.module.business.wms.base.address.domain.vo.AddressVO">
|
||||
SELECT t_address.address_id,
|
||||
t_address.name,
|
||||
t_address.person,
|
||||
t_address.telephone,
|
||||
t_address.address
|
||||
FROM t_address
|
||||
WHERE t_address.address_id > #{lastId}
|
||||
ORDER BY t_address.address_id ASC -- 必须升序
|
||||
LIMIT #{pageSize}
|
||||
ORDER BY t_address.address_id ASC -- 必须升序
|
||||
LIMIT #{pageSize}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ import cn.idev.excel.FastExcel;
|
|||
import cn.idev.excel.write.handler.SheetWriteHandler;
|
||||
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import cn.idev.excel.write.metadata.style.WriteCellStyle;
|
||||
import cn.idev.excel.write.metadata.style.WriteFont;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.openxml4j.opc.PackagePartName;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.openxml4j.opc.TargetMode;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.apache.poi.xssf.usermodel.XSSFPictureData;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRelation;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
|
|
@ -42,6 +45,12 @@ public final class SmartExcelUtil {
|
|||
public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, Class head,Collection<?> data) throws IOException {
|
||||
// 设置下载消息头
|
||||
SmartResponseUtil.setDownloadFileHeader(response, fileName, null);
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
|
||||
WriteFont headWriteFont = new WriteFont();
|
||||
headWriteFont.setFontName("宋体");
|
||||
headWriteFont.setColor(IndexedColors.WHITE.getIndex());
|
||||
headWriteCellStyle.setWriteFont(headWriteFont);
|
||||
// 下载
|
||||
FastExcel.write(response.getOutputStream(), head)
|
||||
.autoCloseStream(Boolean.FALSE)
|
||||
|
|
@ -49,6 +58,7 @@ public final class SmartExcelUtil {
|
|||
.doWrite(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通用单 sheet水印 导出
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue