关于导出速度慢的优化处理

这是一个关于优化批量导出收款记录功能的Java代码实现,使用了Spring Boot、MyBatis和Hutool库。代码中通过PageHelper进行分页查询,并利用线程池并发处理大量数据,提高导出效率。同时,代码还涉及到了字典数据的查询、业务逻辑处理和Excel文件的生成。主要功能包括:查询条件组装、多线程分页查询、数据转换、Excel导出以及日志记录。
摘要由CSDN通过智能技术生成

优化了之前的导出收款功能。贴出代码仅供参考。有大神提出问题我会虚心接受。



import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author 刘能
 * @since 2021/2/2/9:17
 */
@Service
@Slf4j
public class ExportPayRecordServiceImpl implements ExportPayRecordService {

    @Resource
    private ProductCampusService productCampusService;
    @Resource
    private ExportPayRecordSearchMapper exportPayRecordSearchMapper;
    @Resource
    private ExportFinanceService exportFinanceService;
    @Resource
    private ExcelExport excelExport;

    @Resource
    private DictService dictService;
    @Resource
    private AuthorizeFeignService authorizeFeignService;
    @Resource
    private OrderRefundDetailMapper refundMapper;
    @Resource
    private BusinessOwnerMapper businessOwnerMapper;

    @Resource
    private UserInfoService userInfoService;
    @Resource
    private CommonOperateLogsMapper commonOperateLogsMapper;

