上一篇写完了用注解实现异步导入,这一篇实现用注解异步导出。
1.背景:
导出最容易遇到的问题就是超时了,随着我们DB数据越来越多,导出时特别容易超时,这时候通常的解决办法就是优化SQL,或者直接限制导出条数,但是这两种方法都只能解决暂时的问题,过一段时间可能就又会冒出来。所以还是使用异步导出,先存到nas中,然后让用户再去下载。
2. 流程设计:
2.1 生成一条导出记录
2.2 异步查询并写入execl
2.3 把execl上传到nfsc,并更新导出记录
2.4 下载,从nfsc下载文件
3. 代码实现:
从上面的流程中可以看出,系统中只有第二步的不同的,其余都是一样的,所有我们还是可以用注解来实现通过流程。
3.1 创建自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Export {
//业务模块名称
String moduleName() default "";
//文件过期时间
int expireTime() default 24;
//excel解析类
Class clazz();
}
3.2 编写切面:
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.demo.cemp.framework.common.base.ResultUtil;
import com.demo.cemp.goods.common.utils.DateUtil;
import com.demo.cemp.goods.common.utils.FileUploadUtil;
import com.demo.cemp.goods.mallgoods.service.impl.ImportExportRecords;
import com.demo.framework.domain.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.concurrent.*;
/**
*/
@Component
@Aspect
@Slf4j
public class ExportAspect {
public static ThreadFactory commonThreadFactory = new ThreadFactoryBuilder().setNameFormat("upload-pool-%d")
.setPriority(Thread.NORM_PRIORITY).build();
public static ExecutorService uploadExecuteService = new ThreadPoolExecutor(1, 20, 300L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), commonThreadFactory, new ThreadPoolExecutor.AbortPolicy());
@Pointcut("@annotation(com.demo.cemp.goods.mallgoods.service.Export)")
public void exportPoint() {}
@Around(value = "exportPoint()")
public Object uploadControl(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法上的注解,进而获取uploadType
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Export annotation = signature.getMethod().getAnnotation(Export.class);
// 线程池启动异步线程,开始执行上传的逻辑,joinPoint.proceed()就是你实现的业务功能
uploadExecuteService.submit(() -> {
ImportExportRecords records = new ImportExportRecords();
try {
// 1、初始化导出日志,记录开始时间
records.setModuleName(annotation.moduleName());
records.setExpireTime(DateUtil.format(DateUtils.addHours(new Date(),annotation.expireTime()), DateUtil.DATE_TIME_PATTERN));
records.setType(2);
records.setStartTime(new Date());
// records= writeRecordsToDB(records);
System.out.println("writeInitToDB");
//2 执行业务方法 查询导出数据
Object result = joinPoint.proceed();
Result errorResult = JSON.parseObject(JSON.toJSONString(result), Result.class);
if (errorResult!=null && errorResult.isSuccess()) {
// 3 文件上传
String filePath = FileUploadUtil.getDefaultSavePath("businessCoupon", "demo.xlsx","test");
EasyExcel.write(filePath, annotation.clazz()).sheet("sheet").doWrite(JSON.parseArray(errorResult.getObj().toString(),annotation.clazz()));
records.setState(1);
records.setNfsPath(filePath);
} else {
// 失败,
records.setState(2);
records.setErrorReason(errorResult.getErrorMessage());
}
} catch (Throwable e) {
// 异常,需要记录
log.error("error",e);
records.setState(2);
records.setErrorReason(e.getMessage());
}
//4 更新导入日志结果
// updateByRecordsId(records);
System.out.println("updateByRecordsId");
});
return ResultUtil.success();
}
}
3.3 使用:
@Export(moduleName="test",clazz = MallOrderExcelVO.class)
public Result<List<MallOrderExcelVO>> exportOrderList(PointMallOrderDTO orderDTO) {
try {
List<MallOrderExcelVO> excelList = Lists.newArrayList();
int curPageNo = 1;
int pageSize = 500;
orderDTO.setPageSize(pageSize);
while (orderCount > 0) {
orderDTO.setPageNo(curPageNo++);
List<MallOrderPO> orderList2 = pointMallOrderService.getMallOrderList2(orderDTO);
if (orderList2 == null ) {
log.info("[商城订单管理]===请求失败 params={} curPageNo={}", JSON.toJSONString(orderDTO), curPageNo);
continue;
}
if (CollectionUtils.isEmpty(orderList2)) {
break;
}
excelList.addAll(orderList2.stream().map(this::getOrderExcelVO).collect(Collectors.toList()));
orderCount = Math.max(0, orderCount - pageSize);
}
return SFResultUtil.success(excelList);
} catch (Exception e) {
log.error("[商城订单管理]===导出订单异常", e);
}
return SFResultUtil.error("");
}
网友评论