es使用教程(v7)

`package com.rpqb.business.process.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
import com.rpqb.business.domain.App2LoanOrderInfo;
import com.rpqb.business.domain.LoanUser;
import com.rpqb.business.mapper.App2LoanOrderInfoMapper;
import com.rpqb.business.mapper.LoanUserMapper;
import com.rpqb.business.process.constants.ProcessConstants;
import com.rpqb.business.process.entity.ExportLoanPaymentPlan;
import com.rpqb.business.process.entity.LoanPaymentPlan;
import com.rpqb.business.process.entity.OverdueStat;
import com.rpqb.business.process.entity.RiskData;
import com.rpqb.business.process.service.IProcessActiveQueryService;
import com.rpqb.business.process.service.IProcessBusinessCreatePaymentPlanService;
import com.rpqb.common.constant.SysConstants;
import com.rpqb.common.enums.DataSourceTypeByDB;
import com.rpqb.common.utils.CommonUtil;
import com.rpqb.common.utils.DateUtils;
import com.rpqb.common.utils.SecretUtil;
import com.rpqb.common.utils.StringUtils;
import com.rpqb.common.utils.poi.ExcelUtil;
import com.rpqb.framework.datasource.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedCardinality;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProcessActiveQueryService {

/**
 * 删除es中的还款计划
 * @param loanOrderInfo
 */
private void deletePaymentPlansByOrder(App2LoanOrderInfo loanOrderInfo) {
    DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(REPAYMENT_PLAN_INDEX_NAME);
    //构建查询条件
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("order_no", loanOrderInfo.getAppOrderNo());
    boolQueryBuilder.must().add(termQueryBuilder);

    deleteByQueryRequest.setQuery(boolQueryBuilder);
    try {
        BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
        if (bulkByScrollResponse.getTotal() > 0) {
            log.info("【删除ES中还款计划】成功, 订单={}", loanOrderInfo.getAppOrderNo());
        }
    } catch (IOException e) {
        log.error("【删除ES中还款计划】失败, 订单={}, 异常={}", loanOrderInfo.getAppOrderNo(), e);
    }
}

/**
 * 修改es中的还款计划
 * @param paymentPlanList
 */
public void updatePaymentPlans(List<LoanPaymentPlan> paymentPlanList) {
    paymentPlanList.stream().forEach(a -> {
        UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(REPAYMENT_PLAN_INDEX_NAME);
        //构建查询条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("order_no", a.getOrderNo());
        boolQueryBuilder.must().add(termQueryBuilder);
        TermQueryBuilder indexNoTermQuery = QueryBuilders.termQuery("index_no", a.getIndexNo());
        boolQueryBuilder.must().add(indexNoTermQuery);
        updateByQueryRequest.setQuery(boolQueryBuilder);

        //构建修改的字段
        StringBuilder script = new StringBuilder()
                .append("ctx._source['repay_amount']='").append(a.getRepayAmount())
                .append("';ctx._source['repay_principal']='").append(a.getRepayPrincipal())
                .append("';ctx._source['already_repay_amount']='").append(a.getAlreadyRepayAmount())
                .append("';ctx._source['already_repay_principal']='").append(a.getAlreadyRepayPrincipal())
                .append("';ctx._source['already_repay_interest']='").append(a.getAlreadyRepayInterest())

        script.append("';ctx._source['actual_loan_give_time']='").append(a.getActualLoanGiveTime())
                .append("';ctx._source['loan_give_money']='").append(a.getLoanGiveMoney())
                .append("';ctx._source['office_loan_status']='").append(a.getOfficeLoanStatus())
                .append("';ctx._source['update_time']='").append(a.getUpdateTime())
                .append("'").append(";");

        updateByQueryRequest.setScript(new Script(script.toString()));
        try {
            BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
            if (bulkByScrollResponse.getTotal() > 0) {
                log.info("【修改ES中还款计划】保存成功, 订单={}, 期数={}", a.getOrderNo(), a.getIndexNo());
            }
        } catch (IOException e) {
            log.error("【修改ES中还款计划】保存失败, 订单={}, 期数={}, 异常={}", a.getOrderNo(), a.getIndexNo(), e);
        }

    });
}

/**
 * 保存还款计划到es
 * @param paymentPlanList
 */
public boolean insertPaymentPlans(List<LoanPaymentPlan> paymentPlanList, List<LoanPaymentPlan> historyPlanList, String orderNo, String brand) {
    BulkRequest bulkRequest = new BulkRequest();
    List<Map<String, Object>> overdueUserRecordList = new ArrayList<>();
    paymentPlanList.stream().forEach(a -> {
        IndexRequest indexRequest = new IndexRequest(REPAYMENT_PLAN_INDEX_NAME);
        Map<String, Object> params = new HashMap<>();
        params.put("brand", brand);
        params.put("uuid", a.getUuid());
        params.put("loan_office_order", a.getLoanOfficeOrder());
        params.put("loan_office_step", a.getLoanOfficeStep());
        params.put("loan_office_status", a.getLoanOfficeStatus());
        indexRequest.source(params);
        bulkRequest.add(indexRequest);
    });
    //批量保存
    try {
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (!bulkResponse.hasFailures()) {
            log.info("【添加还款计划到ES】保存成功, 订单={}, 品牌={}", orderNo, brand);
            return true;
        } else {
            for (int i = 0; i < bulkResponse.getItems().length; i++) {
                log.info("【添加还款计划到ES】执行完成, 订单={}, 品牌={}, result={}", orderNo, brand, bulkResponse.getItems()[i].getFailureMessage());
            }
        }
    } catch (IOException e) {
        log.error("【添加还款计划到ES】保存失败, 订单={}, 品牌={}, 异常={}", orderNo, brand, e);
    }

    return false;
}

/**
 * 根据订单号查询还款计划
 * @param loanOrderInfo
 * @return
 */
public List<LoanPaymentPlan> queryPaymentPlansFromEs(App2LoanOrderInfo loanOrderInfo) {
    List<LoanPaymentPlan> planList = new ArrayList<>();
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //构建查询条件
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("order_no", loanOrderInfo.getAppOrderNo());
    boolQueryBuilder.must().add(termQueryBuilder);

    searchSourceBuilder.query(boolQueryBuilder);
    searchRequest.source(searchSourceBuilder);

    try {
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
            planList.add(loanPaymentPlan);
        }

    } catch (IOException e) {
        e.printStackTrace();
        log.error("【queryPaymentPlansFromEs】根据订单号查询还款计划失败={}", e);
    }

    return planList;
}



/**
 * 风控贷后数据排重key
 */
public static final String RISK_DATA_REPETITION_KEY = "risk_data_repetition_key";

/**
 *
 * @param isOverDue 是否逾期 0否 1是
 * @param size 数量
 */
public String selectRiskData(String isOverDue, int size, String productKey) {
    FileOutputStream fileOutputStream = null;
    ExcelWriter excelWriter = null;
    String today = DateUtils.getDate();
    try {
        String fileName = new StringBuilder("风控贷后数据_").append(SysConstants.ZERO.equals(isOverDue) ? "无逾期_" : "逾期_").append(today).append(".xlsx").toString();
        fileOutputStream = new FileOutputStream(ExcelUtil.getAbsoluteFile(fileName));
        excelWriter = EasyExcel.write(fileOutputStream, RiskData.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("风控贷后数据").build();

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("loan_give_date");
        rangeQuery.lte(today);
        boolQueryBuilder.must().add(rangeQuery);

        if(StringUtils.isNotBlank(productKey)) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("loan_office_flag", productKey);
            boolQueryBuilder.must().add(termQueryBuilder);
        }


        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        searchSourceBuilder.sort("_id", SortOrder.ASC);
        searchSourceBuilder.size(5000);

        //查询数据
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();

        List<RiskData> resultList = new ArrayList<>();
        int count = 0;
        while (hits.length > 0) {

            for (SearchHit hit : hits) {
                LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
                //处理过则直接跳过
                if(redisTemplate.opsForHash().hasKey(RISK_DATA_REPETITION_KEY, loanPaymentPlan.getOrderNo())) {
                    continue;
                }
                //判断是否符合条件
                if(!filterRiskData(loanPaymentPlan, isOverDue)) {
                    continue;
                }
                redisTemplate.opsForHash().put(RISK_DATA_REPETITION_KEY, loanPaymentPlan.getOrderNo(), SysConstants.ZERO);
                redisTemplate.expire(RISK_DATA_REPETITION_KEY, 1, TimeUnit.DAYS);

                log.info("【符合条件的贷后数据】是否逾期={}, orderNo={}", isOverDue, loanPaymentPlan.getOrderNo());
                App2LoanOrderInfo app2LoanOrderInfo = app2LoanOrderInfoMapper.selectApp2LoanOrderInfoByAppOrderNo(loanPaymentPlan.getOrderNo());
                RiskData riskData = new RiskData();
                riskData.setName(SecretUtil.MD5(loanPaymentPlan.getLoanName()));
                riskData.setIdCard(loanPaymentPlan.getIdCardMd5());
                riskData.setMobile(loanPaymentPlan.getLoanMobileMd5());
                riskData.setApplyTime(DateUtils.dateTime(app2LoanOrderInfo.getCreateTime()));
                resultList.add(riskData);
                count++;

                if(count == size) {
                    hits = new SearchHit[]{};
                    break;
                }
            }
            excelWriter.write(resultList, writeSheet);
            if(hits.length > 0) {
                //根据上次查询结果的最后一个 查询后面的数据
                SearchHit lastHit = hits[hits.length - 1];
                searchSourceBuilder.searchAfter(lastHit.getSortValues());
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                hits = searchResponse.getHits().getHits();
            }
            resultList.clear();
        }

        excelWriter.finish();
        fileOutputStream.flush();

        redisTemplate.delete(RISK_DATA_REPETITION_KEY);
        log.info("【查询贷后数据】成功,fileName={}", fileName);
        return fileName;
    } catch (Exception e) {
        log.error("【查询贷后数据】失败={}", e);
    } finally {
        try {
            if(fileOutputStream != null) {
                fileOutputStream.close();
            }
            if(excelWriter != null) {
                excelWriter.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    return null;
}

/**
 * 根据订单号查询还款计划,判断该订单是否符合条件
 * @param loanPaymentPlan
 * @return true 符合  false不符合
 */
private boolean filterRiskData(LoanPaymentPlan loanPaymentPlan, String isOverDue) {
    boolean result = false;
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

    TermQueryBuilder orderNoQueryBuilder = QueryBuilders.termQuery("order_no", loanPaymentPlan.getOrderNo());
    boolQueryBuilder.must().add(orderNoQueryBuilder);

    searchSourceBuilder.query(boolQueryBuilder);
    searchRequest.source(searchSourceBuilder);
    searchSourceBuilder.sort("index_no", SortOrder.ASC);

    //查询数据
    try {
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        int count = 0;
        for (SearchHit hit : hits) {
            LoanPaymentPlan paymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
            if(!ProcessConstants.ZERO.equals(paymentPlan.getOfficeLoanStatus())) {
                count++;
            }
            //判断是否逾期
            if(ProcessConstants.THREE.equals(paymentPlan.getOfficeLoanStatus())) {
                result = true;
            }
        }
        //还款4个月以上 并且符合条件
        boolean flag = count >= 4 && (SysConstants.ZERO.equals(isOverDue) ? !result : result);
        return flag;
    } catch (Exception e) {
        log.error("【根据订单号查询是否逾期】失败={}", e);
        return false;
    }

}

/**
 * 查询未还款的订单 保存到中间表
 * @param date
 */
public void executeQueryUnpaidOrder(String date) {
    if(StringUtils.isBlank(date)) {
        LocalDateTime localDateTime = LocalDateTime.now().minusDays(1);
        date = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }
    String finalDate = date;
    //分品牌查询数据
    SysConstants.BRAND_LIST.stream().forEach(brand -> {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //构建时间查询条件
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("loan_give_date");
        rangeQuery.lte(finalDate);
        boolQueryBuilder.must().add(rangeQuery);
        //查询状态为0 待还款、3 已逾期、5 部分还款
        TermsQueryBuilder termQueryBuilder = QueryBuilders.termsQuery("office_loan_status", SysConstants.ZERO, SysConstants.THREE, SysConstants.FIVE);
        boolQueryBuilder.must().add(termQueryBuilder);

        TermQueryBuilder brandTermQueryBuilder = QueryBuilders.termQuery("brand", brand);
        boolQueryBuilder.must().add(brandTermQueryBuilder);

        //根据订单号去重
        CollapseBuilder collapseBuilder = new CollapseBuilder("order_no");
        CardinalityAggregationBuilder aggregationBuilder = AggregationBuilders.cardinality("orderNo").field("order_no");
        searchSourceBuilder.aggregation(aggregationBuilder);

        searchSourceBuilder.query(boolQueryBuilder).collapse(collapseBuilder).size(10000).trackTotalHits(true);
        searchRequest.source(searchSourceBuilder);

        try {
            //查询数据
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] hits = searchResponse.getHits().getHits();

            List<String> orderNoList = new ArrayList<>();
            for (SearchHit hit : hits) {
                LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
                orderNoList.add(loanPaymentPlan.getOrderNo());
            }
            //保存未还款订单数据
            saveUnpaidOrderByBrand(orderNoList, brand, finalDate);
        } catch (Exception e) {
            log.error("【查询未还款订单】失败", e);
        }
    });
}

/**
 * 保存未还款订单中间表数据
 * @param orderNoList
 * @param brand
 */
private void saveUnpaidOrderByBrand(List<String> orderNoList, String brand, String date) {
    //清空历史未还款订单重新插入
    deleteUnpaidOrderByBrand(brand, date);

    if(orderNoList != null && orderNoList.size() > 0) {
        BulkRequest bulkRequest = new BulkRequest();
        orderNoList.stream().forEach(orderNo -> {
            IndexRequest indexRequest = new IndexRequest(UNPAID_REPAYMENT_PLAN_INDEX_NAME);
            Map<String, Object> params = new HashMap<>();
            params.put("brand", brand);
            params.put("orderNo", orderNo);

            indexRequest.source(params);
            bulkRequest.add(indexRequest);
        });
        try {
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if(!bulk.hasFailures()) {
                log.info("【保存未还款订单中间表数据】成功,品牌={}, 日期={}", brand, date);
            }
        } catch (IOException e) {
            log.error("【保存未还款订单中间表数据】失败,品牌={}, 日期={}", brand, date, e);
        }
    }
}

/**
 * 删除未还款订单中间表数据
 * @param brand
 */
private void deleteUnpaidOrderByBrand(String brand, String date) {
    DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(UNPAID_REPAYMENT_PLAN_INDEX_NAME);
    //构建查询条件
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", brand);
    boolQueryBuilder.must().add(termQueryBuilder);

    deleteByQueryRequest.setQuery(boolQueryBuilder);

    try {
        BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
        if (bulkByScrollResponse.getTotal() > 0) {
            log.info("【删除未还款订单中间表数据】成功,品牌={}, 日期={}", brand, date);
        }
    } catch (IOException e) {
        log.error("【删除未还款订单中间表数据】失败,品牌={}, 日期={}", brand, date, e);
    }

}

/**
 * 每天定时查询前一天未还款的计划
 * @param
 */
public void executeQueryUnpaidRepaymentPlans() {

    //分品牌查询数据
    SysConstants.BRAND_LIST.stream().forEach(brand -> {
        //查询未还款订单中间表
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(UNPAID_REPAYMENT_PLAN_INDEX_NAME);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", brand);
        boolQueryBuilder.must().add(termQueryBuilder);

        searchSourceBuilder.query(boolQueryBuilder).sort("_id", SortOrder.ASC).size(5000);
        searchRequest.source(searchSourceBuilder);

        try {
            //查询数据
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] hits = searchResponse.getHits().getHits();

            List<String> orderNoList = new ArrayList<>();
            while (hits.length > 0) {
                for (SearchHit hit : hits) {
                    JSONObject jsonObject = JSONObject.parseObject(hit.getSourceAsString());
                    orderNoList.add(jsonObject.getString("orderNo"));
                }
                DataSourceTypeByDB dataSourceTypeByDB = Arrays.stream(DataSourceTypeByDB.values()).filter(d -> d.name().toLowerCase().equals(brand)).findFirst().get();
                DynamicDataSourceContextHolder.setDataSourceType(dataSourceTypeByDB.getName());
                //查询最新的还款计划
                List<App2LoanOrderInfo> orderInfoList = app2LoanOrderInfoMapper.selectOrderInfoListByOrderNos(orderNoList);
                DynamicDataSourceContextHolder.clearDataSourceType();
                if (orderInfoList != null && orderInfoList.size() > 0) {
                    orderInfoList.stream().forEach(o -> {
                        List<LoanPaymentPlan> planList = queryRepaymentPlan(o, brand);
                        if (planList != null) {
                            savePaymentPlansToEs(o, planList, brand);
                        }
                    });
                }

                orderNoList.clear();
                //根据上次查询结果的最后一个 查询后面的数据
                SearchHit lastHit = hits[hits.length - 1];
                searchSourceBuilder.searchAfter(lastHit.getSortValues());
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                hits = searchResponse.getHits().getHits();
            }
        } catch (IOException e) {
            log.error("定时查询前一天待还款的计划出现异常={}", e);
        }

    });

}

/**
 * 放款成功的订单拉取还款计划
 * @param
 */
public boolean queryLoanSuccessOrderPlans(String dbName, String appOrderNo, String applyStatus) {
    log.info("【放款成功订单拉取还款计划】appOrderNo={}, applyStatus={},dbName={}", appOrderNo, applyStatus, dbName);
    //切换数据源 查询订单信息
    DataSourceTypeByDB dataSourceTypeByDB = DataSourceTypeByDB.getDataSourceType(dbName);
    DynamicDataSourceContextHolder.setDataSourceType(dataSourceTypeByDB.getName());
    App2LoanOrderInfo orderInfo = app2LoanOrderInfoMapper.selectApp2LoanOrderInfoByAppOrderNo(appOrderNo);
    DynamicDataSourceContextHolder.clearDataSourceType();

    if(ObjectUtils.isEmpty(orderInfo)) {
        log.error("【放款成功订单拉取还款计划】未查询到订单,appOrderNo={}, 数据源={}", appOrderNo, dataSourceTypeByDB.getName());
        return false;
    }
    //查询还款计划
    List<LoanPaymentPlan> planList = queryRepaymentPlan(orderInfo, dataSourceTypeByDB.name().toLowerCase(Locale.ROOT));
    if(planList != null) {
        return savePaymentPlansToEs(orderInfo, planList, dataSourceTypeByDB.name().toLowerCase(Locale.ROOT));
    }
    return false;
}


/**
 * 保存用户逾期记录
 * @param overdueUserRecordList
 * @param orderNo
 * @param brand
 */
private void saveOverdueUserRecord(List<Map<String, Object>> overdueUserRecordList, String orderNo, String brand) {
    //先删除后新增
    DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(OVERDUE_USER_RECORD_INDEX_NAME);
    BoolQueryBuilder delBoolQueryBuilder = new BoolQueryBuilder();
    //设置查询条件
    TermQueryBuilder orderNoTermQueryBuilder = QueryBuilders.termQuery("order_no", orderNo);
    delBoolQueryBuilder.must().add(orderNoTermQueryBuilder);
    TermQueryBuilder brandTermQueryBuilder = QueryBuilders.termQuery("brand", brand);
    delBoolQueryBuilder.must().add(brandTermQueryBuilder);

    deleteByQueryRequest.setQuery(delBoolQueryBuilder);
    try {
        BulkByScrollResponse delResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
        if(delResponse.getTotal() > 0) {
            log.info("【删除用户逾期记录】成功,orderNo={},brand={}", orderNo, brand);
        }
    } catch (IOException e) {
        log.error("【删除用户逾期记录】失败,orderNo={},brand={}", orderNo, brand, e);
    }

    //新增
    BulkRequest bulkRequest = new BulkRequest();
    overdueUserRecordList.stream().forEach(r -> {
        IndexRequest indexRequest = new IndexRequest(OVERDUE_USER_RECORD_INDEX_NAME);

        indexRequest.source(r);
        bulkRequest.add(indexRequest);
    });
    try {
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        if(!bulkResponse.hasFailures()) {
            log.info("【保存用户逾期记录】成功,orderNo={},brand={}", orderNo, brand);
        }
    } catch (IOException e) {
        log.error("【保存用户逾期记录】失败,orderNo={},brand={}", orderNo, brand, e);
    }

}

/**
 * PD应还单量天数
 */
private static final int[] OVERDUE_PD_DAY = new int[] {0, 1, 7, 15, 30};


/**
 * 查询逾期单量
 * @param overdueStat
 * @param overdueStatList
 * @param day
 */
private void selectOverdueNum(OverdueStat overdueStat, List<OverdueStat> overdueStatList, int day) {
    //查询逾期已结清记录
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    //设置查询条件 条件一:还款计划状态是“部分结清” 且 系统当前时间 > 应还款日期 +  day
    BoolQueryBuilder boolQueryBuilder1 = new BoolQueryBuilder();
    //应还日期
    RangeQueryBuilder dateRangeQuery = QueryBuilders.rangeQuery("loan_give_date");
    dateRangeQuery.gte(overdueStat.getBeginTime());
    dateRangeQuery.lte(overdueStat.getEndTime());
    boolQueryBuilder1.must().add(dateRangeQuery);
    //产品
    TermQueryBuilder productTermQueryBuilder = QueryBuilders.termQuery("loan_office_flag", overdueStat.getLoanOfficeFlag());
    boolQueryBuilder1.must().add(productTermQueryBuilder);
    //还款状态 还款计划状态是“部分结清”
    TermQueryBuilder statusQueryBuilder = QueryBuilders.termQuery("app_loan_status", 5);
    boolQueryBuilder1.must().add(statusQueryBuilder);
    //首期
    TermQueryBuilder indexQueryBuilder = QueryBuilders.termQuery("index_no", 1);
    boolQueryBuilder1.must().add(indexQueryBuilder);
    //系统当前时间 - 应还款日期 > day
    Map<String, Object> params = new HashMap<>();
    params.put("num", System.currentTimeMillis());
    Script script2 = new Script(Script.DEFAULT_SCRIPT_TYPE, "painless", "(params.num - doc['loan_give_date'].value.toInstant().toEpochMilli()) / 1000 > " + (day == 0 ? 0 :(day + 1) * 24 * 60 * 60), params);
    ScriptQueryBuilder scriptQueryBuilder2 = new ScriptQueryBuilder(script2);
    boolQueryBuilder1.must().add(scriptQueryBuilder2);

    //条件二:还款计划状态是“逾期结清”且实际还款日期 > 应还款日期 + day
    BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();
    //还款计划状态是“逾期结清”
    TermQueryBuilder appLoanStatusQueryBuilder = QueryBuilders.termQuery("app_loan_status", 4);
    boolQueryBuilder2.must().add(appLoanStatusQueryBuilder);
    Script script = new Script("if(doc['actual_loan_give_date'] != null && doc['actual_loan_give_date'].value != null){" +
            "                               return (doc['actual_loan_give_date'].value.toInstant().toEpochMilli() - doc['loan_give_date'].value.toInstant().toEpochMilli()) / 1000 > " + (day == 0 ? 0 :(day + 1) * 24 * 60 * 60) +
            "                            }");
    ScriptQueryBuilder scriptQueryBuilder = new ScriptQueryBuilder(script);
    boolQueryBuilder2.must().add(scriptQueryBuilder);
    boolQueryBuilder2.must().add(productTermQueryBuilder);
    boolQueryBuilder2.must().add(dateRangeQuery);
    boolQueryBuilder2.must().add(indexQueryBuilder);

    //条件三:还款计划状态是“逾期” 且 系统当前时间 > 应还款日期 +  day
    BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
    //还款状态 还款计划状态是“逾期”或还款计划状态是“部分结清”
    TermQueryBuilder statusQueryBuilder3 = QueryBuilders.termQuery("app_loan_status", 3);
    boolQueryBuilder3.must().add(statusQueryBuilder3);
    boolQueryBuilder3.must().add(scriptQueryBuilder2);
    boolQueryBuilder3.must().add(productTermQueryBuilder);
    boolQueryBuilder3.must().add(dateRangeQuery);
    boolQueryBuilder3.must().add(indexQueryBuilder);

    //当产品名称选择“易得花”并且选择“过滤掉权益卡未结清未而逾期用户”时,条件三增加还款金额为0的条件
    if(ProcessConstants.FEN_QI_YI_PRODUCT_KEY.equals(overdueStat.getLoanOfficeFlag()) && SysConstants.ONE.equals(overdueStat.getFilterFlag())) {
        TermQueryBuilder amountTermQueryBuilder = QueryBuilders.termQuery("already_repay_amount", "0");
        boolQueryBuilder3.must().add(amountTermQueryBuilder);
    }

    //品牌
    if(StringUtils.isNotBlank(overdueStat.getBrand())) {
        TermQueryBuilder brandTermQueryBuilder = QueryBuilders.termQuery("brand", overdueStat.getBrand());
        boolQueryBuilder1.must().add(brandTermQueryBuilder);
        boolQueryBuilder2.must().add(brandTermQueryBuilder);
        boolQueryBuilder3.must().add(brandTermQueryBuilder);
    }

    //boolQueryBuilder.should().add(boolQueryBuilder1);
    boolQueryBuilder.should().add(boolQueryBuilder2);
    boolQueryBuilder.should().add(boolQueryBuilder3);

    //按日期分组统计
    TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("countAgg").field("loan_give_date").size(500);
    CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("countAgg1").field("order_no");
    aggregationBuilder.subAggregation(cardinalityAggregationBuilder);

    searchSourceBuilder.aggregation(aggregationBuilder);
    searchSourceBuilder.query(boolQueryBuilder).size(5000);
    searchRequest.source(searchSourceBuilder);

    try {
        //分组统计数量
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms countAgg = response.getAggregations().get("countAgg");
        for (Terms.Bucket bucket : countAgg.getBuckets()) {
            overdueStatList.stream().forEach(o -> {
                if(StringUtils.equals(bucket.getKeyAsString(), o.getLoanGiveDate())) {
                    Aggregation aggregation = bucket.getAggregations().asList().get(0);
                    int count = (int) ((ParsedCardinality) aggregation).getValue();
                    // int count = Math.toIntExact(bucket.getDocCount());
                    switch (day) {
                        case 0:
                            o.setPd0PayableNum(count);
                            break;
                        case 1:
                            o.setPd1PayableNum(count);
                            break;
                        case 7:
                            o.setPd7PayableNum(count);
                            break;
                        case 15:
                            o.setPd15PayableNum(count);
                            break;
                        case 30:
                            o.setPd30PayableNum(count);
                            break;
                    }
                }
            });

        }
    } catch (Exception e) {
        log.error("【贷后逾期报表】查询PD{}未还单量失败", day, e);
    }
}

/**
 * 查询应还款单量
 * @param overdueStat
 * @return
 */
private List<OverdueStat> selectPayableNum(OverdueStat overdueStat) {
    //查询逾期已结清记录
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    //设置应还日期查询条件
    RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("loan_give_date");
    rangeQuery.gte(overdueStat.getBeginTime());
    rangeQuery.lte(overdueStat.getEndTime());
    boolQueryBuilder.must().add(rangeQuery);

    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("loan_office_flag", overdueStat.getLoanOfficeFlag());
    boolQueryBuilder.must().add(termQueryBuilder);
    //首期
    TermQueryBuilder indexQueryBuilder = QueryBuilders.termQuery("index_no", 1);
    boolQueryBuilder.must().add(indexQueryBuilder);
    //品牌
    if(StringUtils.isNotBlank(overdueStat.getBrand())) {
        TermQueryBuilder brandTermQueryBuilder = QueryBuilders.termQuery("brand", overdueStat.getBrand());
        boolQueryBuilder.must().add(brandTermQueryBuilder);
    }

    //按日期分组统计
    TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("countAgg").field("loan_give_date").size(500);
    CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("countAgg1").field("order_no");
    aggregationBuilder.subAggregation(cardinalityAggregationBuilder);

    searchSourceBuilder.aggregation(aggregationBuilder);
    searchSourceBuilder.query(boolQueryBuilder).size(5000);
    searchRequest.source(searchSourceBuilder);

    List<OverdueStat> resultList = new ArrayList<>();
    try {
        //分组统计数量
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms countAgg = response.getAggregations().get("countAgg");
        for (Terms.Bucket bucket : countAgg.getBuckets()) {
            OverdueStat stat = new OverdueStat();
            stat.setLoanGiveDate(bucket.getKeyAsString());
            Aggregation aggregation = bucket.getAggregations().asList().get(0);
            int count = (int) ((ParsedCardinality) aggregation).getValue();
            stat.setPayableNum(count);
            resultList.add(stat);
        }
    } catch (Exception e) {
        log.error("【贷后逾期报表】查询应还款单量失败", e);
    }

    return resultList;
}

/**
 * 计算比例
 * @param divisor 除数
 * @param dividend 被除数
 * @return
 */
private String calculateRate(Integer divisor, Integer dividend) {
    return dividend == 0 ? SysConstants.ZERO : new BigDecimal(divisor).multiply(BigDecimal.valueOf(100)).divide(new BigDecimal(dividend), 2, BigDecimal.ROUND_HALF_DOWN).toString();
}

/**
 * 同步历史逾期用户记录
 */
public void executeHistoryOverdueRecord() {
    //查询逾期已结清记录
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //设置查询条件  还款状态=4-逾期已结清,3-已逾期
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("app_loan_status", ProcessConstants.FORE, ProcessConstants.THREE);
    boolQueryBuilder.must().add(termsQueryBuilder);

    searchSourceBuilder.query(boolQueryBuilder).sort("_id", SortOrder.ASC).size(5000);
    searchRequest.source(searchSourceBuilder);

    try {
        List<Map<String, Object>> overdueUserRecordList = new ArrayList<>();
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        if(hits.length == 0) {
            return;
        }

        //分页遍历查询
        while (hits.length > 0) {
            for (SearchHit hit : hits) {
                LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
                Map<String, Object> map = new HashMap<>();

                map.put("brand", loanPaymentPlan.getBrand());
                map.put("user_id", loanPaymentPlan.getUserId());
                map.put("loan_name", loanPaymentPlan.getLoanName());
                map.put("loan_mobile_mask", loanPaymentPlan.getLoanMobileMask());
                map.put("loan_mobile_md5", loanPaymentPlan.getLoanMobileMd5());

                overdueUserRecordList.add(map);
            }

            //保存逾期记录
            BulkRequest bulkRequest = new BulkRequest();
            overdueUserRecordList.stream().forEach(r -> {
                IndexRequest indexRequest = new IndexRequest(OVERDUE_USER_RECORD_INDEX_NAME);

                indexRequest.source(r);
                bulkRequest.add(indexRequest);
            });
            //批量保存
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            overdueUserRecordList.clear();

            //根据上次查询结果的最后一个 查询后面的数据
            SearchHit lastHit = hits[hits.length - 1];
            searchSourceBuilder.searchAfter(lastHit.getSortValues());
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            hits = searchResponse.getHits().getHits();
        }

        log.info("【同步历史逾期用户记录】成功");
    } catch (IOException e) {
        log.error("【同步历史逾期用户记录】出现异常", e);
    }
}


/**
 *
 * @param beginTime
 * @param endTime
 * @param productKey
 * @return
 */
public String exportRepaymentPlans(String beginTime, String endTime, String productKey, String loanStatus, String brand) {
    FileOutputStream fileOutputStream = null;
    ExcelWriter excelWriter = null;
    try {
        String fileName = new StringBuilder(StringUtils.isNotBlank(productKey) ? productKey : "").append("还款计划").append(beginTime).append("_").append(endTime).append(".xlsx").toString();
        fileOutputStream = new FileOutputStream(ExcelUtil.getAbsoluteFile(fileName));
        excelWriter = EasyExcel.write(fileOutputStream, ExportLoanPaymentPlan.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("还款计划").build();

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("loan_time");
        rangeQuery.gte(DateUtils.StringToTimestamp(beginTime + SysConstants.BEGIN_TIME_SUFFIX));
        rangeQuery.lte(DateUtils.StringToTimestamp(endTime + SysConstants.END_TIME_SUFFIX));
        boolQueryBuilder.must().add(rangeQuery);

        if(StringUtils.isNotBlank(productKey)) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("loan_office_flag", productKey);
            boolQueryBuilder.must().add(termQueryBuilder);
        }

        if(StringUtils.isNotBlank(loanStatus)) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("app_loan_status", loanStatus);
            boolQueryBuilder.must().add(termQueryBuilder);
        }

        if(StringUtils.isNotBlank(brand)) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", brand);
            boolQueryBuilder.must().add(termQueryBuilder);
        }

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        searchSourceBuilder.sort("_id", SortOrder.ASC);
        searchSourceBuilder.size(5000);

        //查询数据
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();

        List<ExportLoanPaymentPlan> resultList = new ArrayList<>();
        while (hits.length > 0) {

            for (SearchHit hit : hits) {
                LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
                ExportLoanPaymentPlan exportLoanPaymentPlan = new ExportLoanPaymentPlan();
                exportLoanPaymentPlan.setUserId(loanPaymentPlan.getUserId());
                exportLoanPaymentPlan.setLoanName(loanPaymentPlan.getLoanName());
                exportLoanPaymentPlan.setLoanGiveDate(loanPaymentPlan.getLoanGiveDate());
                if(StringUtils.isNotBlank(loanPaymentPlan.getLoanTime())) {
                    String result2 = simpleDateFormat.format(new Date(Long.parseLong(loanPaymentPlan.getLoanTime()) * 1000));
                    exportLoanPaymentPlan.setLoanTime(result2);
                }
                exportLoanPaymentPlan.setAppLoanStatus(loanPaymentPlan.getAppLoanStatus());

                resultList.add(exportLoanPaymentPlan);
            }
            excelWriter.write(resultList, writeSheet);
            if(hits.length > 0) {
                //根据上次查询结果的最后一个 查询后面的数据
                SearchHit lastHit = hits[hits.length - 1];
                searchSourceBuilder.searchAfter(lastHit.getSortValues());
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                hits = searchResponse.getHits().getHits();
            }
            resultList.clear();
        }

        excelWriter.finish();
        fileOutputStream.flush();

        log.info("【导出还款计划数据】成功,fileName={}", fileName);
        return fileName;
    } catch (Exception e) {
        log.error("【导出还款计划数据】失败={}", e);
    } finally {
        try {
            if(fileOutputStream != null) {
                fileOutputStream.close();
            }
            if(excelWriter != null) {
                excelWriter.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    return null;
}

public void fixData(String beginTime, String endTime, String brand) {
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices(REPAYMENT_PLAN_INDEX_NAME);

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("create_day");
    rangeQuery.gte(beginTime);
    rangeQuery.lte(endTime);
    boolQueryBuilder.must().add(rangeQuery);

    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", brand);
    boolQueryBuilder.must().add(termQueryBuilder);

    searchSourceBuilder.query(boolQueryBuilder).size(5000);
    searchRequest.source(searchSourceBuilder);

    SearchResponse searchResponse = null;
    try {
        searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    SearchHit[] hits = searchResponse.getHits().getHits();
    List<String> orderNoList = new ArrayList<>();
    for (SearchHit hit : hits) {
        LoanPaymentPlan loanPaymentPlan = JSONObject.parseObject(hit.getSourceAsString(), LoanPaymentPlan.class, JSONReader.Feature.SupportSmartMatch);
        orderNoList.add(loanPaymentPlan.getOrderNo());
    }

}

}
`

posted @ 2023-09-28 11:36  QQ-emoji  阅读(50)  评论(0)    收藏  举报