    private static final Integer COUNT = 2000;
    @Value("${cis.product_app_id}")
    private String productAppId;

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(20, 20,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadFactoryBuilder().setNameFormat("export-pay-record-info-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 新导出收款记录接口
     * @param request request
     * @param response response
     */
    @Override
    public void payRecord(HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> requestParams = this.requestParams(request);
        PageReq req = new PageReq();
        req.setSearch(requestParams);
        UserContext userContext = UserThreadLocal.get();

        PageHelper.clearPage();
        PageHelper.startPage(1, 1);
        req.setPage(1);
        req.setPageNum(1);
        log.info("查询开始时间: {}", System.currentTimeMillis());
        List<ExportPayRecordVo> toExportPayRecordVoListPage1 = exportPayRecordSearchMapper.payRecords(req);

        this.logRecord(userContext, requestParams);

        PageInfo<ExportPayRecordVo> pageInfo = new PageInfo<>(toExportPayRecordVoListPage1);
        int totalCount = pageInfo.getPages();
        BigDecimal totalPageB = new BigDecimal(totalCount).divide(new BigDecimal(2000), BigDecimal.ROUND_CEILING);
        int totalPage = totalPageB.intValue();

        CountDownLatch searchCount = new CountDownLatch(totalPage);
        AtomicInteger count = new AtomicInteger(0);
        List<ExportPayRecordVo> toExportPayRecordVoList = Collections.synchronizedList(new ArrayList<>());
        for (int a = 0; a < totalPage; a ++){
            AtomicInteger page = new AtomicInteger(a + 1);
            EXECUTOR.execute(() -> {
                try {
                    PageHelper.clearPage();
                    PageHelper.startPage(page.intValue(), COUNT);
                    req.setPage(page.intValue());
                    req.setPageNum(COUNT);
                    List<ExportPayRecordVo> exportPayRecordVosPage = exportPayRecordSearchMapper.payRecords(req);
                    toExportPayRecordVoList.addAll(exportPayRecordVosPage);
                    count.addAndGet(1);
                }catch (Exception e){
                    e.printStackTrace();
                    throw new RestException(Constant.THREAD_EXCEPTION, Constant.THREAD_EXCEPTION_MESSAGE);
                }finally {
                    searchCount.countDown();
                }
            });
        }

        try {
            if (!searchCount.await(ExportConstant.SEARCH_WAIT_TIME, TimeUnit.SECONDS)){
                log.info("主线程等待子线程时间过长!");
                throw new Exception();
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RestException(Constant.EXPORT_TIME_OUT, Constant.EXPORT_TIME_OUT_MESSAGE);
        }

        if (count.get() != totalPage){
            log.error("有线程查询报错: 总页数:{} ,查询页数;{}", totalPage, count);
            throw new RestException(Constant.THREAD_EXCEPTION, Constant.THREAD_EXCEPTION_MESSAGE);
        }

        log.info("查询结束时间: {}", System.currentTimeMillis());

        List<ExportPayRecordVo> list = new ArrayList<>();
        // 只提取内部挂账跟外部挂账导出数据
        String accountAmount = request.getParameter("accountAmount");
        if(ExportConstant.ZERO.equals(accountAmount)){
            List<ExportPayRecordVo> toExportPayRecordVos = toExportPayRecordVoList.stream().filter(toExportPayRecordVo -> null != toExportPayRecordVo.getAccountAmountId()).collect(Collectors.toList());
            list.addAll(toExportPayRecordVos);
        }else{
            list.addAll(toExportPayRecordVoList);
        }

        BigDecimal totalAmount = list.stream().map(ExportPayRecordVo::getRecordPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        LinkedHashMap<String, String> titleMap = this.recordTitleMap();

        ExcelPageUtil excelPageUtil = new ExcelPageUtil();

        BigDecimal total = new BigDecimal(list.size());
        BigDecimal divide = total.divide(new BigDecimal(COUNT), BigDecimal.ROUND_CEILING);
        CountDownLatch begin = new CountDownLatch(divide.intValue());

        Map<String, Map<Long, Object>> feignSearchMap = this.feignSearch(userContext, req);
        log.info("处理导出数据开始时间: {}", System.currentTimeMillis());

        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            byte[] content = os.toByteArray();
            InputStream is = new ByteArrayInputStream(content);
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);

            ThreadConfig threadConfig = new ThreadConfig();
            Executor executor = threadConfig.getAsyncExecutor();
            List<ExportPayRecordVo> resultList = Collections.synchronizedList(Lists.newArrayList());
            Map<String, String> map = exportFinanceService.listBusiness();

            AtomicInteger excelCount = new AtomicInteger(0);
            for (int num = 0; num < divide.intValue(); num ++) {
                List<ExportPayRecordVo> toExportPayRecordVos = excelPageUtil.handlerListPage(list, num + 1, COUNT);
                executor.execute(() -> {
                    try {
                        UserThreadLocal.set(userContext);
                        JSONArray ja = new JSONArray();
                        this.excelPayRecordChinese(toExportPayRecordVos, ja, feignSearchMap, map);
                        List<ExportPayRecordVo> threadList = ja.toJavaList(ExportPayRecordVo.class);
                        resultList.addAll(threadList);
                        excelCount.addAndGet(1);
                    }catch (Exception e){
                        e.printStackTrace();
                        log.error("处理数据错误信息:{}" + e.getMessage());
                        throw new RestException(Constant.THREAD_DEAL_EXCEPTION, Constant.THREAD_DEAL_EXCEPTION_MESSAGE);
                    }finally {
                        begin.countDown();
                    }
                });
            }

            if (!begin.await(ExportConstant.WAIT_TIME, TimeUnit.SECONDS)){
                log.info("主线程等待子线程时间过长!");
                throw new RestException(Constant.EXPORT_TIME_OUT, Constant.EXPORT_TIME_OUT_MESSAGE);
            }
            log.info("处理导出数据结束时间: {}", System.currentTimeMillis());

            if (excelCount.get() != divide.intValue()){
                log.error("有线程导出报错: 总页数:{} ,查询页数;{}", divide.intValue(), excelCount);
                throw new RestException(Constant.THREAD_DEAL_EXCEPTION, Constant.THREAD_DEAL_EXCEPTION_MESSAGE);
            }

            log.info("导出开始时间: {}", System.currentTimeMillis());
            excelExport.exportExcel("收款记录导出", titleMap, JSONObject.parseArray(JSONObject.toJSONString(resultList)), response, totalAmount);
            log.info("导出结束时间: {}", System.currentTimeMillis());
            bis.close();
            bos.close();
            outputStream.flush();
            outputStream.close();
        }catch (RestException e){
            if (Constant.EXPORT_TIME_OUT.equals(e.getCode())){
                throw new RestException(Constant.EXPORT_TIME_OUT, Constant.EXPORT_TIME_OUT_MESSAGE);
            }
            if (Constant.THREAD_DEAL_EXCEPTION.equals(e.getCode())){
                throw new RestException(Constant.THREAD_DEAL_EXCEPTION, Constant.THREAD_DEAL_EXCEPTION_MESSAGE);
            }
        }catch (Exception e){
            log.error("新导出收款记录接口报错; {}", e.getMessage());
            throw new RestException(Constant.EXPORT_PAY_FAIL, Constant.EXPORT_PAY_FAIL_MESSAGE);
        }
    }

    /**
     * 操作日志记录
     * @param userContext userContext
     * @param requestParams requestParams
     */
    private void logRecord(UserContext userContext, Map<String, Object> requestParams) {
        IdGenerator idGenerator = new IdGenerator();
        //生成流水号记录一次操作留下记录的标识
        String serialNumber = GuidGenerator.generateUUID();

        CommonOperateLogs commonOperateLogs = new CommonOperateLogs();
        commonOperateLogs.setId(idGenerator.nextId());
        commonOperateLogs.setOperateOpenId(userContext.getOpenId());
        commonOperateLogs.setOperateName(userContext.getName());
        commonOperateLogs.setOrgId(Long.parseLong(userContext.getOrgId()));
        commonOperateLogs.setLogSource("EXPORT_PAY_RECORD");
        commonOperateLogs.setLogSourceDetail("导出收款记录");
        commonOperateLogs.setLogJson("导出条件: " + JSONObject.toJSONString(requestParams));
        commonOperateLogs.setCreateTime(Integer.parseInt(String.valueOf(System.currentTimeMillis() / 1000)));
        commonOperateLogs.setSerialNumber(serialNumber);

        commonOperateLogsMapper.insert(commonOperateLogs);
    }

    /**
     * 处理请求参数
     * @param request HttpServletRequest
     * @return Map
     */
    private Map<String, Object> requestParams(HttpServletRequest request){
        Map<String, Object> map = new HashMap<>(16);
        String timeBegin = request.getParameter("timeBegin");
        String timeEnd = request.getParameter("timeEnd");
        String payType = request.getParameter("payType");
        String payStatus = request.getParameter("payStatus");
        String status = request.getParameter("status");
        String uopenId = request.getParameter("uopenId");
        String orgIds = request.getParameter("orgIds");
        String orgId = request.getParameter("orgId");

        UserContext userContext = new UserContext();

        map.put("uopenId", uopenId);
        userContext.setOpenId(uopenId);
        if (null != timeBegin && !"".equals(timeBegin)) {
            map.put("timeBegin", timeBegin);
        }
        if (null != timeEnd && !"".equals(timeEnd)) {
            map.put("timeEnd", timeEnd);
        }
        if (null != payType && !"".equals(payType)) {
            map.put("payType", payType);
        }
        if (null != payStatus && !"".equals(payStatus)) {
            map.put("payStatus", payStatus);
        }
        if (null != status && !"".equals(status)) {
            map.put("status", status);
        }
        if (null != orgId && !"".equals(orgId)) {
            map.put("mechanismId", orgId);
            userContext.setOrgId(orgId);
        }

        //查询当前人得所属机构
        ResultBean<UserNewVO> userInfo = userInfoService.getUserInfoByOpenId(productAppId, uopenId);

        List<Long> orgIdLists = new ArrayList<>();
        log.info(ExportConstant.USER_INFO, JSONObject.toJSONString(userInfo));
        if (null != userInfo){
            UserNewVO data = userInfo.getData();
            userContext.setName(data.getUserName());
            CopyUtil.copyProperties(userContext, data);
            orgIdLists = data.getOrgList().stream().map(OrgVO::getId).collect(Collectors.toList());
        }
        List<Long> orgIdList;
        if (null != orgIds && !"".equals(orgIds)) {
            orgIdList = Arrays.stream(orgIds.split(","))
                    .map(s ->Long.parseLong(s.trim())).collect(Collectors.toList());

            //求机构交集
            List<Long> orgParam = new ArrayList<>(orgIdList);
            orgIdLists.retainAll(orgParam);
        }else{
            orgIdList = new ArrayList<>(orgIdLists);
        }
        map.put("orgIds", orgIdList);

        String lineIdStr = request.getParameter("productLineIds");
        if (StringUtils.isNotBlank(lineIdStr)) {
            List<String> productLineIds = Arrays.asList(lineIdStr.split(","));
            map.put("productLineIds", productLineIds);
            List<Long> orderIdList = exportPayRecordSearchMapper.listOrderIdByProductLineIds(map);
            map.put("orderIdList", orderIdList);
        }

        ResultBean<List<Long>> campusList = productCampusService.listProductCampusByOpenIdAndOrgIdList(uopenId, orgIdList);
        log.info(ExportConstant.USER_CAMPUS_ID, JSONObject.toJSONString(campusList));
        map.put("campusIds", campusList.getData());

        UserThreadLocal.set(userContext);

        return map;
    }

    private BigDecimal keepTwoDigits(BigDecimal price) {
        if (null != price) {
            price = price.setScale(2, BigDecimal.ROUND_HALF_UP);
        } else {
            price = new BigDecimal(0);
        }

        return price;
    }

    private LinkedHashMap<String, String> recordTitleMap() {
        LinkedHashMap<String, String> headMap = new LinkedHashMap<>();
        headMap.put("orderNo", "订单编号");
        headMap.put("payId", "收款流水号");
        headMap.put("payTime", "收款时间");
        headMap.put("receiveSupplierName", "收款商户");
        headMap.put("accountType", "收款类型");
        headMap.put("payMethod", "收款方式");
        headMap.put("recordPayAmount", "收费金额");
        headMap.put("payStatus", "收款状态");
        headMap.put("invoiceNo", "收据单号");
        headMap.put("documentNo", "发票单号");
        headMap.put("accountAmount", "结算金额");
        headMap.put("customerName", "客户");
        headMap.put("commissionAmount", "佣金/代理费");
        headMap.put("earnestAmount", "预付款抵扣-实时");
        headMap.put("changeAmount", "暂存抵扣-实时");
        headMap.put("orderStatus", "订单状态");
        headMap.put("orderSource", "订单来源");
        headMap.put("gatherCompleteTime", "收款完成时间");
        headMap.put("refundCompleteTime", "退费完成时间");
        headMap.put("agreementFee", "保过预留金");
        headMap.put("businessOwnerVOrate", "业绩归属");
        headMap.put("campusName", "业绩校区");
        headMap.put("campusLeaderName", "业绩校区负责人");
        headMap.put("campusCostCenterName", "业绩校区成本中心");
        headMap.put("classCampusName", "上课校区");
        headMap.put("classCostCenterName", "上课校区成本中心");
        headMap.put("shopName", "报名点");
        headMap.put("leaderName", "报名点负责人");
        headMap.put("productLineName", "业务线");
        headMap.put("payedType", "收款类型");
        headMap.put("payWay", "支付渠道");
        headMap.put("feeNo", "收据号");
        headMap.put("approverName", "审批人");
        headMap.put("approveTime", "审批时间");
        headMap.put("status", "审核状态");
        headMap.put("approveRemark", "审批备注");
        headMap.put("arrivalAccountDateTime", "到账时间");
        headMap.put("payRecordRemark", "收款备注");
        headMap.put("productNames", "产品名称");
        headMap.put("productType", "商品类型");
        headMap.put("productCategoryName", "产品分类");
        headMap.put("name", "学员姓名");
        headMap.put("code", "学员学号");
        headMap.put("year", "考研届数");
        headMap.put("mobile", "学员手机号");
        headMap.put("idCard", "身份证号");
        headMap.put("gradeStr", "年级");
        headMap.put("qq", "QQ号码");
        headMap.put("nowSchool", "就读院校");
        headMap.put("nowMajor", "就读专业");
        headMap.put("aimMajor", "目标专业");
        headMap.put("source", "学员来源");
        headMap.put("customerRemark", "学员备注");
        headMap.put("orderOwnerName", "咨询师");
        headMap.put("remark", "订单备注");
        headMap.put("recommendPeople", "订单推荐人");
        headMap.put("orderClassification", "订单类型");
        headMap.put("guestName", "客户");
        headMap.put("approveUserName", "财务核对");
        headMap.put("approveClockTime", "核对时间");
        headMap.put("crmCreateDate", "CRM学员创建时间");
        headMap.put("agentPeople", "代理人");
        headMap.put("agentYear", "代理年份");
        headMap.put("intention", "学员意向");
        headMap.put("orderCreateTime", "订单创建时间");
        headMap.put("orgName", "机构名称");

        return headMap;
    }

    private <T> void turnObject(Map<Long, T> t, String title, Map<String, Map<Long, Object>> resultMap){
        resultMap.put(title, t.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
    }

    /**
     * 导出用的字典等集合
     * @param userContext userContext
     * @param pageReq pageReq
     * @return Map
     */
    private Map<String, Map<Long, Object>> feignSearch(UserContext userContext, PageReq pageReq){
        PageReq req = new PageReq();
        Map<String, Object> searchMap = new HashMap<>(5);
        Map<String, Map<Long, Object>> resultMap = new HashMap<>(8);

        String orgId = userContext.getOrgId();
        searchMap.put("mechanismId", orgId);
        searchMap.put("dictCode", ExportConstant.STUDENTSSOURCE);
        // 让删除的学员来源可查
        searchMap.put("dr", ExportConstant.TWO);
        req.setSearch(searchMap);

        ResultBean<List<DictsVo>> resultBean = dictService.searchDict(req);
        if (resultBean != null && resultBean.getCode() == 0 && resultBean.getData() != null && resultBean.getData().size() > 0) {
            turnObject(resultBean.getData().stream().collect(Collectors.toMap(DictsVo::getId, JSONObject::toJSONString)),
                    "dictMap", resultMap);
        }

        // 机构信息
        ResultBean<List<OrgVO>> orgResult = authorizeFeignService.getOrgInfoAll();
        if (orgResult != null && orgResult.getCode() == 0 && orgResult.getData() != null && orgResult.getData().size() > 0) {
            turnObject(orgResult.getData().stream().collect(Collectors.toMap(OrgVO::getId, JSONObject::toJSONString)),
                    "orgMap", resultMap);
        }

        // 校区报名点信息
        ResultBean<List<ProductCampusDto>> campusEnterByOrg = productCampusService.getCampusEnterByOrg(Long.parseLong(orgId));
        if (null != campusEnterByOrg && null != campusEnterByOrg.getData() && 0 < campusEnterByOrg.getData().size()){
            turnObject(campusEnterByOrg.getData().stream().collect(Collectors.toMap(ProductCampusDto::getId, JSONObject::toJSONString)),
                    "campusMap", resultMap);
        }

        // 年级
        ResultBean<List<DictsVo>> gradeResultBean = dictService.listDictByType(ExportConstant.GRADE, orgId);
        if (null != gradeResultBean && null != gradeResultBean.getData()) {
            List<DictsVo> dictList = gradeResultBean.getData();
            if (!CollectionUtils.isEmpty(dictList)) {
                turnObject(dictList.stream().collect(Collectors.toMap(DictsVo::getId, DictsVo::getDictValue)),
                        "gradeMap", resultMap);
            }
        }

        // 订单来源
        ResultBean<List<DictsVo>> orderSourceResultBean = dictService.selectIdByDictTypeCode(ExportConstant.ORDERSOURCE);
        if (null != orderSourceResultBean && null != orderSourceResultBean.getData() && 0 < orderSourceResultBean.getData().size()) {
            turnObject(orderSourceResultBean.getData().stream().collect(Collectors.toMap(DictsVo::getId, DictsVo::getDictValue)),
                    "sourceMap", resultMap);
        }

        // 咨询师
        List<BusinessOwner> businessOwnerList = businessOwnerMapper.listOwnewByOrderBuyIdsPayRecord(pageReq);
        if (businessOwnerList.size()>0){
            turnObject(businessOwnerList.stream().collect(Collectors.groupingBy(BusinessOwner::getOrderBuyId)),
                    "businessOwnerMap", resultMap);
        }

        return resultMap;
    }

    /**
     * 处理导出数据
     * @param exportPayRecordVos 结果集
     * @param ja JSONArray
     * @param feignSearchMap 导出用的字典等集合
     * @param map 调用配置中心接口返回值
     */
    private void excelPayRecordChinese(List<ExportPayRecordVo> exportPayRecordVos, JSONArray ja, Map<String, Map<Long, Object>> feignSearchMap, Map<String, String> map){
        Map<String, String> gatherCompleteTimeCollect = exportPayRecordVos.stream().collect(Collectors.toMap(ExportPayRecordVo::getOrderNo,
                ExportPayRecordVo::getUnpayTime,
                (String time1, String time2) -> {
                    if (Integer.valueOf(time1) > Integer.valueOf(time2)) {
                        return time1;
                    } else {
                        return time2;
                    }
                }));
        List<ExportPayRecordVo> collect = exportPayRecordVos.stream().filter(a -> "6".equals(a.getOrderStatus())).collect(Collectors.toList());
        List<Long> refundIds = collect.stream().map(ExportPayRecordVo::getId).collect(Collectors.toList());
        List<OrderRefundDetail> orderRefundDetails;
        Map<Long, Integer> refundTimeCollect = new HashMap<>(20);
        if (0 < refundIds.size()){
            orderRefundDetails = refundMapper.refundByBuyIds(refundIds);
            refundTimeCollect = orderRefundDetails.stream().collect(Collectors.toMap(OrderRefundDetail::getOrderBuyId,
                    orderRefundDetail -> orderRefundDetail.getRefundTime() == null ? Integer.valueOf(String.valueOf(orderRefundDetail.getCreateTime().getTime() / 1000))
            : orderRefundDetail.getRefundTime(),
                    (entity1, entity2) -> entity1));
        }

        for (ExportPayRecordVo vo : exportPayRecordVos) {
            switch (vo.getStatus() == null ? "" : vo.getStatus()) {
                case ExportConstant.ZERO:
                    vo.setStatus("支付记录已确认");
                    break;
                case ExportConstant.ONE:
                    vo.setStatus("线下支付审核中");
                    break;
                case ExportConstant.TWO:
                    vo.setStatus("支付拒绝");
                    break;
                case ExportConstant.NINE:
                    vo.setStatus("初始化");
                    break;
                default:
                    vo.setStatus("");
                    break;
            }

            switch (vo.getAccountType() == null ? "" : vo.getAccountType()){
                case ExportConstant.ZERO:
                    vo.setAccountType("内部挂账");
                    break;
                case ExportConstant.ONE:
                    vo.setAccountType("外部挂账");
                    break;
                case ExportConstant.TWO:
                    vo.setAccountType("普通收款");
                    break;
                default:
                    vo.setAccountType("");
                    break;
            }

            Long id = vo.getId();
            Map<Long, Object> businessOwnerMap = feignSearchMap.get("businessOwnerMap");
            List<BusinessOwner> businessOwners = JSONObject.parseArray(JSONObject.toJSONString(businessOwnerMap.get(id)), BusinessOwner.class);
            // 咨询师,业绩归属
            if (businessOwners!=null&&0 != businessOwners.size()){
                Map<String, Float> rates = new HashMap<>(4);
                for (BusinessOwner ownerVO : businessOwners){
                    if (0 < BigDecimal.valueOf(ownerVO.getRate()).compareTo(BigDecimal.valueOf(0))) {
                        // rate > 0
                        rates.put(ownerVO.getName(), ownerVO.getRate());
                    }
                }
                String businessOwnerVoRate = rates.toString();
                StringUtils.strip(businessOwnerVoRate, "[]");
                vo.setBusinessOwnerVOrate(businessOwnerVoRate.replace("=", ":"));
            }

            // 审批备注
            if (null == vo.getApproveRemark() || "".equals(vo.getApproveRemark())) {
                vo.setApproveRemark("");
            }

            vo.setPayMethod("");
            Map<Long, Object> orgMap = feignSearchMap.get("orgMap");
            OrgVO orgVO = JSONObject.parseObject(String.valueOf(orgMap.get(vo.getOrgId())), OrgVO.class);
            vo.setOrgName(orgVO.getName());
            if (ExportConstant.EIGHTEEN.equals(vo.getPayType())) {
                vo.setPayMethod(vo.getPayWay() == null ? "" : vo.getPayWay());
                // 机构账户id
                String supplierId = orgVO.getCode();
                // 判断校区账户
                if (orgVO.getAdminAgenciesVOList() != null && orgVO.getAdminAgenciesVOList().size() > 0) {
                    Map<Long, Object> campusMap = feignSearchMap.get("campusMap");
                    ProductCampusDto productCampusDto = JSONObject.parseObject(String.valueOf(campusMap.get(vo.getCampusId())), ProductCampusDto.class);
                    if (productCampusDto != null && productCampusDto.getAccountId() != null) {
                        Map<Long, AdminAgenciesVO> adminMap = orgVO.getAdminAgenciesVOList().stream().collect(Collectors.toMap(AdminAgenciesVO::getId, a -> a, (k1, k2) -> k1));
                        if (adminMap.get(productCampusDto.getAccountId()) != null && StringUtils.isNotBlank(adminMap.get(productCampusDto.getAccountId()).getCode())) {
                            supplierId = adminMap.get(productCampusDto.getAccountId()).getCode();
                        }
                    }
                }
                if (StringUtils.isNotBlank(map.get(supplierId))) {
                    //离线收款的商户
                    vo.setReceiveSupplierName(map.get(supplierId));
                }
            } else {
                exportFinanceService.dealPayTypeDetails(vo);
                vo.setFeeNo(vo.getReceiptStreamlineNumber() == null ? "" : vo.getReceiptStreamlineNumber());
            }

            // 商品的类型
            Set<String> names = new HashSet<>();
            Set<String> lineNames = new HashSet<>();
            BigDecimal totalMakeSurePrice = new BigDecimal("0");
            List<OrderDetail> orderDetails = vo.getOrderDetails();
            if (null != orderDetails && 0 < orderDetails.size()) {
                // 其他商品处理
                List<String> types = new ArrayList<>();
                List<String> categoryName = new ArrayList<>();
                for (OrderDetail orderDetail : orderDetails) {
                    exportFinanceService.dealProductType(orderDetail, types);
                    if (null != orderDetail.getProductCategoryName() &&
                            !"".equals(orderDetail.getProductCategoryName())) {
                        categoryName.add(orderDetail.getProductCategoryName());
                    }

                    if (null != orderDetail.getMakeSurePrice()) {
                        totalMakeSurePrice = totalMakeSurePrice.add(orderDetail.getMakeSurePrice());
                    }
                    if (null != orderDetail.getProductLineName()){
                        lineNames.add(orderDetail.getProductLineName());
                    }

                    String pn = orderDetail.getProductName();
                    String year = orderDetail.getYear();
                    names.add(year + pn);
                }
                vo.setProductType(StringUtils.strip(types.toString(), "[]"));
                // 产品分类名称
                vo.setProductCategoryName(StringUtils.strip(categoryName.toString(), "[]"));
                // 产品名称
                if (names.size() > 0) {
                    vo.setProductNames(StringUtils.strip(names.toString(), "[]"));
                }
            }
            // 保过预留金
            vo.setAgreementFee(this.keepTwoDigits(totalMakeSurePrice).toString());
            if (lineNames.size() > 0) {
                vo.setProductLineName(StringUtils.strip(String.valueOf(lineNames), "[]"));
            }

            // 支付时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            if (StringUtils.isNotBlank(vo.getPayTime())) {
                if (10 == vo.getPayTime().length()) {
                    Date date = new Date(Long.parseLong(vo.getPayTime()) * 1000);
                    vo.setPayTime(sdf.format(date));
                } else {
                    vo.setPayTime(DateUtil.format(DateUtil.parse(vo.getPayTime()), "yyyy-MM-dd"));
                }
            }
            
            // 核对时间
            if (null != vo.getApproveClock()) {
                Date date = new Date(vo.getApproveClock() * 1000L);
                vo.setApproveClockTime(sdf.format(date));
            }

            if (vo.getCrmCreateTime()!=null){
                Date date = new Date(vo.getCrmCreateTime() * 1000L);
                vo.setCrmCreateDate(sdf.format(date));
            }

            // 订单状态
            String orderStatus = vo.getOrderStatus();
            switch (orderStatus == null ? "" : orderStatus){
                case ExportConstant.ZERO:
                    vo.setOrderStatus("未支付");
                    break;
                case ExportConstant.ONE:
                    vo.setOrderStatus("支付完成");

                    Integer time = Integer.valueOf(gatherCompleteTimeCollect.get(vo.getOrderNo()));
                    Date date = new Date(time * 1000L);
                    vo.setGatherCompleteTime(sdf.format(date));
                    break;
                case ExportConstant.TWO:
                    vo.setOrderStatus("部分付款");
                    break;
                case ExportConstant.THREE:
                    vo.setOrderStatus("现金支付待审批");
                    break;
                case ExportConstant.FOUR:
                    vo.setOrderStatus("取消");
                    break;
                case ExportConstant.FIVE:
                    vo.setOrderStatus("现金支付驳回审批");
                    break;
                case ExportConstant.SIX:
                    vo.setOrderStatus("已退");

                    // 订单退费完成(不欠费)的时间
                    Integer refundTime = refundTimeCollect.get(vo.getId());
                    if (refundTimeCollect.containsKey(vo.getId()) && 0 != refundTime){
                        Date refundDate = new Date(refundTime * 1000L);
                        vo.setRefundCompleteTime(sdf.format(refundDate));
                    }
                    break;
                case ExportConstant.SEVEN:
                    vo.setOrderStatus("支付中");
                    break;
                case ExportConstant.EIGHT:
                    vo.setOrderStatus("已转让");
                    break;
                case ExportConstant.NINE:
                    vo.setOrderStatus("已更换");
                    break;
                default:
                    vo.setOrderStatus("");
                    break;
            }

            // 收款状态 1-成功 0-未成功 2-审核中
            String payStatus = vo.getPayStatus();
            switch (payStatus == null ? "" : payStatus){
                case ExportConstant.ZERO:
                    vo.setPayStatus("未成功");
                    break;
                case ExportConstant.ONE:
                    vo.setPayStatus("成功");
                    break;
                case ExportConstant.TWO:
                    vo.setPayStatus("审核中");
                    break;
                default:
                    vo.setPayStatus("");
                    break;
            }

            // 收费方式 payedType 0-线下支付 1-线上支付
            String payType = vo.getPayType();
            String payedType = vo.getPayedType();
            if (ExportConstant.ZERO.equals(payedType) || ExportConstant.ONE.equals(payType) || ExportConstant.TWO.equals(payType)) {
                String unPayTime = vo.getUnpayTime();
                if (null != unPayTime && !"".equals(unPayTime)) {
                    long l = Long.parseLong(unPayTime + "000");
                    Date date = new Date(l);
                    vo.setPayTime(sdf.format(date));
                }

                vo.setPayedType("离线");
                // 离线收款时间
                if (null != vo.getArrivalAccountDate()) {
                    Date date = new Date(vo.getArrivalAccountDate() * 1000L);
                    String time = sdf.format(date);
                    vo.setArrivalAccountDateTime(time);
                }
            } else if (ExportConstant.ONE.equals(payedType)) {
                vo.setPayedType("在线");
                vo.setArrivalAccountDateTime(vo.getTransferDate());
            } else if (null == payedType) {
                vo.setPayedType("");
            }

            // 身份证号处理
            String idCard = vo.getIdCard();
            if (null != idCard && !"".equals(idCard)) {
                String setIdCard = idCard.substring(0, 6) + "********" + idCard.substring(14);
                vo.setIdCard(setIdCard);
            }
            // 手机号处理
            String mobile = vo.getMobile();
            if (null != mobile && !"".equals(mobile)) {
                String setMobile = mobile.substring(0, 3) + "****" + mobile.substring(7);
                vo.setMobile(setMobile);
            }

            // 订单创建时间
            String orderCreateTime = vo.getOrderCreateTime();
            if (null != orderCreateTime && !"".equals(orderCreateTime)) {
                Date date = new Date(Long.parseLong(orderCreateTime + "000"));
                vo.setOrderCreateTime(sdf.format(date));
            }

            // 学员意向(0-高端、1-低端、2-不明确)
            switch (vo.getIntention() == null ? "" : vo.getIntention()){
                case ExportConstant.ZERO:
                    vo.setIntention("高端");
                    break;
                case ExportConstant.ONE:
                    vo.setIntention("低端");
                    break;
                case ExportConstant.TWO:
                    vo.setIntention("不明确");
                    break;
                default:
                    vo.setIntention("");
                    break;
            }

            // 学员来源
            String source = vo.getSource();
            if (!"".equals(source) && null != source) {
                Map<Long, Object> dictMap = feignSearchMap.get("dictMap");
                DictsVo dictsVo = JSONObject.parseObject(String.valueOf(dictMap.get(Long.parseLong(source))), DictsVo.class);
                if (null != dictMap.get(Long.parseLong(source))) {
                    vo.setSource(dictsVo.getDictValue());
                }
            }

            String orderClassification = vo.getOrderClassification();
            switch(orderClassification == null ? "" : orderClassification){
                case "0":
                    vo.setOrderClassification("内部赊销");
                    break;
                case "1":
                    vo.setOrderClassification("外部赊销");
                    break;
                case "2":
                    vo.setOrderClassification("现销订单");
                    break;
                default:
                    vo.setOrderClassification("");
                    break;
            }

            // 年级
            if (null != vo.getGrade()){
                Map<Long, Object> gradeMap = feignSearchMap.get("gradeMap");
                vo.setGradeStr(String.valueOf(gradeMap.get(vo.getGrade())));
            }

            // 财务审核 0-未审核 1-审核通过 2-审核驳回
            switch (vo.getFinanceCheck() == null ? "" : vo.getFinanceCheck()){
                case "0":
                    vo.setFinanceCheck("未审核");
                    break;
                case "1":
                    vo.setFinanceCheck("审核通过");
                    break;
                case "2":
                    vo.setFinanceCheck("审核驳回");
                    break;
                default:
                    vo.setFinanceCheck("");
                    break;
            }
            
            // 审核时间
            if (null != vo.getCheckTime()) {
                Date date = new Date(Long.parseLong(vo.getCheckTime() + "000"));
                vo.setCheckTimeStr(sdf.format(date));
            }

            // 订单来源
            Map<Long, Object> sourceMap = feignSearchMap.get("sourceMap");
            if (sourceMap.containsKey(vo.getOrderSourceFirstId()) && sourceMap.containsKey(vo.getOrderSourceSecondId())) {
                vo.setOrderSource(sourceMap.get(vo.getOrderSourceFirstId()) + " / " + sourceMap.get(vo.getOrderSourceSecondId()));
            }

            vo.setClassCampusName(vo.getCampusName());

            vo.setRecordPayAmount(this.keepTwoDigits(vo.getRecordPayAmount()));
            vo.setEarnestAmount(this.keepTwoDigits(vo.getEarnestAmount()));
            vo.setChangeAmount(this.keepTwoDigits(vo.getChangeAmount()));
            vo.setAccountAmount(this.keepTwoDigits(vo.getAccountAmount()));
            vo.setCommissionAmount(this.keepTwoDigits(vo.getCommissionAmount()));

            ja.add(vo);
        }
    }



}

下面是导出用的poi工具类。网上有很多



import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * @author 刘能
 * @since 2021/2/3/14:05
 */
@Component
@Slf4j
public class ExcelExport {

    public static String DEFAULT_DATE_PATTERN="yyyy年MM月dd日";
    public static int DEFAULT_COLUMN_WIDTH = 17;


    public void exportExcel(String title, Map<String,String> headMap, JSONArray ja, HttpServletResponse response, BigDecimal totalAmount){
        log.info("导出数据开始****************************");
        try {

            ByteArrayOutputStream os = new ByteArrayOutputStream();
            exportExcelXPayRecord(title, headMap, ja,null,0, os, totalAmount);

            response.reset();
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode("收款记录导出.xlsx","UTF-8"));

            byte[] content = os.toByteArray();
            InputStream is = new ByteArrayInputStream(content);
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);

            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            bis.close();
            bos.close();
            outputStream.flush();
            outputStream.close();
        }catch (Exception e) {
            e.printStackTrace();
            log.error("导出收款记录失败的错误信息: {}", e.getMessage());
            throw new RestException(Constant.EXPORT_PAY_FAIL, Constant.EXPORT_PAY_FAIL_MESSAGE);
        }
    }

