package com.simuwang.daq.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.simuwang.base.common.conts.HoldingType;
import com.simuwang.base.common.util.ValuationBusinessUtils;
import com.simuwang.base.common.util.ValuationDataUtils;
import com.simuwang.base.common.util.ValuationTableParseUtil;
import com.simuwang.base.mapper.*;
import com.simuwang.base.pojo.dos.*;
import com.simuwang.base.pojo.valuation.*;
import com.smppw.utils.BigDecimalUtils;
import com.smppw.utils.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
public class ValuationParseService {

    private static final Logger log = LoggerFactory.getLogger(ValuationParseService.class);

    public static final int stepSize = 10000;

    private final ValuationMarketCodeMapper valuationMarketCodeMapper;
    private final FundPositionDetailMapper fundPositionDetailMapper;
    private final ValuationTableMapper valuationTableMapper;
    private final FundService fundService;
    private final PdfValuationRecordMapper pdfValuationRecordMapper;
    private final ThreadPoolTaskExecutor executor;
    private final ValuationTableAttributeMapper valuationTableAttributeMapper;

    public ValuationParseService(ValuationMarketCodeMapper valuationMarketCodeMapper, FundPositionDetailMapper fundPositionDetailMapper,
                                 ValuationTableMapper valuationTableMapper, FundService fundService,
                                 PdfValuationRecordMapper pdfValuationRecordMapper, @Qualifier("valuationExecutor") ThreadPoolTaskExecutor executor,
                                 ValuationTableAttributeMapper valuationTableAttributeMapper) {
        this.valuationMarketCodeMapper = valuationMarketCodeMapper;
        this.fundPositionDetailMapper = fundPositionDetailMapper;
        this.valuationTableMapper = valuationTableMapper;
        this.fundService = fundService;
        this.pdfValuationRecordMapper = pdfValuationRecordMapper;
        this.executor = executor;
        this.valuationTableAttributeMapper = valuationTableAttributeMapper;
    }

