业务背景
在系统中有这么一个场景,前台页面上有这么一些数据,其中检测结果是通过api调用生成的,每次检测都要调用检测接口。
现在有个新需求,要导出页面上的数据为excel,也就是如下的导出按钮:
问题分析
考虑到该页面数据并不是直接通过查询数据库得到,而是由前端输入相应数据后,调用后台检测结果接口生成,如果在调用导出接口时,再次调用后台检测接口,无疑会导致导出接口性能下降,所以笔者通过思考得到以下解决方案:
方案一:
由于数据量不大,前台可以将页面所有数据进行传输到后台,再由后台导出接口直接生成表格后返回;
方案二:
前台在调用后台检测结果接口生成页面后,页面的数据已经是完整的,此时根本无需后台再做数据库之类的操作,可以直接利用开源的生成Excel的工具,前台直接导出;
方案三:
前台在调用后台检测结果接口生成页面后,后台将检测结果存入到redis中,并将redis的key返回给前台,前台将该key存入一个全局变量中,在调用导出接口时,将该全局变量传给后台,后台根据key查找到该缓存数据,将该数据生成表格后返回。
方案对比
方案一可能存在的问题是:在数据量过大的情况下,前台传输数据太多,在传输过程中有丢失的风险;
方案二是由纯js实现的,由于没有试过,对其中存在的风险不予置评,具体实现可以参考这个;
方案三中由于采用了缓存,可能出现的问题是:
如果单纯使用一个类似UUID来生成redis的key,不去考虑真实的业务场景,则调用数据生成接口中,前台多次发送相同的数据,却生成了不同的缓存,这显然是不符合业务逻辑的,也会给系统造成很多垃圾数据。
针对这个问题,我的解决方案是:
在调用生成数据接口时,可以考虑在key上加入用户id,再加上特定的“export”字符,以及前台传入的条件,这样大概率就是唯一的redis id,再给前台返回这个key。
由于该数据是临时的,所以必须设置一个过期时间,将过期时间设置为24小时。
最终解决方案
方案一:纯js导出,该方案不需要后台做任何处理,简单易用;具体实现可以参考这个;
方案二:即上述方案三,利用redis来提高性能,伪代码如下
public GoodsTypeVO queryAndOperateData(GoodsTypeDTO goodsTypeDTO){
// 1、queryData from database
GoodsTypePO goodsTypePO = new GoodsTypePO();
//2、 operate data
//3、return data
GoodsTypeVO goodsTypeVO = new GoodsTypeVO();
String redisKey = userId + goodsTypeDTO.getGoodsTypeId();
Int expireTime = 24;
if (redis.get(redisKey).isEmpty()){
redis.set(goodsTypeVO,expireTime);
}
goodsTypeVO.setRedisKey(redisKey);
return goodsTypeVO;
}
public OutputStream getExcel(String redisKey){
List data = redis.get(redisKey).isEmpty();
if (StringUtils.isEmpty(redisKey)){
return null;
}else {
//excel export
return Excel.export(data);
}
}