    /**
     * 导出Excel 2007 OOXML (.xlsx)格式
     * @param title 标题行
     * @param headMap 属性-列头
     * @param jsonArray 数据集
     * @param datePattern 日期格式,传null值则默认 年月日
     * @param colWidth 列宽 默认 至少17个字节
     * @param out 输出流
     */
    public void exportExcelXPayRecord(String title,Map<String, String> headMap,JSONArray jsonArray,String datePattern,int colWidth, OutputStream out,
                                      BigDecimal totalAmount) {
        if(datePattern==null){
            datePattern = DEFAULT_DATE_PATTERN;
        }
        // 声明一个工作薄
        SXSSFWorkbook workbook = new SXSSFWorkbook(1000);//缓存
        workbook.setCompressTempFiles(true);
        //表头样式
        CellStyle titleStyle = workbook.createCellStyle();
        titleStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        Font titleFont = workbook.createFont();
        titleFont.setFontHeightInPoints((short) 20);
        titleFont.setBoldweight((short) 700);
        titleStyle.setFont(titleFont);
        // 列头样式
        CellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        headerStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        headerStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        headerStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        headerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        Font headerFont = workbook.createFont();
        headerFont.setFontHeightInPoints((short) 12);
        headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        headerStyle.setFont(headerFont);
        // 单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        Font cellFont = workbook.createFont();
        cellFont.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
        cellStyle.setFont(cellFont);
        // 生成一个(带标题)表格
        Sheet sheet = workbook.createSheet();
        //workbook.setSheetName(sheetNum, sheetTitle);
        //设置列宽
        int minBytes = colWidth<DEFAULT_COLUMN_WIDTH ? DEFAULT_COLUMN_WIDTH:colWidth;//至少字节数
        int[] arrColWidth = new int[headMap.size()];
        // 产生表格标题行,以及设置列宽
        String[] properties = new String[headMap.size()];
        String[] headers = new String[headMap.size()];
        int ii = 0;
        for (Iterator<String> iter = headMap.keySet().iterator(); iter
                .hasNext();) {
            String fieldName = iter.next();

            properties[ii] = fieldName;
            headers[ii] = headMap.get(fieldName);

            int bytes = fieldName.getBytes().length;
            arrColWidth[ii] =  bytes < minBytes ? minBytes : bytes;
            sheet.setColumnWidth(ii,arrColWidth[ii]*256);
            ii++;
        }
        // 遍历集合数据,产生数据行
        int rowIndex = 0;
        // 不分sheet页
        Row headerRow = sheet.createRow(0); //列头 rowIndex =1
        for(int i=0;i<headers.length;i++)
        {
            headerRow.createCell(i).setCellValue(headers[i]);
            headerRow.getCell(i).setCellStyle(headerStyle);
        }
        rowIndex = 1;//数据内容从 rowIndex=2开始
        for (Object obj : jsonArray) {
            JSONObject jo = (JSONObject) JSONObject.toJSON(obj);
            Row dataRow = sheet.createRow(rowIndex);
            for (int i = 0; i < properties.length; i++)
            {
                Cell newCell = dataRow.createCell(i);
                Object o =  jo.get(properties[i]);
                String cellValue = "";
                if(o==null) cellValue = "";
                else if(o instanceof Date) cellValue = new SimpleDateFormat(datePattern).format(o);
                else if(o instanceof Float || o instanceof Double)
                    cellValue= new BigDecimal(o.toString()).setScale(2,BigDecimal.ROUND_HALF_UP).toString();
                else cellValue = o.toString();

                newCell.setCellValue(cellValue);
                newCell.setCellStyle(cellStyle);
            }
            rowIndex++;
        }
        Row dataRow = sheet.createRow(rowIndex);
        for (int i = 0; i < properties.length; i++)
        {
            Cell newCell = dataRow.createCell(i);
            String cellValue = "";

            if (0 == i){
                newCell.setCellValue("合计收款金额");
                newCell.setCellStyle(headerStyle);
            } else if (6== i){
                //
                newCell.setCellValue(String.valueOf(totalAmount));
                newCell.setCellStyle(headerStyle);
            }else {
                newCell.setCellValue(cellValue);
                newCell.setCellStyle(cellStyle);
            }
        }
        try {
            workbook.write(out);
            workbook.dispose();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

至于里面一些抛出异常之类的常量就不贴了。大概就是那么些东西。

还有一些service的方法返回类型是resultBean类型,是使用的feign微服务的调用。也不细说这里了,都是一些简单的查询。

最后感谢批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值