    public List<AssetsValuationResult.Record> parseValuationExcel(List<ValuationNeedParseParam> valuationNeedParseParams) {
        if (CollUtil.isEmpty(valuationNeedParseParams)) {
            return CollUtil.newArrayList();
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("create valuation executor");
        stopWatch.stop();
        log.info("create valuation executor cost -> {}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
        List<AssetsValuationResult.Record> records = CollectionUtil.newArrayList();
        List<Future<ValuationResult>> futureList = CollectionUtil.newArrayList();
        List<String> marketCodeList = valuationMarketCodeMapper.queryMarketCode();
        try {
            for (ValuationNeedParseParam valuationNeedParseParam : valuationNeedParseParams) {
                ParseValuationInfo parseValuationInfo;
                AssetsValuationResult.Record record = new AssetsValuationResult.Record();
                PreAssetsValuationInfo<PreAssetsValuationBase> preAssetsValuationInfo = ValuationBusinessUtils.importFile(valuationNeedParseParam);
                PreAssetsValuationInfo<PreAssetsValuationBase>.Error error = preAssetsValuationInfo.getError();
                if (error != null) {
                    error = preAssetsValuationInfo.getError();
                    record.setExcelName(error.getExcelName()).setMsg(error.getMsg()).setRowNum(error.getRowNum()).setColumnNum(error.getCellNum())
                            .setSuccess(0);
                    records.add(record);
                } else {
                    AssetsValuationExcelInfo<AssetsValuationInfo> excelInfo = processDataV2(preAssetsValuationInfo);
                    // 邮件解析 -> 需要根据备案编码和基金名称去查询对应的基金id
                    if (StringUtils.isEmpty(valuationNeedParseParam.getFundId())) {
                        String headInfo = excelInfo.getExtInfo().getHeadInfo();
                        if (StringUtils.isNotEmpty(headInfo)) {
                            AssetsValuationDetails details = excelInfo.getExtInfo();

                            parseValuationInfo = parseRegisterNumAndFundName(headInfo);
                            record.setParseValuationInfo(parseValuationInfo);
                            log.info("表格:{},从表格中解析到的基金名称和备案编码:{}", valuationNeedParseParam.getOriginFileName(), parseValuationInfo.toString());

                            List<FundInfoDO> fundInfoDOList = fundService.getFundInfoByNamesAndCode(parseValuationInfo.getFundName(), parseValuationInfo.getRegisterNumber());
                            log.info("表格:{}, 匹配上的基金:{}", valuationNeedParseParam.getOriginFileName(), fundInfoDOList);
                            if (CollUtil.isEmpty(fundInfoDOList)) {
                                // 未匹配基金的情况 -> 不保存估值表信息
                                record.setNav(String.valueOf(details.getNav()));
                                record.setCumulativeNavWithdrawal(String.valueOf(details.getCumulativeNav()));
                                record.setExcelName(valuationNeedParseParam.getOriginFileName());
                                record.setDate(details.getValuationDate());
                                record.setExcelName(valuationNeedParseParam.getOriginFileName());
                                record.setMsg("未匹配基金");
                                record.setDate(details.getValuationDate());
                                record.setSuccess(0);
                                BigDecimal assetNet = details.getNetAssetsValue() != null ? BigDecimal.valueOf(details.getNetAssetsValue()) : null;
                                BigDecimal assetShare = details.getTotalMarketValue() != null ? BigDecimal.valueOf(details.getTotalMarketValue()) : null;
                                record.setAssetNet(assetNet != null ? String.valueOf(assetNet) : null);
                                record.setAssetShare(assetShare != null ? String.valueOf(assetShare) : null);
                                records.add(record);
                            } else {
                                for (FundInfoDO fundInfoDO : fundInfoDOList) {
                                    String fundId = fundInfoDO.getFundId();
                                    AssetsValuationResult.Record singleFundRecord = new AssetsValuationResult.Record();
                                    singleFundRecord.setExcelName(valuationNeedParseParam.getOriginFileName());
                                    singleFundRecord.setDate(details.getValuationDate());
                                    singleFundRecord.setSuccess(1);
                                    singleFundRecord.setFundId(fundId);
                                    singleFundRecord.setCumulativeNavWithdrawal(String.valueOf(details.getCumulativeNav()));
                                    singleFundRecord.setNav(String.valueOf(details.getNav()));
                                    singleFundRecord.setParseValuationInfo(parseValuationInfo);
                                    String valuationDate = details.getValuationDate();
                                    List<AssetsValuationInfo> data = excelInfo.getData();
                                    if (CollUtil.isNotEmpty(data)) {
                                        ValuationTableDO tableInfo = new ValuationTableDO();
                                        Integer valuationId = trans2UserValuationDoAndWrite(details, fundId, tableInfo, valuationNeedParseParam);
                                        singleFundRecord.setValuationId(valuationId);
                                        Future<ValuationResult> future = executor.submit(() -> {
                                            ValuationResult valuationResult = new ValuationResult();
                                            valuationResult.setFundId(fundId);
                                            List<CmValuationTableAttribute> valuationTableAttributes = ValuationTableParseUtil.getAttrList(data);
                                            List<FundPositionDetailDO> fundPositionDetailDOList = ValuationTableParseUtil.parseDataNew(valuationTableAttributes, tableInfo, marketCodeList);
                                            if (CollectionUtil.isNotEmpty(fundPositionDetailDOList)) {
                                                fundPositionDetailDOList.stream().filter(p -> "202".equals(p.getSecuritiesCode())).findFirst()
                                                        .ifPresent(p -> valuationResult.setCumulativeNavWithdrawal(p.getMarketValue() != null ? String.valueOf(p.getMarketValue()) : null));
                                            }
                                            valuationResult.setValuationTableAttributes(valuationTableAttributes);
                                            valuationResult.setList(fundPositionDetailDOList);
                                            valuationResult.setRecord(singleFundRecord);
                                            valuationResult.setValuationDate(valuationDate);
                                            return valuationResult;
                                        });
                                        futureList.add(future);
                                    }
                                }
                            }
                        }
                    } else {
                        String fundId = valuationNeedParseParam.getFundId();
                        AssetsValuationDetails details = excelInfo.getExtInfo();
                        record.setNav(String.valueOf(details.getNav()));
                        record.setCumulativeNavWithdrawal(String.valueOf(details.getCumulativeNav()));
                        record.setExcelName(valuationNeedParseParam.getOriginFileName());
                        record.setDate(details.getValuationDate());
                        record.setSuccess(1);
                        record.setFundId(fundId);
                        String valuationDate = details.getValuationDate();
                        List<AssetsValuationInfo> data = excelInfo.getData();
                        if (CollectionUtil.isNotEmpty(data)) {
                            ValuationTableDO tableInfo = new ValuationTableDO();
                            Integer valuationId = trans2UserValuationDoAndWrite(details, fundId, tableInfo, valuationNeedParseParam);
                            record.setValuationId(valuationId);
                            Future<ValuationResult> future = executor.submit(() -> {
                                long startTime = System.currentTimeMillis();
                                ValuationResult valuationResult = new ValuationResult();
                                valuationResult.setFundId(fundId);
                                valuationResult.setUserId(0);
                                List<FundPositionDetailDO> fundPositionDetailDOList = CollUtil.newArrayList();
                                try {
                                    List<CmValuationTableAttribute> valuationTableAttributes = ValuationTableParseUtil.getAttrList(data);
                                    valuationResult.setValuationTableAttributes(valuationTableAttributes);
                                    fundPositionDetailDOList = ValuationTableParseUtil.parseDataNew(valuationTableAttributes, tableInfo, marketCodeList);
                                } catch (Exception e) {
                                    log.info("Exception: {}", ExceptionUtil.stacktraceToString(e));
                                }
                                if (CollectionUtil.isNotEmpty(fundPositionDetailDOList)) {
                                    fundPositionDetailDOList.stream().filter(p -> "202".equals(p.getSecuritiesCode())).findFirst()
                                            .ifPresent(p -> valuationResult.setCumulativeNavWithdrawal(p.getMarketValue() != null ? String.valueOf(p.getMarketValue()) : null));
                                }
                                valuationResult.setList(fundPositionDetailDOList);
                                valuationResult.setRecord(record);
                                valuationResult.setValuationDate(valuationDate);
                                long endTime = System.currentTimeMillis();
                                log.info("end parse -> {},spend time -> {}", record.excelName, (endTime - startTime) + "ms");
                                return valuationResult;
                            });
                            futureList.add(future);
                        }
                    }
                }
            }

            for (Future<ValuationResult> future : futureList) {
                try {
                    ValuationResult r = future.get(3600, TimeUnit.SECONDS);
                    String fundId = r.getFundId();
                    AssetsValuationResult.Record record = r.getRecord();
                    record.setCumulativeNavWithdrawal(record.getCumulativeNavWithdrawal());
                    // 获取资产净值和资产份额
                    BigDecimal assetNet = r.getList().stream().filter(e -> StrUtil.isNotBlank(e.getSecuritiesCode()) && "112".equals(e.getSecuritiesCode()))
                            .findFirst().map(FundPositionDetailDO::getMarketValue).orElse(null);
                    BigDecimal assetShare = r.getList().stream().filter(e -> StrUtil.isNotBlank(e.getSecuritiesCode()) && "107".equals(e.getSecuritiesCode()))
                            .findFirst().map(FundPositionDetailDO::getMarketValue).orElse(null);
                    record.setAssetNet(assetNet != null ? String.valueOf(assetNet) : null);
                    record.setAssetShare(assetShare != null ? String.valueOf(assetShare) : null);
                    records.add(record);
                    Integer valuationId = record.getValuationId();
                    List<CmValuationTableAttribute> valuationTableAttributes = r.getValuationTableAttributes();
                    // 保存估值表原始信息
                    saveValuationTableAttribute(valuationId, valuationTableAttributes);
                    // 保存估值表持仓明细信息
                    saveFundPositionDetail(r.getList(), fundId, r.getValuationDate());
                } catch (InterruptedException | ExecutionException | TimeoutException e) {
                    log.info("valuation excel upload future exec exception: {}", e.getMessage(), e);
                }
            }
        } catch (Exception exception) {
            log.error("parse valuation excel error ->{}", ExceptionUtil.stacktraceToString(exception));
        } finally {
            stopWatch.start("destruction valuation executor");
            stopWatch.stop();
            log.info("destruction valuation executor cost -> {}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
        }
        //保存PDF估值表记录
        savePdfValuationRecord(valuationNeedParseParams, records);
        return records;
    }

    private void saveValuationTableAttribute(Integer valuationId, List<CmValuationTableAttribute> valuationTableAttributes) {
        if (valuationId == null || CollUtil.isEmpty(valuationTableAttributes)) {
            return;
        }
        List<ValuationTableAttributeDO> valuationTableAttributeDOList = buildValuationTableAttributeDO(valuationId, valuationTableAttributes);
        valuationTableAttributeMapper.deleteByValuationId(valuationId);
        if (CollUtil.isNotEmpty(valuationTableAttributeDOList)) {
            valuationTableAttributeMapper.batchInsert(valuationTableAttributeDOList);
        }
    }

    private List<ValuationTableAttributeDO> buildValuationTableAttributeDO(Integer valuationId, List<CmValuationTableAttribute> valuationTableAttributes) {
        if (CollUtil.isEmpty(valuationTableAttributes)) {
            return CollUtil.newArrayList();
        }
        return valuationTableAttributes.stream().map(e -> {
            ValuationTableAttributeDO tableAttributeDO = new ValuationTableAttributeDO();
            tableAttributeDO.setValuationId(valuationId);
            tableAttributeDO.setSubjectCode(e.getOriginalSubjectCode());
            tableAttributeDO.setSubjectName(e.getSubjectName());
            tableAttributeDO.setCurrency(e.getCurrency());
            tableAttributeDO.setExchangeRate(e.getExchangeRate());
            tableAttributeDO.setSecuritiesAmount(e.getSecuritiesAmount());
            tableAttributeDO.setUnitCost(e.getUnitCost());
            tableAttributeDO.setOriCurrencyCost(e.getOCurrencyCost());
            tableAttributeDO.setNetCost(e.getNetCost());
            tableAttributeDO.setNetCostRatio(e.getNetCostRatio());
            tableAttributeDO.setMarketPrice(e.getMarketPrice());
            tableAttributeDO.setOriCurrencyMarketValue(e.getOCurrencyMarketValue());
            tableAttributeDO.setMarketValue(e.getMarketValue());
            tableAttributeDO.setMarketValueRatio(e.getMarketValueRatio());
            tableAttributeDO.setOriCurrencyIncrement(e.getOCurrencyMarketValue());
            tableAttributeDO.setIncrement(e.getIncrement());
            tableAttributeDO.setHaltInfo(e.getHaltInfo());
            tableAttributeDO.setRightsInterestsInfo(e.getRightsInterestsInfo());
            tableAttributeDO.setIsvalid(1);
            tableAttributeDO.setCreatorId(0);
            tableAttributeDO.setUpdaterId(0);
            tableAttributeDO.setCreateTime(new Date());
            tableAttributeDO.setUpdateTime(new Date());
            return tableAttributeDO;
        }).collect(Collectors.toList());
    }

    /**
     * 保存PDF估值表记录
     *
     * @param valuationNeedParseParams 估值表解析参数
     * @param records                  估值表解析结果
     */
    private void savePdfValuationRecord(List<ValuationNeedParseParam> valuationNeedParseParams, List<AssetsValuationResult.Record> records) {
        if (CollUtil.isEmpty(records)) {
            return;
        }
        List<AssetsValuationResult.Record> pdfRecordList = records.stream().filter(e -> e.getExcelName().endsWith("pdf")).collect(Collectors.toList());
        if (CollUtil.isEmpty(pdfRecordList)) {
            return;
        }
        List<PdfValuationRecordDO> pdfValuationRecordDoList = buildCmPdfValuationRecordDo(valuationNeedParseParams, pdfRecordList);
        if (CollUtil.isNotEmpty(pdfValuationRecordDoList)) {
            pdfValuationRecordMapper.batchInsert(pdfValuationRecordDoList);
        }
    }

    private List<PdfValuationRecordDO> buildCmPdfValuationRecordDo(List<ValuationNeedParseParam> valuationNeedParseParams,
                                                                   List<AssetsValuationResult.Record> pdfRecordList) {
        if (CollUtil.isEmpty(pdfRecordList)) {
            return CollUtil.newArrayList();
        }
        Map<String, ValuationNeedParseParam> needParseParamMap = valuationNeedParseParams.stream()
                .collect(Collectors.toMap(ValuationNeedParseParam::getOriginFileName, v -> v));
        return pdfRecordList.stream().map(e -> {
            PdfValuationRecordDO pdfValuationRecordDo = new PdfValuationRecordDO();
            ValuationNeedParseParam parseParam = needParseParamMap.get(e.getExcelName());
            if (parseParam != null) {
                pdfValuationRecordDo.setUploadDate(new Date());
                pdfValuationRecordDo.setExcelUrl(parseParam.getFile().getAbsolutePath());
                pdfValuationRecordDo.setPdfUrl(parseParam.getFileUrl());
                pdfValuationRecordDo.setFromEmail(parseParam.getFromEmail() == 1 ? 1 : 0);
            }
            pdfValuationRecordDo.setFundId(e.getFundId());
            pdfValuationRecordDo.setFileName(e.getExcelName());
            pdfValuationRecordDo.setIsSuccess(e.getSuccess());
            pdfValuationRecordDo.setReason(e.getMsg());
            pdfValuationRecordDo.setCreatorId(0);
            pdfValuationRecordDo.setUpdaterId(0);
            pdfValuationRecordDo.setCreateTime(new Date());
            pdfValuationRecordDo.setUpdateTime(new Date());
            pdfValuationRecordDo.setIsvalid(1);
            return pdfValuationRecordDo;
        }).collect(Collectors.toList());
    }

    /**
     * 解析备案编码和备案编码
     *
     * @param headInfo excel第一行和第二行的内容,中间以'@'符号分隔
     * @return 备案编码和备案编码
     */
    public ParseValuationInfo parseRegisterNumAndFundName(String headInfo) {
        ParseValuationInfo info = new ParseValuationInfo();
        if (StringUtils.isNotEmpty(headInfo)) {
            List<String> contentList = Arrays.stream(headInfo.split("@")).collect(Collectors.toList());
            for (String content : contentList) {
                String registerNumber = content.length() > 6 ? content.substring(0, 6) : null;
                if (StrUtil.isNotBlank(registerNumber) && !ValuationDataUtils.hasChinese(registerNumber, false)) {
                    info.setRegisterNumber(registerNumber);
                }
                if (content.contains("估值表") || content.contains("专用表")) {
                    String originRegisterNumber = info.getRegisterNumber();
                    String originFundName = info.getFundName();
                    if (content.contains("___")) {
                        List<String> collect = Arrays.stream(content.split("___")).collect(Collectors.toList());
                        if (collect.size() == 4 && content.contains("专用表")) {
                            info.setRegisterNumber(collect.get(1));
                            info.setFundName(collect.get(2));
                        } else {
                            if (CollUtil.isNotEmpty(collect) && StrUtil.isBlank(originRegisterNumber) && !ValuationDataUtils.hasChinese(registerNumber, false)) {
                                originRegisterNumber = collect.get(0);
                                info.setRegisterNumber(originRegisterNumber);
                            }
                            originFundName = collect.size() == 2 ? collect.get(0) : originFundName;
                            originFundName = collect.size() > 2 ? collect.get(1) : originFundName;
                            info.setFundName(originFundName);
                        }

                    } else if (content.contains("__")) {
                        List<String> collect = Arrays.stream(content.split("__")).collect(Collectors.toList());
                        if (collect.size() == 4 && content.contains("专用表")) {
                            info.setRegisterNumber(collect.get(1));
                            info.setFundName(collect.get(2));
                        } else {
                            if (CollUtil.isNotEmpty(collect) && StrUtil.isBlank(originRegisterNumber) && !ValuationDataUtils.hasChinese(registerNumber, false)) {
                                originRegisterNumber = collect.get(0);
                                info.setRegisterNumber(originRegisterNumber);
                            }
                            originFundName = collect.size() == 2 ? collect.get(0) : originFundName;
                            originFundName = collect.size() > 2 ? collect.get(1) : originFundName;
                            info.setFundName(originFundName);
                        }
                    } else if (content.contains("_")) {
                        List<String> collect = Arrays.stream(content.split("_")).collect(Collectors.toList());
                        if (collect.size() == 4 && content.contains("专用表")) {
                            info.setRegisterNumber(collect.get(1));
                            info.setFundName(collect.get(2));
                        } else {
                            if (CollUtil.isNotEmpty(collect) && StrUtil.isBlank(originRegisterNumber) && !ValuationDataUtils.hasChinese(registerNumber, false)) {
                                originRegisterNumber = collect.get(0);
                                info.setRegisterNumber(originRegisterNumber);
                            }
                            originFundName = collect.size() == 2 ? collect.get(0) : originFundName;
                            originFundName = collect.size() > 2 ? collect.get(1) : originFundName;
                            info.setFundName(originFundName);
                        }
                    }
                }
            }
        }
        //兼容格式: xxx估值表@基金名称 -> 只能识别到基金名称
        if (StrUtil.isBlank(info.getFundName()) && StrUtil.isBlank(info.getRegisterNumber())) {
            List<String> contentList = Arrays.stream(headInfo.split("@")).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(contentList) && contentList.size() >= 2) {
                String fundName = !contentList.get(1).contains("_") ? contentList.get(1) : null;
                info.setFundName(fundName);
            }
        }

        // 兼容格式:编码编码+基金名称+日期+估值报表四级 -> SXT974同增FOF稳增1期私募证券投资基金2024年08月02日估值报表四级
        if (StrUtil.isBlank(info.getFundName()) && StrUtil.isNotBlank(info.getRegisterNumber())) {
            int index = StrUtil.indexOfIgnoreCase(headInfo, "私募证券投资基金");
            // 说明找到了
            if (index != -1) {
                String shortName = headInfo.substring(0, StrUtil.indexOfIgnoreCase(headInfo, "私募证券投资基金"));
                String fundName = shortName.replace(info.getRegisterNumber(), "") + "私募证券投资基金";
                info.setFundName(fundName);
            }
        }
        return info;
    }

    public void saveFundPositionDetail(List<FundPositionDetailDO> fundPositionDetails, String fundId, String valuationDate) {
        int subBegin = 0;
        int subEnd = stepSize;
        int insertNum = 0;
        int hasStock = 0;
        int hasBond = 0;
        int hasFuture = 0;
        if (CollectionUtil.isNotEmpty(fundPositionDetails)) {
            //插入持仓数据 cm_fund_position_detail（记录数较多，得分批）
            // 先删除原先的数据 然后写入数据
            fundPositionDetailMapper.deleteUnUsed(fundId, valuationDate);
            // 将最终结果写入 cm_fund_position_detail
            while (subBegin < fundPositionDetails.size()) {
                List<FundPositionDetailDO> segment = fundPositionDetails.subList(subBegin, Math.min(subEnd, fundPositionDetails.size()));
                for (FundPositionDetailDO fundPositionDetail : segment) {
                    //实收信托、实收资本对应的编码为107
                    if (StrUtil.isNotBlank(fundPositionDetail.getSecuritiesName()) && "实收信托".equals(fundPositionDetail.getSecuritiesName().trim())) {
                        fundPositionDetail.setSubjectCode("107");
                        fundPositionDetail.setSecuritiesCode("107");
                    }
                    //设置创建者id,在拿取最近修改人时有用
                    fundPositionDetail.setCreatorId(0);
                    if (fundPositionDetail.getLevel() != null && fundPositionDetail.getLevel() >= 4) {
                        Integer secType = fundPositionDetail.getSecType();
                        Integer subType = fundPositionDetail.getSubType();
                        if (secType != null && subType != null) {
                            if (secType == HoldingType.Stock.getId() && subType == HoldingType.Stock.getId()) {
                                hasStock = 1;
                            }
                            // 正逆回购不属于债券
                            String subjectCode = fundPositionDetail.getSubjectCode();
                            String[] SALE_CODES = {"2202", "1202"};
                            if (secType == HoldingType.Bond.getId() && !StrUtil.containsAny(subjectCode, SALE_CODES)) {
                                hasBond = 1;
                            }
                            if (secType == HoldingType.Future.getId() || secType == HoldingType.Option.getId()) {
                                hasFuture = 1;
                            }
                        }
                    }
                }
                insertNum += fundPositionDetailMapper.insertMulti(segment);
                subBegin = subEnd;
                subEnd += stepSize;
            }
        }
        // 更新 cm_user_valuation_table
        if (insertNum > 0) {
            valuationTableMapper.updateUpdateAnalyzed(fundId, valuationDate, hasStock, hasBond, hasFuture);
        }
    }

    public Integer trans2UserValuationDoAndWrite(AssetsValuationDetails details, String fundId, ValuationTableDO tableInfo, ValuationNeedParseParam valuationNeedParseParam) {
        valuationTableMapper.unValid(fundId, details.getValuationDate());
        tableInfo.setFundId(fundId);
        tableInfo.setValuationDate(DateUtil.StringToDate(details.getValuationDate()));
        tableInfo.setIsAnalyzed(0);
        tableInfo.setIsvalid(1);
        tableInfo.setCreateTime(DateTime.now());
        tableInfo.setUpdateTime(DateTime.now());
        tableInfo.setCreatorId(0);
        tableInfo.setUpdaterId(0);
        tableInfo.setTotalMarketValue(BigDecimalUtils.toBigDecimal(details.getTotalMarketValue()));
        tableInfo.setNetAssetsValue(BigDecimalUtils.toBigDecimal(details.getNetAssetsValue()));
        tableInfo.setIncrement(BigDecimalUtils.toBigDecimal(details.getIncrement()));
        tableInfo.setTitle(details.getTitle());
        tableInfo.setNav(BigDecimalUtils.toBigDecimal(details.getNav()));
        tableInfo.setFileUrl(valuationNeedParseParam.getFileUrl());
        tableInfo.setHeadInfo(details.getHeadInfo());
        tableInfo.setTailInfo(details.getTailInfo());
        tableInfo.setTableType(details.getType());
        tableInfo.setFromEmail(valuationNeedParseParam.getFromEmail());
        tableInfo.setOriginalFile(valuationNeedParseParam.getOriginFileName());
        tableInfo.setConvertedFile(valuationNeedParseParam.getFile().getName());
        valuationTableMapper.insert(tableInfo);
        return tableInfo.getId();
    }

    public AssetsValuationExcelInfo<AssetsValuationInfo> processDataV2(PreAssetsValuationInfo<PreAssetsValuationBase> info) {
        AssetsValuationExcelInfo<AssetsValuationInfo> avInfo = new AssetsValuationExcelInfo<>();

        AssetsValuationDetails extInfo = new AssetsValuationDetails();
        extInfo.setTailInfo(list2String(info.getFooter()));
        extInfo.setHeadInfo(list2String(info.getHeader().getHeaderList()));
        extInfo.setOriginalfile(info.getExcelOriginName());
        extInfo.setConvertedFile(info.getExcelNewName());
        extInfo.setFileUrl(info.getSavePath());
        extInfo.setTitle(info.getHeader().getTitle());
        extInfo.setType(info.getType().getId());
        extInfo.setNav(info.getHeader().getUnitNetValue());
        extInfo.setUserId(ValuationDataUtils.string2Int(info.getUserId()));
        extInfo.setValuationDate(new Date(info.getHeader().getValuationDate().getTime()));

        List<PreAssetsValuationBase> lBases = info.getData();
        ValuationDataUtils.removeUnderLine(lBases);
        List<AssetsValuationInfo> list = new ArrayList<>(lBases.size());

        //现在业务需要保存基金的总资产  总资产  资产净值  直接从解析结果获取
        Double totalMarketValue = null;
        // 总资产净值
        Double netAssetMarketValue = null;
        // 基金估值增值
        Double increment = null;

        for (int i = 0; i < lBases.size(); i++) {

            PreAssetsValuationBase d = lBases.get(i);

            if (d.getSubjectCode() == null || d.getSubjectCode().isEmpty()) {
                continue;
            }

            String originalSubjectCode = d.getOriginalSubjectCode();
            if (ValuationDataUtils.checkMarketValue(originalSubjectCode)) {
                totalMarketValue = ValuationDataUtils.string2Double(d.getMarketValue());
            }
            if (ValuationDataUtils.checkNetAssetsValue(originalSubjectCode)) {
                netAssetMarketValue = ValuationDataUtils.string2Double(d.getMarketValue());
                increment = ValuationDataUtils.string2Double(d.getValueAdded());
            }

            AssetsValuationInfo data = new AssetsValuationInfo();
            data.setUserId(ValuationDataUtils.string2Int(info.getUserId()));
            data.setSecuritiesAmount(ValuationDataUtils.string2Double(d.getAmount()));
            data.setHaltInfo(d.getSuspensionInfo());
            data.setIncrement(ValuationDataUtils.string2Double(d.getValueAdded()));
            data.setMarketValue(ValuationDataUtils.string2Double(d.getMarketValue()));
            data.setMarketValueRatio(ValuationDataUtils.string2Double(d.getRatioOfMarketValueToNetWorth()));
            data.setNetCost(ValuationDataUtils.string2Double(d.getCost()));
            data.setNetCostRatio(ValuationDataUtils.string2Double(d.getRatioOfCostToNetWorth()));
            data.setSubjectCode(d.getSubjectCode());
            data.setSecuritiesCode((d.getSecurtiesCode()));
            // 对于期权，如果以DR、XR开头，则剔除这两个字母
            if (data.getSubjectName() != null && (data.getSubjectName().startsWith("DR") || data.getSubjectName().startsWith("XR"))) {
                String subjectName = data.getSubjectName().replace("DR", "");
                data.setSubjectName(subjectName.replace("XR", ""));
            } else {
                data.setSubjectName(d.getSubjectName());
            }
            data.setUnitCost(ValuationDataUtils.string2Double(d.getUnitCost()));

            //寻找累计单位净值
            if (null == extInfo.getCumulativeNav()) {
                if (StringUtils.isNotEmpty(d.getSubjectCode()) && d.getSubjectCode().contains("累计单位净值")) {
                    if (StringUtils.isNotEmpty(d.getSubjectName()) && ValuationDataUtils.isNumber(d.getSubjectName())) {
                        extInfo.setCumulativeNav(Double.valueOf(d.getSubjectName()));
                    }
                }
            }

            switch (d.getType()) {
                case GeneralTemplate:
                    PreAssetsValuationGeneralTemplate general = (PreAssetsValuationGeneralTemplate) d;
                    data.setOriginalSubjectCode(general.getOriginalSubjectCode());
                    data.setMarketPrice(ValuationDataUtils.string2Double(general.getMarketPrice()));
                    data.setCurrency(general.getCurrency());
                    data.setExchangeRate(general.getExchangeRate());
                    data.setOriCurrencyCost(ValuationDataUtils.string2Double(general.getOriCurrencyCost()));
                    data.setOriCurrencyMarketValue(ValuationDataUtils.string2Double(general.getOriCurrencyMarketValue()));
                    data.setOriCurrencyValueAdded(ValuationDataUtils.string2Double(general.getOriCurrencyValueAdded()));
                    data.setSubjectLevel(general.getSubjectLevel());
                    data.setIssuer(general.getIssuer());
                    data.setVProjectCode(general.getvProjectCode());
                    data.setVProjectName(general.getvProjectName());
                    data.setCostRatioAsset(general.getCostRatioAsset());
                    data.setMvRatioAsset(general.getMvRatioAsset());
                    data.setSpotPrice(general.getSpotPrice());
                    data.setRemark(general.getRemark());
                    data.setRightsInterestsInfo(general.getRightAndInterestInfo());
                    data.setAssumingCost(general.getAssumingCost());
                    data.setHoldingState(general.getHoldingState());

                    if (data.getSubjectName() != null && ValuationDataUtils.isValid(data.getSubjectCode())) {
                        data.setSubjectCode(data.getSubjectCode().replace(".", ""));
                        if (ValuationDataUtils.hasChinese(data.getSubjectCode(), false)) {
                            String code = data.getSubjectCode().replace(data.getSubjectName(), "");
                            code = ValuationDataUtils.getSubjectCode(code);
                            if (code.length() >= 4) {
                                data.setSubjectCode(code);
                            } else {
                                data.setSecType(100);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
            list.add(data);
        }
        extInfo.setTotalMarketValue(totalMarketValue);
        extInfo.setNetAssetsValue(netAssetMarketValue);
        extInfo.setIncrement(increment);
        avInfo.setExtInfo(extInfo);
        avInfo.setData(list);
        return avInfo;
    }

    private static String list2String(List<String> list) {
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        return String.join("@", list);
    }
}
