Selaa lähdekoodia

Merge branch 'develop' of http://112.74.196.215:3000/Tech2/data-daq into develop

wangzaijun 7 kuukautta sitten
vanhempi
commit
4915d2cd58
49 muutettua tiedostoa jossa 5525 lisäystä ja 75 poistoa
  1. 31 0
      service-base/src/main/java/com/simuwang/base/ano/Excel.java
  2. 87 0
      service-base/src/main/java/com/simuwang/base/common/conts/AccountingItems.java
  3. 98 0
      service-base/src/main/java/com/simuwang/base/common/conts/AccountingNewItems.java
  4. 59 0
      service-base/src/main/java/com/simuwang/base/common/conts/HoldingType.java
  5. 20 0
      service-base/src/main/java/com/simuwang/base/common/conts/ValuationConst.java
  6. 39 0
      service-base/src/main/java/com/simuwang/base/common/enums/MarketTradIngCodeEnum.java
  7. 910 0
      service-base/src/main/java/com/simuwang/base/common/util/ValuationBusinessUtils.java
  8. 149 0
      service-base/src/main/java/com/simuwang/base/common/util/ValuationDataUtils.java
  9. 1206 0
      service-base/src/main/java/com/simuwang/base/common/util/ValuationTableParseUtil.java
  10. 35 0
      service-base/src/main/java/com/simuwang/base/config/ThreadPoolConfig.java
  11. 4 3
      service-base/src/main/java/com/simuwang/base/mapper/FundAliasMapper.java
  12. 5 3
      service-base/src/main/java/com/simuwang/base/mapper/FundInfoMapper.java
  13. 8 0
      service-base/src/main/java/com/simuwang/base/mapper/FundPositionDetailMapper.java
  14. 11 0
      service-base/src/main/java/com/simuwang/base/mapper/PdfValuationRecordMapper.java
  15. 3 0
      service-base/src/main/java/com/simuwang/base/mapper/ValuationMarketCodeMapper.java
  16. 16 0
      service-base/src/main/java/com/simuwang/base/mapper/ValuationTableAttributeMapper.java
  17. 9 0
      service-base/src/main/java/com/simuwang/base/mapper/ValuationTableMapper.java
  18. 10 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundAssetDO.java
  19. 4 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/FundPositionDetailDO.java
  20. 134 0
      service-base/src/main/java/com/simuwang/base/pojo/dos/ValuationTableAttributeDO.java
  21. 2 2
      service-base/src/main/java/com/simuwang/base/pojo/dos/ValuationTableDO.java
  22. 173 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationDetails.java
  23. 25 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationExcelInfo.java
  24. 96 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationInfo.java
  25. 207 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationResult.java
  26. 47 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationType.java
  27. 159 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/CmValuationTableAttribute.java
  28. 13 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ExcelEntity.java
  29. 84 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ExcelInfo.java
  30. 153 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ImportParams.java
  31. 12 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ParseValuationInfo.java
  32. 138 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationBase.java
  33. 309 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationGeneralTemplate.java
  34. 202 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationInfo.java
  35. 29 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationNeedParseParam.java
  36. 17 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationResult.java
  37. 88 0
      service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationTableConfig.java
  38. 4 2
      service-base/src/main/resources/mapper/EmailFundAssetMapper.xml
  39. 6 6
      service-base/src/main/resources/mapper/FundAliasMapper.xml
  40. 12 6
      service-base/src/main/resources/mapper/FundInfoMapper.xml
  41. 22 0
      service-base/src/main/resources/mapper/FundPositionDetailMapper.xml
  42. 12 0
      service-base/src/main/resources/mapper/PdfValuationRecordMapper.xml
  43. 6 0
      service-base/src/main/resources/mapper/ValuationMarketCodeMapper.xml
  44. 53 0
      service-base/src/main/resources/mapper/ValuationTableAttributeMapper.xml
  45. 35 0
      service-base/src/main/resources/mapper/ValuationTableMapper.xml
  46. 23 52
      service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java
  47. 87 0
      service-daq/src/main/java/com/simuwang/daq/service/FundService.java
  48. 70 1
      service-daq/src/main/java/com/simuwang/daq/service/ValuationEmailParser.java
  49. 603 0
      service-daq/src/main/java/com/simuwang/daq/service/ValuationParseService.java

+ 31 - 0
service-base/src/main/java/com/simuwang/base/ano/Excel.java

@@ -0,0 +1,31 @@
+package com.simuwang.base.ano;
+
+import java.lang.annotation.*;
+
+/**
+ * @author William
+ * @version 2
+ */
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel {
+	/** Excel列名,多个值表示同义词 */
+	public String[] value() default {};
+
+	/** 使用外部配置, 等同于 String[] value()*/
+	public String values() default "";
+
+	/**支持null,整型,浮点型,带%的数值,默认非数值false */
+	public boolean numerical() default false;
+
+	/**是否为百分数 */
+	public boolean percent() default false;
+
+	/** 容许为null,默认为true */
+	public boolean nullable() default true;
+
+	/** 容许列表中间有空白符,默认为true */
+	public boolean blankAllowable() default true;
+
+}

+ 87 - 0
service-base/src/main/java/com/simuwang/base/common/conts/AccountingItems.java

@@ -0,0 +1,87 @@
+package com.simuwang.base.common.conts;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+
+/**
+ * @author mozuwen
+ * @date 2024/03/19
+ * @describe 会计科目
+ */
+public class AccountingItems {
+
+    /**
+     * 现金类
+     */
+    public final static HashSet<String> CASH = toHashSet("1002,1051,1204,1021,1031,1114,310211,1121,1132,1203,1207,"
+            + "1221,1304,1501,1522,1531,1532,3003,3999,2203,2204,2205,2206,2207,2210,2221,2231,2241,2261,2211,2331,2214");
+
+    /**
+     * 债券类
+     */
+    public final static HashSet<String> BOND = toHashSet("1107,1113,1321,1303,1401,1461,1104");
+
+    /**
+     * 其他
+     */
+    public final static HashSet<String> OTHER = toHashSet("1115,1503,1511");
+
+    /**
+     * 权证
+     */
+    public final static String WARRANT = "1106";
+
+    /**
+     * 股票类
+     */
+    public final static HashSet<String> EQUITY_LIST = toHashSet("1102,2101");
+
+    /**
+     * 债券类: 1202-正回购,2202-逆回购
+     */
+    public final static HashSet<String> BOND_LIST = toHashSet("1103,1202,2202");
+
+    /**
+     * 基金类
+     */
+    public final static List<String> FUND_LIST = toList("1105,1108,1109,1101");
+
+    /**
+     * 期货及衍生品类
+     */
+    public final static List<String> DERIVATIVES = toList("3102,3201,3103,3101");
+
+    /**
+     * 股指期货
+     */
+    public final static HashSet<String> INDEX_FUTRUE = toHashSet("IZ,IH,IF,IC,IM");
+
+    /**
+     * 国债期货
+     */
+    public final static HashSet<String> BOND_FUTRUE = toHashSet("TS,TL,TF,T");
+
+    /**
+     * 商品期货
+     */
+    public final static HashSet<String> CMDT_FUTRUE = toHashSet("SI,LC,ZC,WT,WH,UR,TA,SR,SM,SH,SF,SA,RS,RM,RI,PX,PM,PK,PF,OI,MA,LR,JR,GN,FG," +
+            "CY,CJ,CF,AP,Y,V,S,RR,PP,PG,P,M,LH,L,JM,JD,J,I,FB,EG,EB,CS,C,BB,B,A,SCTAS,SC,NR,LU,EC,BC,ZN,WR,SS,SP,SN,RU,RB,PB,NI,HC,FU,CU,BU,BR,AU,AO,AL,AG");
+
+
+    private static List<String> toList(String code) {
+        if (null == code || "".equals(code.trim())) {
+            return null;
+        }
+        return Arrays.asList(code.replaceAll("\\s+", "").split(","));
+    }
+
+
+    private static HashSet<String> toHashSet(String string) {
+        String[] arr = string.replaceAll("\\s+", "").split(",");
+        HashSet<String> set = new HashSet<>(arr.length);
+		set.addAll(Arrays.asList(arr));
+        return set;
+    }
+}

+ 98 - 0
service-base/src/main/java/com/simuwang/base/common/conts/AccountingNewItems.java

@@ -0,0 +1,98 @@
+package com.simuwang.base.common.conts;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+
+/**
+ * @author mozuwen
+ * @date 2024/03/19
+ * @describe 会计科目
+ */
+public class AccountingNewItems {
+
+    /**
+     * 现金类
+     */
+    public final static HashSet<String> CASH = toHashSet("1002,1021,1031,3003");
+
+    /**
+     * 股票类
+     */
+    public final static HashSet<String> EQUITY_LIST = toHashSet("1102,2101");
+
+    /**
+     * 债券类
+     */
+    public final static HashSet<String> BOND_LIST = toHashSet("1103,1104");
+
+    /**
+     * 正逆回购: 1202-逆回购,2202-正回购
+     */
+    public final static HashSet<String> BOND_BACK_LIST = toHashSet("1202,2202");
+
+    /**
+     * 基金类
+     */
+    public final static List<String> FUND_LIST = toList("1105,1107,1108,1109,1110");
+
+    /**
+     * 期货及衍生品类
+     */
+    public final static List<String> DERIVATIVES = toList("3102,3201,3103,3101");
+
+    /**
+     * 权证
+     */
+    public final static String WARRANT = "1106";
+
+    /**
+     * 其他
+     */
+    public final static HashSet<String> OTHER = toHashSet("4,5");
+
+    /**
+     * 其他资产
+     */
+    public final static HashSet<String> OTHER_ASSET = toHashSet("1");
+
+    /**
+     * 其他
+     */
+    public final static HashSet<String> OTHER_DEBT = toHashSet("2");
+
+    /**
+     * 股指期货
+     */
+    public final static HashSet<String> INDEX_FUTRUE = toHashSet("IZ,IH,IF,IC,IM");
+
+    /**
+     * 国债期货
+     */
+    public final static HashSet<String> BOND_FUTRUE = toHashSet("TF,0T");
+
+    /**
+     * 商品期货
+     */
+    public final static HashSet<String> CMDT_FUTRUE = toHashSet("TC,SR,SM,SF,RS,RM,TA,FG,MA,V,FB,M,PP,Y,BB,JM,P,I,J,L,CU,AL,RU,ZN,RB,"
+            + "WR,AG,AU,BU,HC,PB,IF,A,B,IM,RO,FU,TF,JD,PM,"
+            + "OI,RI,WH,JR,CF,C,LR,TT-S,TM,EF-S,AF-S,WS,WT,CS,IFM,T,SN,NI,IC,IH,ZC,ER,TS-S,SC,CY,AP,TS,SP,FR007_3M,ShiborON_1M,"
+            + "Shibor3M_3M,CDB10,Shibor1W_3M,CDB3,CDB5,EG,TL-S,CJ,UR,NR,RR,SS,EB,SA,SCTAS,ME");
+
+
+    private static List<String> toList(String code) {
+        if (null == code || "".equals(code.trim())) {
+            return null;
+        }
+        return Arrays.asList(code.replaceAll("\\s+", "").split(","));
+    }
+
+
+    private static HashSet<String> toHashSet(String string) {
+        String[] arr = string.replaceAll("\\s+", "").split(",");
+        HashSet<String> set = new HashSet<>(arr.length);
+        set.addAll(Arrays.asList(arr));
+        return set;
+    }
+}

+ 59 - 0
service-base/src/main/java/com/simuwang/base/common/conts/HoldingType.java

@@ -0,0 +1,59 @@
+package com.simuwang.base.common.conts;
+
+import java.util.stream.Stream;
+
+
+public enum HoldingType {
+    Cash(2, "现金"),
+    CostCash(20, "冲抵现金(估值增值)"),
+    Interest(22, "应计利息"),
+    Stock(0, "股票"),
+    StockSZH(110, "深港通"),
+    StockSHH(111, "沪港通"),
+    StockHGT(112, "港股通"),
+    Bond(1, "债券"),
+    BondBack(5, "回购"),
+    BondBackRepo(51, "正回购"),
+    BondBackReverseRepo(52, "逆回购"),
+    Derivatives(7, "其他衍生品"),
+    Future(3, "期货"),
+    IndexFuture(31, "股指期货"),
+    CmdtFuture(32, "商品期货"),
+    BondFuture(33, "国债期货"),
+    Option(4, "期权"),
+    ETFOption(40, "ETF期权"),
+    IndexOption(41, "股指期权"),
+    CmdtOption(42, "商品期权"),
+    Fund(6, "基金"),
+    FundPrivate(61, "私募基金"),
+    Warrant(21, "权证"),
+    Other(99, "其他"),
+    OTHER_ASSET(101, "其他资产"),
+    OTHER_DEBT(102, "其他负债"),
+    All(-1, "全部"),//方便过滤
+    Tail(100, "尾部");
+
+    private final int id;
+    private final String name;
+
+    public int getId() {
+        return id;
+    }
+
+    HoldingType(int id, String name2) {
+        this.id = id;
+        this.name = name2;
+    }
+
+    public static HoldingType getHoldingType(int id) {
+        return Stream.of(HoldingType.values()).filter(e -> e.id == id).findFirst().orElse(null);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isTrue(int stockType) {
+        return this.id == stockType;
+    }
+}

+ 20 - 0
service-base/src/main/java/com/simuwang/base/common/conts/ValuationConst.java

@@ -0,0 +1,20 @@
+package com.simuwang.base.common.conts;
+
+/**
+ * @author Rain
+ * @date 2023/6/5 15:20
+ * @description
+ */
+public class ValuationConst {
+
+    /**
+     * 资产总值
+     */
+    public static final String[] TOTAL_MARKET_VALUE = {"资产合计", "资产类合计", "资产类合计:", "资产类合计:"};
+
+    /**
+     * 资产净值  ----减去了资产负债的
+     */
+    public static final String[] NET_ASSET_VALUE = {"资产净值", "基金资产净值", "基金资产净值:", "基金资产净值:", "资产资产净值",
+            "资产资产净值:", "资产资产净值:", "信托资产净值", "信托资产净值:", "信托资产净值:"};
+}

+ 39 - 0
service-base/src/main/java/com/simuwang/base/common/enums/MarketTradIngCodeEnum.java

@@ -0,0 +1,39 @@
+package com.simuwang.base.common.enums;
+
+/**
+ * @author mozuwen
+ * @date 2024/05/21
+ * @describe 交易场所
+ */
+public enum MarketTradIngCodeEnum {
+    OTC(0, "场外"),
+    SHFE(10, "上海期货交易所"),
+    INE(11, "上海国际能源交易中心"),
+    DCE(13, "大连商品交易所"),
+    CZC(15, "郑州商品交易所"),
+    GZFE(17, "广州期货交易所"),
+    NEEQ(18, "北京证券交易所"),
+    CFFEX_FUTURE(20, "中国金融期货交易所"),
+    CFFEX_OPTION(51, "中国金融期货交易所"),
+    HKEX(72, "香港证券交易所"),
+    NTBC(81, "新三板"),
+    SSE(83, "上海证券交易所"),
+    IBM(89, "银行间债券市场"),
+    SZSE(90, "深圳证券交易所");
+
+    private final int marketId;
+    private final String name;
+
+    MarketTradIngCodeEnum(int marketId, String name) {
+        this.marketId = marketId;
+        this.name = name;
+    }
+
+    public int getMarketId() {
+        return marketId;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 910 - 0
service-base/src/main/java/com/simuwang/base/common/util/ValuationBusinessUtils.java

@@ -0,0 +1,910 @@
+package com.simuwang.base.common.util;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
+import com.simuwang.base.ano.Excel;
+import com.simuwang.base.pojo.valuation.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.openxml4j.util.ZipSecureFile;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author Rain
+ * @date 2023/6/5 10:02
+ * @description
+ */
+@Slf4j
+public class ValuationBusinessUtils {
+
+    private static final String ERROR_MSG_NOT_EXCEL_FILE = "文件格式错误";
+
+    private static final String ERROR_MSG_NOT_MATCH_TEMPLATE = "估值表模板暂不支持,请联系管理员";
+
+    private static final String ERROR_MSG_NO_DATA = "无数据";
+
+    private static final String ERROR_MSG_NOT_NUMBER = "非数值数据,请检查模板是否匹配";
+
+    private static final String ERROR_MSG_NOT_MARKET_VALUE_OR_AMOUNT = "无市值列或者无数量列,无法导入";
+
+    public static PreAssetsValuationInfo<PreAssetsValuationBase> importFile(ValuationNeedParseParam valuationNeedParseParam) {
+        /*
+         * 异常: 1.扩展名非Excel 2.非Excel,恶意篡改扩展名 3.没有找到匹配的模板 4.空数据
+         */
+        PreAssetsValuationInfo<PreAssetsValuationBase> preInfo = new PreAssetsValuationInfo<>();
+        PreAssetsValuationInfo<PreAssetsValuationBase>.Error error = null;
+
+        File file = valuationNeedParseParam.getFile();
+        assert file != null;
+        InputStream inputStream = file2InStream(file);
+        Workbook workbook = null;
+        assert inputStream != null;
+        ZipSecureFile.setMinInflateRatio(0.001);
+        boolean is2007ExcelFile = is2007ExcelFile(file);
+        try {
+            try {
+                workbook = is2007ExcelFile ? new XSSFWorkbook(inputStream) : new HSSFWorkbook(inputStream);
+            } catch (Exception e) {
+                log.error("excel first parse error: {}", ExceptionUtil.stacktraceToString(e));
+            }
+
+            // 非Excel,恶意篡改扩展名
+            if (null == workbook) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_EXCEL_FILE);
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 主动探测
+            ExcelInfo excelInfo = getExcelInfo(sheet);
+
+            // 没有找到匹配的模板
+            if (excelInfo.getType() == null) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE);
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+
+            preInfo.setType(excelInfo.getType());
+
+            List<String> headerInfo = excelInfo.getHeaderInfo();
+            // 没有找到匹配的模板
+            if (CollectionUtil.isEmpty(headerInfo)) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE);
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+
+            PreAssetsValuationInfo<PreAssetsValuationBase>.Header header = preInfo.new Header();
+            getHeader(excelInfo, header);
+
+            // 未找到估值表标题
+            if (StringUtils.isEmpty(header.getTitle())) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE + ":未找到估值表标题");
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+            // 未找到估值日期
+            if (header.getValuationDate() == null) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE + ":未找到估值日期");
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+            // 未找到有效净值
+            if (header.getUnitNetValue() <= 0) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE + ":未找到有效净值");
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+
+            if (excelInfo.getHeaderRows() == 0 || excelInfo.getTitleRows() == 0) {
+                error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NOT_MATCH_TEMPLATE + ":未找到表头信息");
+                error.setExcelName(valuationNeedParseParam.getOriginFileName());
+                preInfo.setError(error);
+                return preInfo;
+            }
+
+            preInfo.setHeader(header);
+            preInfo.setFooter(excelInfo.getFooterInfo());
+            preInfo.setUserId("0");
+
+            preInfo.setExcelOriginName(valuationNeedParseParam.getOriginFileName());
+
+            extractDataV2(sheet, excelInfo, preInfo);
+
+            // 校验成功,保存Excel
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            if (workbook != null) {
+                try {
+                    workbook.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return preInfo;
+    }
+
+    private static boolean is2007ExcelFile(File file) {
+        try (InputStream inputStream = new FileInputStream(file)) {
+            byte[] headerBytes = new byte[8];
+            inputStream.read(headerBytes, 0, 8);
+            return headerBytes[0] == 0x50 && headerBytes[1] == 0x4B && headerBytes[2] == 0x03 && headerBytes[3] == 0x04;
+        } catch (Exception e) {
+            log.error("异常={}", e);
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void extractDataV2(Sheet sheet, ExcelInfo excelInfo, PreAssetsValuationInfo<PreAssetsValuationBase> preInfo) {
+
+        //要使用修正后的ExcelEntity
+        Map<String, ExcelEntity> fieldMap = excelInfo.getFieldMap();
+        // 列名所在列号
+        Map<String, Integer> cellIndexMap = excelInfo.getCellIndexMap();
+
+        List<PreAssetsValuationBase> dataList = new ArrayList<>();
+        @SuppressWarnings("rawtypes")
+        PreAssetsValuationInfo.Error error = null;
+
+        int startRowNum = 0, endRowNum = 0, lastRowNum = sheet.getLastRowNum();
+        startRowNum = excelInfo.getHeaderRows() + excelInfo.getTitleRows();
+        endRowNum = excelInfo.getFooterRows();
+
+        if (endRowNum > 0) {
+            lastRowNum = (lastRowNum - endRowNum - 1);
+        }
+
+        for (int rowNum = startRowNum; rowNum <= lastRowNum; rowNum++) {
+            Row row = sheet.getRow(rowNum);
+
+            if (null == row) {
+                continue;
+            }
+
+            // 校验空白行
+            boolean isRowBlank = true;
+            for (int cellNum = 0; cellNum < row.getLastCellNum(); cellNum++) {
+                Cell cell = row.getCell(cellNum);
+                if (null == cell) {
+                    continue;
+                }
+
+                String value;
+                switch (cell.getCellTypeEnum()) {
+                    case STRING:
+                        value = cell.getStringCellValue();
+                        break;
+                    case NUMERIC:
+                        value = cell.getNumericCellValue() + "";
+                        break;
+                    default:
+                        value = "";
+                }
+                if (StringUtils.isNotEmpty(value)) {
+                    isRowBlank = false;
+                    break;
+                }
+            }
+
+            if (isRowBlank) {
+                continue;
+            }
+
+            // 提取数据
+            try {
+                PreAssetsValuationGeneralTemplate instance = new PreAssetsValuationGeneralTemplate();
+
+                boolean isInstanceValid = false;
+                for (String field : cellIndexMap.keySet()) {
+                    int cellIndex = cellIndexMap.get(field);
+                    Cell cell = row.getCell(cellIndex);
+                    if (null == cell) {
+                        continue;
+                    }
+
+                    //当表头带%,但是表格是真实小数,却被格式化了带%的百分数,代码获取的依旧是真实小数
+                    //百分数4位小数: 0.0000%
+                    boolean isPercentFormat = false;
+                    String dataFormatString = cell.getCellStyle().getDataFormatString();
+                    if (null != dataFormatString && dataFormatString.contains("%")) {
+                        isPercentFormat = true;
+                    }
+
+                    String value;
+                    switch (cell.getCellTypeEnum()) {
+                        case STRING:
+                            value = cell.getStringCellValue();
+                            // 特殊处理原因:excel常规类型 1105 会被识别为数字类型,值变为"1105.0" -> 样例估值表:492194交银优享平衡202101号专户资产估值表2021102120211201_103054_488.xlsx
+                            if (value.endsWith(".0")) {
+                                value = value.replace(".0", "");
+                            }
+                            break;
+                        case NUMERIC:
+                            value = cell.getNumericCellValue() + "";
+                            break;
+                        case BLANK:
+                            value = "";
+                            break;
+                        case _NONE:
+                            value = "";
+                            break;
+                        default:
+                            value = "";
+                    }
+                    String cellValue = value;
+
+                    if (StringUtils.isBlank(cellValue)) {
+                        continue;
+                    }
+
+                    ExcelEntity excelEntity = fieldMap.get(field);
+
+                    if (excelEntity.isBlankAllowable()) {
+                        cellValue = removeBlank(cellValue);
+                    }
+
+                    // 校验数值
+                    if (excelEntity.isNumerical() && isNotNumber(cellValue)) {
+                        error = preInfo.new Error();
+                        // 注意,row,cell均从0开始
+                        error.setRowNum(rowNum + 1).setCellNum(cellIndex + 1).setMsg(ERROR_MSG_NOT_NUMBER + ":" + cellValue);
+                        error.setExcelName(preInfo.getExcelOriginName());
+                        preInfo.setError(error);
+                        return;
+                    }
+
+                    List<Method> methodList = getMethods(instance.getClass());
+                    for (Method method : methodList) {
+                        if (method.getName().equals("set" + firstUpperCase(field))) {
+                            method.setAccessible(true);
+
+                            if (excelEntity.isNumerical()) {
+                                if (isPercentFormat) {
+                                    method.invoke(instance, toPlainNumber(cellValue));
+                                } else if ("ratioOfCostToNetWorth".equals(field)) {
+                                    //特殊处理 ratioOfCostToNetWorth
+                                    method.invoke(instance, toPlainNumberSpecial(excelEntity, cellValue));
+                                } else if ("ratioOfMarketValueToNetWorth".equals(field)) {
+                                    //特殊处理 ratioOfMarketValueToNetWorth
+                                    method.invoke(instance, toPlainNumberSpecial(excelEntity, cellValue));
+                                } else {
+                                    method.invoke(instance, toPlainNumber(cellValue));
+                                }
+                            } else {
+                                method.invoke(instance, cellValue);
+                            }
+
+                            isInstanceValid = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (isInstanceValid) {
+                    dataList.add(instance);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        preInfo.setData(dataList);
+        if (dataList.size() == 0) {
+            error = preInfo.new Error().setCellNum(0).setRowNum(0).setMsg(ERROR_MSG_NO_DATA);
+            error.setExcelName(preInfo.getExcelOriginName());
+            preInfo.setError(error);
+        } else {
+            long marketValueCount = dataList.stream().filter(p -> StringUtils.isEmpty(p.getMarketValue())).count();
+            long amountCount = dataList.stream().filter(p -> StringUtils.isEmpty(p.getAmount())).count();
+            if (marketValueCount == dataList.size() || amountCount == dataList.size()) {
+                error = preInfo.new Error().setMsg(ERROR_MSG_NOT_MARKET_VALUE_OR_AMOUNT);
+                error.setExcelName(preInfo.getExcelOriginName());
+                preInfo.setError(error);
+            }
+        }
+    }
+
+    /**
+     * 特殊处理带%的列
+     **/
+    private static String toPlainNumberSpecial(ExcelEntity excelEntity, String value) {
+        if (StringUtils.isEmpty(value) || "0".equals(value)) {
+            return value;
+        }
+        if ("%".equals(value)) {
+            return "0";
+        }
+        String num = value.replace(",", "").replace(",", "");
+        if (value.endsWith("%")) {
+            num = value.replace("%", "") + "e-2";
+        }
+        //如果本身是科学计数法
+        else if (value.toLowerCase().contains("e")) {
+            try {
+                num = new BigDecimal(value).toPlainString();
+                num += "e-2";
+            } catch (Exception e) {
+                log.error("转换错误:" + num);
+            }
+        } else {
+            if (excelEntity.isPercent()) {
+                num += "e-2";
+            }
+        }
+        try {
+            return new BigDecimal(num).toPlainString();
+        } catch (Exception e) {
+            log.error("转换错误:" + num);
+            e.printStackTrace();
+        }
+        return value;
+    }
+
+    /**
+     * 主要用于将科学计数法转换为普通数值
+     */
+    public static String toPlainNumber(String value) {
+        if (StringUtils.isEmpty(value) || "0".equals(value)) {
+            return value;
+        }
+        if ("%".equals(value)) {
+            return "0";
+        }
+        String num = value.replace(",", "").replace(",", "");
+        if (value.endsWith("%")) {
+            num = value.replace("%", "") + "e-2";
+        }
+
+        try {
+            return new BigDecimal(num).toPlainString();
+        } catch (Exception e) {
+            log.error("转换错误:" + num);
+            e.printStackTrace();
+        }
+        return value;
+    }
+
+    private static String firstUpperCase(String str) {
+        return (str.charAt(0) + "").toUpperCase() + str.substring(1);
+    }
+
+    private static List<Method> getMethods(Class<? extends PreAssetsValuationBase> clazz) {
+        Method[] subMethods = clazz.getDeclaredMethods();
+        Method[] superMethods = clazz.getSuperclass().getDeclaredMethods();
+
+        final List<Method> subList = Arrays.asList(subMethods);
+        // 过滤父类中被覆盖的方法
+        List<Method> superList = Arrays.asList(superMethods).stream().filter(superM -> {
+            boolean anyMatch = subList.stream().anyMatch(subM -> subM.getName().equals(superM.getName()));
+            return !anyMatch;
+        }).collect(Collectors.toList());
+        superList.addAll(subList);
+        return superList;
+    }
+
+    /**
+     * 支持正负:int 或 double 或带%数值
+     */
+    public static boolean isNotNumber(String str) {
+        return !isNumber(str);
+    }
+
+    /**
+     * 支持正负:int 或 double 或带%数值, 科学计数法
+     */
+    public static boolean isNumber(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return false;
+        }
+        if ("%".equals(str)) {
+            return true;
+        }
+        str = str.replace(",", "").replace(",", "");
+        //容许没有整数部分 eg:.2
+        String regex = "^-?(\\d+)?(\\.)?\\d+%?$";
+
+        boolean judge = str.matches(regex);
+        // 非常规数值,可能为科学计数法
+        if (!judge) {
+            try {
+                String plainString = new BigDecimal(str).toPlainString();
+                log.info("number is BigDecimal...,number -> {}", plainString);
+            } catch (Exception e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static void getHeader(ExcelInfo excelInfo, PreAssetsValuationInfo<PreAssetsValuationBase>.Header header) {
+        List<String> headerInfo = excelInfo.getHeaderInfo();
+
+        header.setHeaderList(headerInfo);
+        for (int i = 0; i < headerInfo.size(); i++) {
+            String str = removeBlank(headerInfo.get(i));
+            if (str.contains("估值表")) {
+                if (null == header.getTitle()) {
+                    header.setTitle(str);
+                }
+            } else if (str.contains("专用表")) {
+                if (null == header.getSubTitle()) {
+                    header.setSubTitle(str);
+                }
+            } else if (str.contains("估值日期") || str.contains("日期")) {
+                if (null == header.getValuationDate()) {
+                    Date valuationDate = getDateFromString(str);
+                    //估值日期字段名 和 估值日期值 分为两个单元格的情况,此时取下一个单元格的值作为估值日期值
+                    if (null == valuationDate && (i + 1) < headerInfo.size()) {
+                        valuationDate = getDateFromString(removeBlank(headerInfo.get(i + 1)));
+                    }
+                    header.setValuationDate(valuationDate);
+                }
+            } else if (str.contains("今日单位净值") || str.contains("单位净值") || str.contains("净值")) {
+                //header.getUnitNetValue() <= 0 代表还未识别
+                if (header.getUnitNetValue() <= 0) {
+                    double nav = extraNumberFromString(str);
+                    //单位净值字段名 和 单位净值值 分为两个单元格的情况,此时取下一个单元格的值作为单位净值
+                    if (nav == -1D && (i + 1) < headerInfo.size()) {
+                        nav = extraNumberFromString(removeBlank(headerInfo.get(i + 1)));
+                    }
+                    header.setUnitNetValue(nav);
+                }
+            }
+        }
+
+        if (null == header.getTitle()) {
+            header.setTitle(headerInfo.get(0));
+        }
+
+        //从表头中提取日期
+        if (null == header.getValuationDate()) {
+            for (String str : headerInfo) {
+                Date date = getDateFromString(str);
+                if (null != date) {
+                    header.setValuationDate(date);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * 从字符串中提取日期,返回该日期的中午12:00 <br/>
+     * 支持日期格式: <br/>
+     * 2017-10-17 <br/>
+     * 2017/10/17 <br/>
+     * 2017年10月17日 <br/>
+     * 20170703
+     */
+    public static Date getDateFromString(String str) {
+        String regex = "(19\\d{2}|20\\d{2})[-/年]?(\\d{1,2})[-/月]?(\\d{1,2})";
+        Matcher matcher = Pattern.compile(regex).matcher(str);
+        if (matcher.find()) {
+            int year = Integer.parseInt(matcher.group(1));
+            // 注意,月份从0开始
+            int month = Integer.parseInt(matcher.group(2)) - 1;
+            int day = Integer.parseInt(matcher.group(3));
+
+            Calendar calendar = Calendar.getInstance();
+            calendar.set(year, month, day, 12, 00, 00);
+            return calendar.getTime();
+        }
+        return null;
+    }
+
+    /**
+     * 主动探测 Excel非数据部分info
+     */
+    private static ExcelInfo getExcelInfo(Sheet sheet) {
+        ExcelInfo info = new ExcelInfo();
+
+        List<String> headerInfo = new ArrayList<>();
+        List<String> footerInfo = new ArrayList<>();
+        List<String> titleList = new ArrayList<>();
+
+        Map<String, ExcelEntity> fieldsMap = getFieldsAndExcelAnnotationMap(PreAssetsValuationGeneralTemplate.class);
+        info.setFieldMap(fieldsMap);
+
+        // 列名所在列号
+        Map<String, Integer> cellIndexMap = new LinkedHashMap<>();
+
+        String netValue = null;
+        for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
+            Row row = sheet.getRow(rowNum);
+            if (null == row) {
+                continue;
+            }
+            for (int cellNum = 0; cellNum < row.getLastCellNum(); cellNum++) {
+                Cell cell = row.getCell(cellNum);
+                if (null == cell) {
+                    continue;
+                }
+
+                String value;
+                switch (cell.getCellTypeEnum()) {
+                    case STRING:
+                        value = cell.getStringCellValue();
+                        break;
+                    case NUMERIC:
+                        cell.setCellType(CellType.STRING);
+                        value = cell.getStringCellValue();
+                        break;
+                    default:
+                        value = "";
+                }
+                String cellValue = removeBlank(value);
+
+                if (StringUtils.isEmpty(cellValue)) {
+                    continue;
+                }
+
+                //合并的单元格这里有个坑,表面上合并了,实际每个单元格里合并前是什么值,合并后还是什么值
+                if (ImportParams.isEqualHeaderIdentify(cellValue)) {
+                    MergedRegion mergedRegion = isMergedRegion(sheet, rowNum, cellNum);
+                    if (mergedRegion.isMerged()) {
+                        info.setTitleRows(mergedRegion.getLastRowNum() - mergedRegion.getFirstRowNum() + 1);
+
+                        info.setHeaderRows(mergedRegion.getFirstRowNum());
+                    } else {
+                        info.setTitleRows(1);
+                        info.setHeaderRows(rowNum);
+                    }
+
+                } else if (ImportParams.isContainFooterIdentify(cellValue)) {
+                    info.setFooterRows(sheet.getLastRowNum() - rowNum);
+                }
+
+                if (info.getHeaderRows() == 0) {
+                    if (null == netValue) {
+                        if (cellValue.contains("今日单位净值") || cellValue.contains("单位净值") || cellValue.contains("今日净值")) {
+                            double netValueTemp = extraNumberFromString(cellValue);
+                            if (netValueTemp > 0) {
+                                netValue = cellValue;
+                            }
+                        }
+                    }
+
+                    headerInfo.add(cellValue);
+                }
+                if (info.getFooterRows() > 0) {
+                    footerInfo.add(cellValue);
+                }
+
+                if (rowNum >= info.getHeaderRows() && rowNum < info.getHeaderRows() + info.getTitleRows()) {
+
+                    for (Map.Entry<String, ExcelEntity> fm : fieldsMap.entrySet()) {
+                        String field = fm.getKey();
+                        ExcelEntity excelEntity = fm.getValue();
+                        String[] values = excelEntity.getValue();
+
+                        boolean shouldBreak = false;
+
+                        for (String v : values) {
+                            if (v.contains(":")) {
+                                String[] sCell = v.split(":");
+                                if (!sCell[1].equals(cellValue)) {
+                                    continue;
+                                }
+
+                                //find the previous row and the same cell index
+                                Row rowBefore = sheet.getRow(rowNum - 1);
+                                if (null == rowBefore) {
+                                    break;
+                                }
+                                Cell cellBefore = rowBefore.getCell(cellNum);
+                                if (null == cellBefore) {
+                                    break;
+                                }
+
+                                String cellBeforeValue = "";
+
+                                MergedRegion mergedRegion = isMergedRegion(sheet, rowNum - 1, cellNum);
+                                if (mergedRegion.isMerged()) {
+                                    boolean isOver = false;
+                                    for (int i = mergedRegion.getFirstRowNum(); i <= mergedRegion.getLastRowNum(); i++) {
+                                        Row tRow = sheet.getRow(i);
+                                        if (tRow == null) {
+                                            continue;
+                                        }
+                                        for (int j = mergedRegion.getFirstCellNum(); j <= mergedRegion.getLastCellNum(); j++) {
+                                            Cell tCell = tRow.getCell(j);
+                                            if (tCell == null) {
+                                                continue;
+                                            }
+
+                                            tCell.setCellType(CellType.STRING);
+                                            String tCellValue = removeBlank(tCell.getStringCellValue());
+                                            if (StringUtils.isNotEmpty(tCellValue)) {
+                                                cellBeforeValue = tCellValue;
+                                                isOver = true;
+                                                break;
+                                            }
+                                        }
+                                        if (isOver) {
+                                            break;
+                                        }
+                                    }
+                                } else {
+                                    cellBefore.setCellType(CellType.STRING);
+                                    cellBeforeValue = removeBlank(cellBefore.getStringCellValue());
+                                }
+
+                                if (sCell[0].equals(cellBeforeValue)) {
+                                    cellValue = v;
+
+                                    if (cellValue.contains("%")) {
+                                        excelEntity.setPercent(true);
+                                    }
+
+                                    cellIndexMap.put(field, cellNum);
+
+                                    shouldBreak = true;
+
+                                    break;
+                                }
+                            } else if (v.equals(cellValue)) {
+                                if (cellValue.contains("%")) {
+                                    excelEntity.setPercent(true);
+                                }
+                                cellIndexMap.put(field, cellNum);
+                                shouldBreak = true;
+                                break;
+                            }
+                        }
+                        if (shouldBreak) {
+                            break;
+                        }
+                    }
+                    titleList.add(cellValue);
+                } else {
+                    if (null == netValue) {
+                        if (cellValue.contains("单位") && cellValue.contains("净值")) {
+                            if (cellValue.equals("单位净值") || cellValue.contains("今日") || cellValue.contains("基金")) {
+                                //把它跟右边的cell一起拿过来
+                                double netValueTemp = extraNumberFromString(cellValue);
+                                if (netValueTemp > 0) {
+                                    netValue = cellValue;
+                                } else {
+                                    Cell cellRight = row.getCell(cellNum + 1);
+
+                                    if (null != cellRight) {
+                                        cellRight.setCellType(CellType.STRING);
+                                        String vDate = cellRight.getStringCellValue();
+                                        if (null != vDate && !"".equals(vDate.trim())) {
+                                            netValue = cellValue + vDate;
+                                            headerInfo.add(netValue);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (CollectionUtil.isEmpty(titleList)) {
+            info.setType(null);
+            return info;
+        }
+
+        info.setType(AssetsValuationType.GeneralTemplate);
+        info.setCellIndexMap(cellIndexMap);
+
+        if (info.getHeaderRows() > 0) {
+            info.setHeaderInfo(headerInfo);
+        }
+        if (info.getFooterRows() > 0) {
+            info.setFooterInfo(footerInfo);
+        }
+
+        return info;
+    }
+
+    /**
+     * 从字符串中提取数值:正负 int 或 double 或带%数值
+     */
+    public static double extraNumberFromString(String str) {
+        if (null == str) {
+            return -1D;
+        }
+        str = str.replace(",", "").replace(",", "");
+
+        String regex = "-?(\\d+)?(\\.)?\\d+";
+
+        Matcher matcher = Pattern.compile(regex).matcher(str);
+        if (matcher.find()) {
+            String value = matcher.group();
+
+            if (value.endsWith("%")) {
+                return Double.parseDouble(value.substring(0, value.length() - 1)) / 100D;
+            }
+
+            return Double.parseDouble(value);
+        }
+        return -1D;
+    }
+
+    /**
+     * 判断指定的单元格是否是合并单元格
+     */
+    private static MergedRegion isMergedRegion(Sheet sheet, final int rowNum, final int cellNum) {
+        final MergedRegion mergedRegion = new MergedRegion().setMerge(false);
+
+        int numMergedRegions = sheet.getNumMergedRegions();
+        if (numMergedRegions <= 0) {
+            return mergedRegion;
+        }
+
+        for (int i = 0; i < numMergedRegions; i++) {
+            CellRangeAddress region = sheet.getMergedRegion(i);
+            boolean rowMerge = region.getFirstRow() <= rowNum && rowNum <= region.getLastRow();
+            boolean cellMerge = region.getFirstColumn() <= cellNum && cellNum <= region.getLastColumn();
+            if (rowMerge && cellMerge) {
+                mergedRegion.setMerge(true).setFirstRowNum(region.getFirstRow()).setLastRowNum(region.getLastRow());
+                mergedRegion.setFirstCellNum(region.getFirstColumn()).setLastCellNum(region.getLastColumn());
+                break;
+            }
+        }
+        return mergedRegion;
+    }
+
+    private static Map<String, ExcelEntity> getFieldsAndExcelAnnotationMap(Class<? extends PreAssetsValuationBase> clazz) {
+        // 处理父类
+        Map<String, ExcelEntity> superMap = getExcelFieldsMap(PreAssetsValuationBase.class);
+        // 处理子类
+        Map<String, ExcelEntity> clazzMap = getExcelFieldsMap(clazz);
+
+        // 子类覆盖父类相同属性
+        for (String key : superMap.keySet()) {
+            if (clazzMap.containsKey(key)) {
+                continue;
+            }
+            clazzMap.put(key, superMap.get(key));
+        }
+        return clazzMap;
+    }
+
+    private static <T> Map<String, ExcelEntity> getExcelFieldsMap(Class<T> clazz) {
+        Map<String, ExcelEntity> map = new HashMap<>();
+
+        Field[] fields = clazz.getDeclaredFields();
+        for (int i = 0; i < fields.length; i++) {
+            Field field = fields[i];
+            Excel excel = field.getDeclaredAnnotation(Excel.class);
+            if (excel == null) {
+                continue;
+            }
+
+            ExcelEntity excelEntity = new ExcelEntity();
+            excelEntity.setNumerical(excel.numerical());
+            excelEntity.setNullable(excel.nullable());
+            excelEntity.setBlankAllowable(excel.blankAllowable());
+            excelEntity.setPercent(excel.percent());
+
+            String values = excel.values();
+            if (!"".equals(values.trim())) {
+
+                String[] valuesArr = values.split(",");
+                if (excel.blankAllowable()) {
+                    excelEntity.setValue(removeBlank(valuesArr));
+                } else {
+                    excelEntity.setValue(valuesArr);
+                }
+
+            } else {
+
+                if (excel.blankAllowable()) {
+                    excelEntity.setValue(removeBlank(excel.value()));
+                } else {
+                    excelEntity.setValue(excel.value());
+                }
+            }
+
+            map.put(field.getName(), excelEntity);
+        }
+        return map;
+    }
+
+    public static String[] removeBlank(String[] array) {
+        for (int i = 0; i < array.length; i++) {
+            array[i] = array[i].replaceAll("\\s+", "");
+        }
+        return array;
+    }
+
+    public static String removeBlank(String str) {
+        return str.replaceAll("\\s+", "");
+    }
+
+    public static InputStream file2InStream(File file) {
+        try {
+            return new FileInputStream(file);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static class MergedRegion {
+        private boolean merge;
+        private int firstRowNum;
+        private int lastRowNum;
+        private int firstCellNum;
+        private int lastCellNum;
+
+        public boolean isMerged() {
+            return merge;
+        }
+
+        public MergedRegion setMerge(boolean merge) {
+            this.merge = merge;
+            return this;
+        }
+
+        public int getFirstRowNum() {
+            return firstRowNum;
+        }
+
+        public MergedRegion setFirstRowNum(int firstRowNum) {
+            this.firstRowNum = firstRowNum;
+            return this;
+        }
+
+        public int getLastRowNum() {
+            return lastRowNum;
+        }
+
+        public MergedRegion setLastRowNum(int lastRowNum) {
+            this.lastRowNum = lastRowNum;
+            return this;
+        }
+
+        public int getFirstCellNum() {
+            return firstCellNum;
+        }
+
+        public MergedRegion setFirstCellNum(int firstCellNum) {
+            this.firstCellNum = firstCellNum;
+            return this;
+        }
+
+        public int getLastCellNum() {
+            return lastCellNum;
+        }
+
+        public MergedRegion setLastCellNum(int lastCellNum) {
+            this.lastCellNum = lastCellNum;
+            return this;
+        }
+    }
+}

+ 149 - 0
service-base/src/main/java/com/simuwang/base/common/util/ValuationDataUtils.java

@@ -0,0 +1,149 @@
+package com.simuwang.base.common.util;
+
+import cn.hutool.core.util.StrUtil;
+import com.simuwang.base.common.conts.ValuationConst;
+import com.simuwang.base.pojo.valuation.PreAssetsValuationBase;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
+public class ValuationDataUtils {
+
+    /**
+     * 支持正负整型或浮点型
+     */
+    public static boolean isNumber(String str) {
+        return (StringUtils.isNotEmpty(str) && str.matches("^-?\\d+(\\.\\d+)?$"));
+    }
+
+    public static Double string2Double(String str) {
+        if (StrUtil.isBlank(str) || StrUtil.isBlank(str.trim())) {
+            return null;
+        }
+
+        str = str.trim().replace(",", "");
+        try {
+            if (str.endsWith("%")) {
+                return Double.parseDouble(str.substring(0, str.length() - 1)) / 100D;
+            }
+            return Double.parseDouble(str);
+        } catch (NumberFormatException e) {
+            log.error("parse Double error");
+        }
+        return 0D;
+    }
+
+    public static int string2Int(String str) {
+        if (str == null || "".equals(str.trim())) {
+            return 0;
+        }
+
+        try {
+            return Integer.parseInt(str.trim());
+        } catch (NumberFormatException e) {
+            log.error("parse Integer error");
+        }
+        return 0;
+    }
+
+    /**
+     * 判断字符串是否为空或者null
+     *
+     * @param str 要验证的参数
+     * @return boolean
+     */
+    public static boolean isValid(String str) {
+        return null != str && str.length() != 0 && !"null".equals(str.toLowerCase());
+    }
+
+    /**
+     * 判断字符串是有汉字
+     *
+     * @param str   待判断的字符串
+     * @param isAll true:判断该字符串是否是汉字,false:判断该字符串是否包含汉字
+     */
+    public static boolean hasChinese(String str, boolean isAll) {
+        Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+");
+        Matcher m = pattern.matcher(str);
+        if (isAll) {
+            return m.find() && m.group(0).equals(str);
+        } else {
+            return m.find();
+        }
+    }
+
+    public static String getSubjectCode(String str) {
+        char[] cArr = str.toCharArray();
+        if (!Character.isDigit(cArr[0])) {
+            return str;
+        }
+        int startIndex = 0;
+        for (int i = 0; i < cArr.length; i++) {
+            int index = cArr[i];
+            if ((index >= 48 && index <= 57) || (index >= 65 && index <= 90) || (index >= 97 && index <= 122)) {
+                continue;
+            }
+            startIndex = i;
+            break;
+        }
+        if (startIndex == 0) {
+            return str;
+        }
+        str = str.substring(0, startIndex);
+        return str;
+    }
+
+    public static List<PreAssetsValuationBase> removeUnderLine(List<PreAssetsValuationBase> list) {
+        for (int i = 0; i < list.size(); i++) {
+            list.get(i).setOriginalSubjectCode(list.get(i).getSubjectCode());
+            String subjectCode = list.get(i).getSubjectCode();
+            if (subjectCode != null) {
+                subjectCode = subjectCode.toUpperCase();
+
+                //含有中文不处理
+                if (subjectCode.matches(".*[\u4e00-\u9fa5]+.*")) {
+                    continue;
+                }
+                subjectCode = subjectCode.replaceAll("[\\.\\s_-]", "");
+            }
+
+            list.get(i).setSubjectCode(subjectCode);
+        }
+
+        return list;
+    }
+
+    /**
+     * 校验该科目是否是资产总值
+     *
+     * @param str 待校验字符串
+     * @return 检验结果
+     */
+    public static boolean checkMarketValue(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return false;
+        }
+        long count = Arrays.stream(ValuationConst.TOTAL_MARKET_VALUE).filter(p -> p.trim().equals(str)).count();
+        return count > 0;
+    }
+
+    /**
+     * 校验该科目是否是资产净值
+     *
+     * @param str 待校验字符串
+     * @return 检验结果
+     */
+    public static boolean checkNetAssetsValue(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return false;
+        }
+        long count = Arrays.stream(ValuationConst.NET_ASSET_VALUE).filter(p -> p.trim().equals(str)).count();
+        return count > 0;
+    }
+
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1206 - 0
service-base/src/main/java/com/simuwang/base/common/util/ValuationTableParseUtil.java


+ 35 - 0
service-base/src/main/java/com/simuwang/base/config/ThreadPoolConfig.java

@@ -0,0 +1,35 @@
+package com.simuwang.base.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+@Configuration
+@EnableAsync
+public class ThreadPoolConfig {
+
+    @Bean("valuationExecutor")
+    public ThreadPoolTaskExecutor valuationExecutor() {
+        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+        int cpuSize = Runtime.getRuntime().availableProcessors();
+        cpuSize = Math.max(cpuSize, 1);
+
+        taskExecutor.setCorePoolSize(cpuSize);
+        taskExecutor.setMaxPoolSize(50);
+        taskExecutor.setQueueCapacity(1024 * 4);
+        taskExecutor.setKeepAliveSeconds(60);
+        taskExecutor.setThreadNamePrefix("valuationExecutor--");
+        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
+        taskExecutor.setAwaitTerminationSeconds(60);
+
+        // 修改拒绝策略为使用当前线程执行
+        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 初始化线程池
+        taskExecutor.initialize();
+        return taskExecutor;
+    }
+
+}

+ 4 - 3
service-base/src/main/java/com/simuwang/base/mapper/FundAliasMapper.java

@@ -1,5 +1,6 @@
 package com.simuwang.base.mapper;
 
+import com.simuwang.base.pojo.dos.FundAliasDO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -8,9 +9,9 @@ import java.util.List;
 @Mapper
 public interface FundAliasMapper {
 
-    List<String> queryFundByNameAndRegisterNumber(@Param("fundName") String fundName, @Param("registerNumber") String registerNumber);
+    List<FundAliasDO> queryFundByNameAndRegisterNumber(@Param("fundName") String fundName, @Param("registerNumber") String registerNumber);
 
-    List<String> queryFundByName(@Param("fundName") String fundName);
+    List<FundAliasDO> queryFundByName(@Param("fundName") String fundName);
 
-    List<String> queryFundByRegisterNumber(@Param("registerNumber") String registerNumber);
+    List<FundAliasDO> queryFundByRegisterNumber(@Param("registerNumber") String registerNumber);
 }

+ 5 - 3
service-base/src/main/java/com/simuwang/base/mapper/FundInfoMapper.java

@@ -1,5 +1,6 @@
 package com.simuwang.base.mapper;
 
+import com.simuwang.base.pojo.dos.FundInfoDO;
 import com.simuwang.base.pojo.vo.FundInformationVO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -22,9 +23,10 @@ public interface FundInfoMapper {
                                                @Param("assetFrequency") Integer assetFrequency,@Param("startDate") Integer startDate,
                                                @Param("endDate")Integer endDate);
 
-    List<String> queryFundByNameAndRegisterNumber(@Param("fundName")String fundName, @Param("registerNumber")String registerNumber);
+    List<FundInfoDO> queryFundByNameAndRegisterNumber(@Param("fundName")String fundName, @Param("registerNumber")String registerNumber);
 
-    List<String> queryFundByName(@Param("fundName") String fundName);
+    List<FundInfoDO> queryFundByName(@Param("fundName") String fundName);
+
+    List<FundInfoDO> queryFundByRegisterNumber(@Param("registerNumber") String registerNumber);
 
-    List<String> queryFundByRegisterNumber(@Param("registerNumber") String registerNumber);
 }

+ 8 - 0
service-base/src/main/java/com/simuwang/base/mapper/FundPositionDetailMapper.java

@@ -1,8 +1,16 @@
 package com.simuwang.base.mapper;
 
+import com.simuwang.base.pojo.dos.FundPositionDetailDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 @Mapper
 public interface FundPositionDetailMapper {
 
+    void deleteUnUsed(@Param("fundId") String fundId, @Param("valuationDate") String valuationDate);
+
+    int insertMulti(@Param("details") List<FundPositionDetailDO> segment);
+
 }

+ 11 - 0
service-base/src/main/java/com/simuwang/base/mapper/PdfValuationRecordMapper.java

@@ -1,8 +1,19 @@
 package com.simuwang.base.mapper;
 
+import com.simuwang.base.pojo.dos.PdfValuationRecordDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 @Mapper
 public interface PdfValuationRecordMapper {
 
+    /**
+     * 批量插入用户pdf估值表文件记录
+     *
+     * @param pdfValuationRecordDoList 用户pdf估值表文件记录Do
+     */
+    void batchInsert(@Param("itemDoList") List<PdfValuationRecordDO> pdfValuationRecordDoList);
+
 }

+ 3 - 0
service-base/src/main/java/com/simuwang/base/mapper/ValuationMarketCodeMapper.java

@@ -2,7 +2,10 @@ package com.simuwang.base.mapper;
 
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface ValuationMarketCodeMapper {
 
+    List<String> queryMarketCode();
 }

+ 16 - 0
service-base/src/main/java/com/simuwang/base/mapper/ValuationTableAttributeMapper.java

@@ -0,0 +1,16 @@
+package com.simuwang.base.mapper;
+
+import com.simuwang.base.pojo.dos.ValuationTableAttributeDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface ValuationTableAttributeMapper {
+
+    void deleteByValuationId(@Param("valuationId") Integer valuationId);
+
+    void batchInsert(@Param("itemDoList") List<ValuationTableAttributeDO> valuationTableAttributeDOList);
+
+}

+ 9 - 0
service-base/src/main/java/com/simuwang/base/mapper/ValuationTableMapper.java

@@ -1,8 +1,17 @@
 package com.simuwang.base.mapper;
 
+import com.simuwang.base.pojo.dos.ValuationTableDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 @Mapper
 public interface ValuationTableMapper {
 
+    void unValid(@Param("fundId") String fundId, @Param("valuationDate") String date);
+
+    int insert(ValuationTableDO record);
+
+    void updateUpdateAnalyzed(@Param("fundId") String fundId,
+                              @Param("valuationDate") String valuationDate, @Param("hasStock") Integer hasStock,
+                              @Param("hasBond") Integer hasBond, @Param("hasFuture") Integer hasFuture);
 }

+ 10 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/EmailFundAssetDO.java

@@ -57,6 +57,16 @@ public class EmailFundAssetDO {
     @TableField(value = "asset_net")
     private BigDecimal assetNet;
     /**
+     * 是否入库 0-没有,1-有
+     */
+    @TableField(value = "is_stored")
+    private Integer isStored;
+    /**
+     * 异常情况:1-无异常,2-资产净值<=0
+     */
+    @TableField(value = "exception_status")
+    private Integer exceptionStatus;
+    /**
      * 记录的有效性;1-有效;0-无效
      */
     @TableField(value = "isvalid")

+ 4 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/FundPositionDetailDO.java

@@ -61,6 +61,10 @@ public class FundPositionDetailDO {
      */
     @TableField(value = "sec_type")
     private Integer secType;
+
+    private Integer subType;
+    private Integer marketId;
+
     /**
      * 证券数量
      */

+ 134 - 0
service-base/src/main/java/com/simuwang/base/pojo/dos/ValuationTableAttributeDO.java

@@ -0,0 +1,134 @@
+package com.simuwang.base.pojo.dos;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("valuation_table_attribute")
+public class ValuationTableAttributeDO {
+    /**
+     * 主键id
+     */
+    @TableId(value = "id")
+    private Integer id;
+    /**
+     * 估值表id(valuation_table.id)
+     */
+    @TableField(value = "valuation_id")
+    private Integer valuationId;
+    /**
+     * 科目代码
+     */
+    @TableField(value = "subject_code")
+    private String subjectCode;
+    /**
+     * 科目名称
+     */
+    @TableField(value = "subject_name")
+    private String subjectName;
+    /**
+     * 币别
+     */
+    @TableField(value = "currency")
+    private String currency;
+    /**
+     * 汇率
+     */
+    @TableField(value = "exchange_rate")
+    private BigDecimal exchangeRate;
+    /**
+     * 证券数量
+     */
+    @TableField(value = "securities_amount")
+    private BigDecimal securitiesAmount;
+    /**
+     * 单位成本
+     */
+    @TableField(value = "unit_cost")
+    private BigDecimal unitCost;
+    /**
+     * 成本(原币)
+     */
+    @TableField(value = "ori_currency_cost")
+    private BigDecimal oriCurrencyCost;
+    /**
+     * 成本(本币)
+     */
+    @TableField(value = "net_cost")
+    private BigDecimal netCost;
+    /**
+     * 成本占比
+     */
+    @TableField(value = "net_cost_ratio")
+    private BigDecimal netCostRatio;
+    /**
+     * 行情
+     */
+    @TableField(value = "market_price")
+    private BigDecimal marketPrice;
+    /**
+     * 市值(原币)
+     */
+    @TableField(value = "ori_currency_market_value")
+    private BigDecimal oriCurrencyMarketValue;
+    /**
+     * 市值(本币)
+     */
+    @TableField(value = "market_value")
+    private BigDecimal marketValue;
+    /**
+     * 市值占比
+     */
+    @TableField(value = "market_value_ratio")
+    private BigDecimal marketValueRatio;
+    /**
+     * 估值增值(原币)
+     */
+    @TableField(value = "ori_currency_increment")
+    private BigDecimal oriCurrencyIncrement;
+    /**
+     * 估值增值(本币)
+     */
+    @TableField(value = "increment")
+    private BigDecimal increment;
+    /**
+     * 停牌信息
+     */
+    @TableField(value = "halt_info")
+    private String haltInfo;
+    /**
+     * 权益信息
+     */
+    @TableField(value = "rights_interests_info")
+    private String rightsInterestsInfo;
+    /**
+     * 是否有效:0-无效,1-有效
+     */
+    @TableField(value = "isvalid")
+    private Integer isvalid;
+    /**
+     * 创建者Id
+     */
+    @TableField(value = "creatorid")
+    private Integer creatorId;
+    /**
+     * 创建时间
+     */
+    @TableField(value = "createtime")
+    private Date createTime;
+    /**
+     * 修改者Id
+     */
+    @TableField(value = "updaterid")
+    private Integer updaterId;
+    /**
+     * 更新时间
+     */
+    @TableField(value = "updatetime")
+    private Date updateTime;
+}

+ 2 - 2
service-base/src/main/java/com/simuwang/base/pojo/dos/ValuationTableDO.java

@@ -63,7 +63,7 @@ public class ValuationTableDO {
     @TableField(value = "file_url")
     private String fileUrl;
     /**
-     * 表格类型:1-赢时胜,2-恒生,3-金证
+     * 类型:1-赢时胜,2-恒生,3-金证,4-百航,5-通用
      */
     @TableField(value = "table_type")
     private Integer tableType;
@@ -86,7 +86,7 @@ public class ValuationTableDO {
      * 估值增值
      */
     @TableField(value = "increment")
-    private Integer increment;
+    private BigDecimal increment;
     /**
      * 估值表来源:1-邮件
      */

+ 173 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationDetails.java

@@ -0,0 +1,173 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+public class AssetsValuationDetails {
+
+	private int id;
+	private int userId;
+	private String title;
+	private String ValuationDate;
+	private Double nav;
+
+	//可能没有
+	private Double cumulativeNav;
+
+	private String headInfo;
+	private String tailInfo;
+	private String originalfile;
+	private String convertedFile;
+	private String fileUrl;
+	private int type;
+
+	private Double totalMarketValue;
+	private Double netAssetsValue;
+	private Double increment;
+
+
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public int getUserId() {
+		return userId;
+	}
+
+	public void setUserId(int userId) {
+		this.userId = userId;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getValuationDate() {
+		return ValuationDate;
+	}
+
+	public void setValuationDate(String valuationDate) {
+		ValuationDate = valuationDate;
+	}
+	public void setValuationDate(Date valuationDate) {
+	    ValuationDate = new SimpleDateFormat("yyyy-MM-dd").format(valuationDate);
+	}
+
+	public double getNav() {
+		return nav;
+	}
+
+	public double getIncrement() {
+		return increment;
+	}
+
+	public void setIncrement(double increment) {
+		this.increment = increment;
+	}
+
+	public void setNav(double nav) {
+		this.nav = nav;
+	}
+
+	public Double getCumulativeNav() {
+		return cumulativeNav;
+	}
+
+	public void setCumulativeNav(Double cumulativeNav) {
+		this.cumulativeNav = cumulativeNav;
+	}
+
+	public String getHeadInfo() {
+		return headInfo;
+	}
+
+	public void setHeadInfo(String headInfo) {
+		this.headInfo = headInfo;
+	}
+
+	public String getTailInfo() {
+		return tailInfo;
+	}
+
+	public void setTailInfo(String tailInfo) {
+		this.tailInfo = tailInfo;
+	}
+
+	public String getOriginalfile() {
+		return originalfile;
+	}
+
+	public void setOriginalfile(String originalfile) {
+		this.originalfile = originalfile;
+	}
+
+	public String getConvertedFile() {
+		return convertedFile;
+	}
+
+	public void setConvertedFile(String convertedFile) {
+		this.convertedFile = convertedFile;
+	}
+
+	public String getFileUrl() {
+		return fileUrl;
+	}
+
+	public void setFileUrl(String fileUrl) {
+		this.fileUrl = fileUrl;
+	}
+
+	public int getType() {
+		return type;
+	}
+
+	public void setType(int type) {
+		this.type = type;
+	}
+
+	public double getTotalMarketValue() {
+		return totalMarketValue;
+	}
+
+	public void setTotalMarketValue(double totalMarketValue) {
+		this.totalMarketValue = totalMarketValue;
+	}
+
+	public double getNetAssetsValue() {
+		return netAssetsValue;
+	}
+
+	public void setNetAssetsValue(double netAssetsValue) {
+		this.netAssetsValue = netAssetsValue;
+	}
+
+	@Override
+	public String toString() {
+		return "AssetsValuationDetails{" +
+				"id=" + id +
+				", userId=" + userId +
+				", title='" + title + '\'' +
+				", ValuationDate='" + ValuationDate + '\'' +
+				", nav=" + nav +
+				", cumulativeNav=" + cumulativeNav +
+				", headInfo='" + headInfo + '\'' +
+				", tailInfo='" + tailInfo + '\'' +
+				", originalfile='" + originalfile + '\'' +
+				", convertedFile='" + convertedFile + '\'' +
+				", fileUrl='" + fileUrl + '\'' +
+				", type=" + type +
+				", totalMarketValue=" + totalMarketValue +
+				", netAssetsValue=" + netAssetsValue +
+				'}';
+	}
+}

+ 25 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationExcelInfo.java

@@ -0,0 +1,25 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.util.List;
+
+
+public class AssetsValuationExcelInfo<T> {
+	private AssetsValuationDetails extInfo;
+	private List<T> data;
+
+	public AssetsValuationDetails getExtInfo() {
+		return extInfo;
+	}
+
+	public void setExtInfo(AssetsValuationDetails extInfo) {
+		this.extInfo = extInfo;
+	}
+
+	public List<T> getData() {
+		return data;
+	}
+
+	public void setData(List<T> data) {
+		this.data = data;
+	}
+}

+ 96 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationInfo.java

@@ -0,0 +1,96 @@
+package com.simuwang.base.pojo.valuation;
+
+import lombok.Data;
+
+@Data
+public class AssetsValuationInfo {
+	/**
+	 * 用户id
+	 */
+	private int userId;
+	/**
+	 * 估值表id(valuation_table.id)
+	 */
+	private int valuationId;
+	/**
+	 * 科目代码(去掉.)
+	 */
+	private String subjectCode;
+	private String securitiesCode;
+	private int secType = -1;
+
+	/**
+	 * 原始科目代码(表格字段)
+	 */
+	private String originalSubjectCode;
+	/**
+	 * 科目名称(表格字段)
+	 */
+	private String subjectName;
+	/**
+	 * 数量(表格字段)
+	 */
+	private Double securitiesAmount = null;
+	/**
+	 * 单位成本(表格字段)
+	 */
+	private Double unitCost;
+	/**
+	 * 成本(表格字段)
+	 */
+	private Double netCost;
+	/**
+	 * 成本占比(表格字段)
+	 */
+	private Double netCostRatio;
+	/**
+	 * 行情(表格字段)
+	 */
+	private Double marketPrice;
+	/**
+	 * 市值(表格字段)
+	 */
+	private Double marketValue;
+	/**
+	 * 市值占比(表格字段)
+	 */
+	private Double marketValueRatio;
+	/**
+	 * 估值增值(表格字段)
+	 */
+	private Double increment;
+	/**
+	 * 停牌信息(表格字段)
+	 */
+	private String haltInfo;
+	/**
+	 * 权益信息(表格字段)
+	 */
+	private String rightsInterestsInfo;
+	/**
+	 * 币种(表格字段)
+	 */
+	private String currency;
+	/**
+	 * 汇率(表格字段)
+	 */
+	private String exchangeRate;
+
+	private Double oriCurrencyCost;
+	private Double oriCurrencyMarketValue;
+	private Double oriCurrencyValueAdded;
+
+	private String subjectLevel;
+	private String issuer;
+	private String vProjectCode;
+	private String vProjectName;
+	private String costRatioAsset;
+	private String mvRatioAsset;
+	private String spotPrice;
+	private String remark;
+	private String assumingCost;
+	private String holdingState;
+
+	private String valuationDate;
+
+}

+ 207 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationResult.java

@@ -0,0 +1,207 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.util.List;
+
+public class AssetsValuationResult {
+	private int okNum;
+	private int errorNum;
+	private List<Record> okList;
+	private List<Record> errorList;
+
+	public int getOkNum() {
+		return okNum;
+	}
+
+	public AssetsValuationResult setOkNum(int okNum) {
+		this.okNum = okNum;
+		return this;
+	}
+
+	public int getErrorNum() {
+		return errorNum;
+	}
+
+	public AssetsValuationResult setErrorNum(int errorNum) {
+		this.errorNum = errorNum;
+		return this;
+	}
+
+	public List<Record> getOkList() {
+		return okList;
+	}
+
+	public AssetsValuationResult setOkList(List<Record> okList) {
+		this.okList = okList;
+		return this;
+	}
+
+	public List<Record> getErrorList() {
+		return errorList;
+	}
+
+	public AssetsValuationResult setErrorList(List<Record> errorList) {
+		this.errorList = errorList;
+		return this;
+	}
+
+	public static class Record {
+		public String excelName;
+		public String msg;
+		public int rowNum;
+		public int columnNum;
+		/**
+		 * 估值表id
+		 */
+		private Integer valuationId;
+		/**
+		 * 估值日期
+		 */
+		private String date;
+		/**
+		 * 单位净值
+		 */
+		private String nav;
+		/**
+		 * 累计净值
+		 */
+		private String cumulativeNavWithdrawal;
+		/**
+		 * 解析表格得到的基金名称和备案编码
+		 */
+		private ParseValuationInfo parseValuationInfo;
+		/**
+		 * 匹配上的基金id
+		 */
+		private String fundId;
+		/**
+		 * 解析状态:0-失败,1-成功
+		 */
+		private Integer success;
+
+		/**
+		 * 资产净值(资产净值112)
+		 */
+		private String assetNet;
+
+		/**
+		 * 资产份额(实收资本107)
+		 */
+		private String assetShare;
+
+		public Integer getValuationId() {
+			return valuationId;
+		}
+
+		public void setValuationId(Integer valuationId) {
+			this.valuationId = valuationId;
+		}
+
+		public String getAssetShare() {
+			return assetShare;
+		}
+
+		public void setAssetShare(String assetShare) {
+			this.assetShare = assetShare;
+		}
+
+		public String getAssetNet() {
+			return assetNet;
+		}
+
+		public void setAssetNet(String assetNet) {
+			this.assetNet = assetNet;
+		}
+
+		public ParseValuationInfo getParseValuationInfo() {
+			return parseValuationInfo;
+		}
+
+		public void setParseValuationInfo(ParseValuationInfo parseValuationInfo) {
+			this.parseValuationInfo = parseValuationInfo;
+		}
+
+		public String getFundId() {
+			return fundId;
+		}
+
+		public void setFundId(String fundId) {
+			this.fundId = fundId;
+		}
+
+		public Integer getSuccess() {
+			return success;
+		}
+
+		public Record setSuccess(Integer success) {
+			this.success = success;
+			return this;
+		}
+
+		public String getExcelName() {
+			return excelName;
+		}
+
+		public Record setExcelName(String excelName) {
+			this.excelName = excelName;
+			return this;
+		}
+
+		public String getMsg() {
+			return msg;
+		}
+
+		public Record setMsg(String msg) {
+			this.msg = msg;
+			return this;
+		}
+
+		public int getRowNum() {
+			return rowNum;
+		}
+
+		public Record setRowNum(int rowNum) {
+			this.rowNum = rowNum;
+			return this;
+		}
+
+		public int getColumnNum() {
+			return columnNum;
+		}
+
+		public Record setColumnNum(int columnNum) {
+			this.columnNum = columnNum;
+			return this;
+		}
+
+		public String getDate() {
+			return date;
+		}
+
+		public void setDate(String date) {
+			this.date = date;
+		}
+
+		public String getNav() {
+			return nav;
+		}
+
+		public void setNav(String nav) {
+			this.nav = nav;
+		}
+
+		public String getCumulativeNavWithdrawal() {
+			return cumulativeNavWithdrawal;
+		}
+
+		public void setCumulativeNavWithdrawal(String cumulativeNavWithdrawal) {
+			this.cumulativeNavWithdrawal = cumulativeNavWithdrawal;
+		}
+
+		@Override
+		public String toString() {
+			return "Record [excelName=" + excelName + ", msg=" + msg + ", rowNum=" + rowNum + ", columnNum=" + columnNum + ", date=" + date + ", nav=" + nav + ", cumulativeNav="
+					+ cumulativeNavWithdrawal + "]";
+		}
+
+	}
+}

+ 47 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/AssetsValuationType.java

@@ -0,0 +1,47 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.util.stream.Stream;
+
+/**
+ * 资产估值表类型
+ * 
+ * @author William
+ */
+public enum AssetsValuationType {
+	/** 赢时胜,id=1 */
+	YSSTech(1),
+
+	/** 恒生,id=2 */
+	HangSeng(2),
+
+	/** 金证,id=3 */
+	SZKingdom(3),
+
+	/** 百航,id=4 */
+	BaiHang(4),
+
+	/** GeneralTemplate,id=5 */
+	GeneralTemplate(5);
+
+	private int id;
+
+	private AssetsValuationType(int id) {
+		this.id = id;
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public static AssetsValuationType[] getAllType() {
+		return AssetsValuationType.values();
+	}
+	
+	public static boolean isValid(int id) {
+		return Stream.of(AssetsValuationType.values()).anyMatch(t -> t.getId() == id);
+	}
+
+	public static AssetsValuationType getType(int id) {
+		return Stream.of(AssetsValuationType.values()).filter(t -> t.getId() == id).findFirst().orElse(null);
+	}
+}

+ 159 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/CmValuationTableAttribute.java

@@ -0,0 +1,159 @@
+package com.simuwang.base.pojo.valuation;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class CmValuationTableAttribute {
+    /**
+    * 主键id,估值属性表id
+    */
+    private Integer id;
+
+    /**
+    * 用户id
+    */
+    private Integer userid;
+
+    /**
+    * 估值表id,和cm_user_valuation_table.id关联
+    */
+    private Integer valuationId;
+
+    /**
+    * 币别
+    */
+    private String currency;
+
+    /**
+    * 汇率
+    */
+    private BigDecimal exchangeRate;
+
+    /**
+    * 原币成本
+    */
+    private BigDecimal oCurrencyCost;
+
+    /**
+    * 原币市值
+    */
+    private BigDecimal oCurrencyMarketValue;
+
+    /**
+    * 原币增值
+    */
+    private BigDecimal oCurrencyValueAdded;
+
+    /**
+    * 科目代码
+    */
+    private String subjectCode;
+
+    /**
+     * 新增字段,保存原始科目代码
+     */
+    private String originalSubjectCode;
+
+    /**
+    * 证券代码
+    */
+    private String securitiesCode;
+
+    /**
+    * 对应市场:0-股票市场,1-债券市场,2-商品期货,3-大宗商品,4-外汇,5-贵金属,6-公募基金,7-私募基金,8-货币,9-金融期货
+    */
+    private Byte secType;
+
+    /**
+    * 科目名称
+    */
+    private String subjectName;
+
+    /**
+    * 证券数量
+    */
+    private BigDecimal securitiesAmount;
+
+    /**
+    * 单位成本
+    */
+    private BigDecimal unitCost;
+
+    /**
+    * 成本
+    */
+    private BigDecimal netCost;
+
+    /**
+    * 成本占净值
+    */
+    private BigDecimal netCostRatio;
+
+    /**
+    * 行情收市价
+    */
+    private BigDecimal marketPrice;
+
+    /**
+    * 市值
+    */
+    private BigDecimal marketValue;
+
+    /**
+    * 市值占净值
+    */
+    private BigDecimal marketValueRatio;
+
+    /**
+    * 估值增值
+    */
+    private BigDecimal increment;
+
+    /**
+    * 停牌信息
+    */
+    private String haltInfo;
+
+    /**
+    * 权益信息
+    */
+    private String rightsInterestsInfo;
+
+    /**
+    * 是否有效:1-有效 0-无效
+    */
+    private Integer isvalid;
+
+    /**
+    * 创建者Id,默认第一次创建者名称,创建后不变更
+    */
+    private Integer creatorid;
+
+    /**
+    * 创建时间,默认第一次创建的getdate()时间
+    */
+    private Date createtime;
+
+    /**
+     * 修改者Id;第一次创建时与Creator值相同,修改时与修改人值相同
+     */
+    private Integer updaterid;
+
+    /**
+     * 修改时间;第一次创建时与CreatTime值相同,修改时与修改时间相同
+     */
+    private Date updatetime;
+
+    /**
+     * 科目代码层级
+     */
+    private Integer level;
+
+    /**
+     * 上一层级科目代码
+     */
+    private String preOriginalSubjectCode;
+}

+ 13 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ExcelEntity.java

@@ -0,0 +1,13 @@
+package com.simuwang.base.pojo.valuation;
+
+import lombok.Data;
+
+
+@Data
+public class ExcelEntity {
+    private String[] value;
+    private boolean numerical;
+    private boolean nullable;
+    private boolean blankAllowable;
+    private boolean percent;
+}

+ 84 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ExcelInfo.java

@@ -0,0 +1,84 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.util.List;
+import java.util.Map;
+
+public  class ExcelInfo {
+		/** 列标题所占行数 */
+		private int titleRows = 1;
+		private int headerRows = 0;
+		private int footerRows = 0;
+		private List<String> headerInfo = null;
+		private List<String> footerInfo = null;
+
+		/** 修正后的*/
+		private Map<String, ExcelEntity> fieldMap = null;
+
+		private Map<String, Integer> cellIndexMap = null;
+
+		private AssetsValuationType type = null;
+
+		public int getTitleRows() {
+			return titleRows;
+		}
+
+		public void setTitleRows(int titleRows) {
+			this.titleRows = titleRows;
+		}
+
+		public int getHeaderRows() {
+			return headerRows;
+		}
+
+		public void setHeaderRows(int headerRows) {
+			this.headerRows = headerRows;
+		}
+
+		public int getFooterRows() {
+			return footerRows;
+		}
+
+		public void setFooterRows(int footerRows) {
+			this.footerRows = footerRows;
+		}
+
+		public List<String> getHeaderInfo() {
+			return headerInfo;
+		}
+
+		public void setHeaderInfo(List<String> headerInfo) {
+			this.headerInfo = headerInfo;
+		}
+
+		public List<String> getFooterInfo() {
+			return footerInfo;
+		}
+
+		public void setFooterInfo(List<String> footerInfo) {
+			this.footerInfo = footerInfo;
+		}
+
+		public AssetsValuationType getType() {
+			return type;
+		}
+
+		public void setType(AssetsValuationType type) {
+			this.type = type;
+		}
+
+		public Map<String, Integer> getCellIndexMap() {
+			return cellIndexMap;
+		}
+
+		public void setCellIndexMap(Map<String, Integer> cellIndexMap) {
+			this.cellIndexMap = cellIndexMap;
+		}
+
+		public Map<String, ExcelEntity> getFieldMap() {
+			return fieldMap;
+		}
+
+		public void setFieldMap(Map<String, ExcelEntity> fieldMap) {
+			this.fieldMap = fieldMap;
+		}
+	}

+ 153 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ImportParams.java

@@ -0,0 +1,153 @@
+package com.simuwang.base.pojo.valuation;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ImportParams {
+	private static final String HEADER_IDENTIFY_V1 = "科目代码";
+	private static final String[] HEADER_IDENTIFY_V2 = ValuationTableConfig.SUBJECT_CODE.split(",");
+
+	private static final String Footer_IDENTIFY_V1 = "制表";
+
+	/** header所占行数 */
+	private int headerRows = 0;
+
+	/** 列标题所占行数 */
+	private int titleRows = 1;
+
+	/** footer所占行数 */
+	private int footerRows = 0;
+
+	/** 优先级高于行数 */
+	private String headerIdentify = null;
+	/** 优先级高于行数 */
+	private String footerIdentify = null;
+
+	private static final Map<AssetsValuationType, Class<? extends PreAssetsValuationBase>> typeClassMap = new HashMap<>();
+	static {
+		typeClassMap.put(AssetsValuationType.GeneralTemplate, PreAssetsValuationGeneralTemplate.class);
+
+	}
+
+	public static boolean isEqualHeaderIdentify(String str) {
+		if (HEADER_IDENTIFY_V1.equals(str)) {
+			return true;
+		}
+
+		for (String identify : HEADER_IDENTIFY_V2) {
+			if (identify.equals(str)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public static boolean isContainFooterIdentify(String str) {
+		return (null != str && str.contains(Footer_IDENTIFY_V1));
+	}
+
+	public static Class<? extends PreAssetsValuationBase> getPreprocessEntity(AssetsValuationType type) {
+		if (null == type) {
+			return null;
+		}
+
+		return typeClassMap.entrySet().stream().filter(e -> e.getKey().getId() == type.getId()).findFirst().get()
+				.getValue();
+	}
+
+	/** 提供默认模板配置 */
+	public static ImportParams getDefaultImportParams(AssetsValuationType type) {
+		switch (type) {
+			case YSSTech :
+				return getYYSTech();
+			case HangSeng :
+				return getHangSeng();
+			case SZKingdom :
+				return getSZKingdom();
+			case BaiHang :
+				return getBaiHang();
+			default :
+				return getGeneralTemplate();
+		}
+	}
+
+	private static ImportParams getGeneralTemplate() {
+		ImportParams params = new ImportParams();
+		params.setHeaderIdentify(HEADER_IDENTIFY_V1);
+		params.setTitleRows(1);
+		return params;
+	}
+
+	/** 百航BaiHang-默认模板配置 */
+	private static ImportParams getBaiHang() {
+		ImportParams params = new ImportParams();
+		params.setHeaderIdentify(HEADER_IDENTIFY_V1);
+		params.setTitleRows(1);
+		return params;
+	}
+
+	/** 赢时胜YYSTech-默认模板配置 */
+	public static ImportParams getYYSTech() {
+		ImportParams params = new ImportParams();
+		params.setHeaderIdentify(HEADER_IDENTIFY_V1);
+		params.setTitleRows(2);
+		return params;
+	}
+
+	/** 恒生HangSeng-默认模板配置 */
+	public static ImportParams getHangSeng() {
+		ImportParams params = new ImportParams();
+		params.setHeaderIdentify(HEADER_IDENTIFY_V1);
+		params.setFooterIdentify(Footer_IDENTIFY_V1);
+		params.setTitleRows(1);
+		return params;
+	}
+
+	/** 金证SZKingdom-默认模板配置 */
+	public static ImportParams getSZKingdom() {
+		ImportParams params = new ImportParams();
+		params.setHeaderIdentify(HEADER_IDENTIFY_V1);
+		params.setTitleRows(2);
+		return params;
+	}
+
+	public String getHeaderIdentify() {
+		return headerIdentify;
+	}
+
+	public void setHeaderIdentify(String headerIdentify) {
+		this.headerIdentify = headerIdentify;
+	}
+
+	public String getFooterIdentify() {
+		return footerIdentify;
+	}
+
+	public void setFooterIdentify(String footerIdentify) {
+		this.footerIdentify = footerIdentify;
+	}
+
+	public int getTitleRows() {
+		return titleRows;
+	}
+
+	public void setTitleRows(int titleRows) {
+		this.titleRows = titleRows;
+	}
+
+	public int getHeaderRows() {
+		return headerRows;
+	}
+
+	public void setHeaderRows(int headerRows) {
+		this.headerRows = headerRows;
+	}
+
+	public int getFooterRows() {
+		return footerRows;
+	}
+
+	public void setFooterRows(int footerRows) {
+		this.footerRows = footerRows;
+	}
+}

+ 12 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ParseValuationInfo.java

@@ -0,0 +1,12 @@
+package com.simuwang.base.pojo.valuation;
+
+import lombok.Data;
+
+@Data
+public class ParseValuationInfo {
+
+    private String fundName;
+
+    private String registerNumber;
+
+}

+ 138 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationBase.java

@@ -0,0 +1,138 @@
+package com.simuwang.base.pojo.valuation;
+
+
+import com.simuwang.base.ano.Excel;
+
+public abstract class PreAssetsValuationBase {
+	@Excel(value = "科目代码")
+	private String subjectCode;
+
+	@Excel("科目名称")
+	private String subjectName;
+
+	private String securtiesCode;
+
+	@Excel(value = "数量", numerical = true)
+	private String amount;
+
+	@Excel(value = "单位成本", numerical = true)
+	private String unitCost;
+
+	@Excel(value = "成本", numerical = true)
+	private String cost;
+
+	@Excel(value = "成本占净值%", numerical = true)
+	private String ratioOfCostToNetWorth;
+
+	@Excel(value = "市值", numerical = true)
+	private String marketValue;
+
+	@Excel(value = "市值占净值%", numerical = true)
+	private String ratioOfMarketValueToNetWorth;
+
+	@Excel(value = "估值增值", numerical = true)
+	private String valueAdded;
+
+	@Excel("停牌信息")
+	private String suspensionInfo;
+
+	private String originalSubjectCode;
+
+	public abstract AssetsValuationType getType();
+
+	public String getSubjectCode() {
+		return subjectCode;
+	}
+
+	public void setSubjectCode(String subjectCode) {
+		this.subjectCode = subjectCode;
+	}
+
+	public String getSecurtiesCode() {
+		return securtiesCode;
+	}
+
+	public void setSecurtiesCode(String securtiesCode) {
+		this.securtiesCode = securtiesCode;
+	}
+
+	public String getSubjectName() {
+		return subjectName;
+	}
+
+	public void setSubjectName(String subjectName) {
+		this.subjectName = subjectName;
+	}
+
+	public String getAmount() {
+		return amount;
+	}
+
+	public void setAmount(String amount) {
+		this.amount = amount;
+	}
+
+	public String getUnitCost() {
+		return unitCost;
+	}
+
+	public void setUnitCost(String unitCost) {
+		this.unitCost = unitCost;
+	}
+
+	public String getCost() {
+		return cost;
+	}
+
+	public void setCost(String cost) {
+		this.cost = cost;
+	}
+
+	public String getRatioOfCostToNetWorth() {
+		return ratioOfCostToNetWorth;
+	}
+
+	public void setRatioOfCostToNetWorth(String ratioOfCostToNetWorth) {
+		this.ratioOfCostToNetWorth = ratioOfCostToNetWorth;
+	}
+
+	public String getMarketValue() {
+		return marketValue;
+	}
+
+	public void setMarketValue(String marketValue) {
+		this.marketValue = marketValue;
+	}
+
+	public String getRatioOfMarketValueToNetWorth() {
+		return ratioOfMarketValueToNetWorth;
+	}
+
+	public void setRatioOfMarketValueToNetWorth(String ratioOfMarketValueToNetWorth) {
+		this.ratioOfMarketValueToNetWorth = ratioOfMarketValueToNetWorth;
+	}
+
+	public String getValueAdded() {
+		return valueAdded;
+	}
+
+	public void setValueAdded(String valueAdded) {
+		this.valueAdded = valueAdded;
+	}
+
+	public String getSuspensionInfo() {
+		return suspensionInfo;
+	}
+
+	public void setSuspensionInfo(String suspensionInfo) {
+		this.suspensionInfo = suspensionInfo;
+	}
+
+	public String getOriginalSubjectCode() {
+		return originalSubjectCode;
+	}
+
+	public void setOriginalSubjectCode(String originalSubjectCode) {
+		this.originalSubjectCode = originalSubjectCode;
+	}
+}

+ 309 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationGeneralTemplate.java

@@ -0,0 +1,309 @@
+package com.simuwang.base.pojo.valuation;
+
+import com.simuwang.base.ano.Excel;
+
+public class PreAssetsValuationGeneralTemplate extends PreAssetsValuationBase {
+
+    @Excel(values = ValuationTableConfig.SUBJECT_CODE)
+    private String subjectCode;
+
+    @Excel({ValuationTableConfig.SUBJECT_NAME,ValuationTableConfig.SUBJECT_FORMAT_NAME})
+    private String subjectName;
+
+    @Excel(values = ValuationTableConfig.AMOUNT, numerical = true)
+    private String amount;
+
+    @Excel(values = ValuationTableConfig.UNIT_COST, numerical = true)
+    private String unitCost;
+
+    @Excel(values = ValuationTableConfig.COST, numerical = true)
+    private String cost;
+
+    @Excel(values = ValuationTableConfig.RATIO_OF_COST_TO_NET_WORTH, numerical = true)
+    private String ratioOfCostToNetWorth;
+
+    @Excel(values = ValuationTableConfig.MARKET_PRICE, numerical = true)
+    private String marketPrice;
+
+    @Excel(values = ValuationTableConfig.MARKET_VALUE, numerical = true)
+    private String marketValue;
+
+    @Excel(values = ValuationTableConfig.RATIO_OF_MARKET_VALUE_TO_NET_WORTH, numerical = true)
+    private String ratioOfMarketValueToNetWorth;
+
+    @Excel(values = ValuationTableConfig.VALUE_ADDED, numerical = true)
+    private String valueAdded;
+
+    @Excel(values = ValuationTableConfig.SUSPENSION_INFO)
+    private String suspensionInfo;
+
+    @Excel(values = ValuationTableConfig.RIGHT_AND_INTEREST_INFO)
+    private String rightAndInterestInfo;
+
+    @Excel(values = ValuationTableConfig.CURRENCY)
+    private String currency;
+
+    @Excel(values = ValuationTableConfig.EXCHANGE_RATE)
+    private String exchangeRate;
+
+    @Excel(values = ValuationTableConfig.ORI_CURRENCY_COST)
+    private String oriCurrencyCost;
+
+    @Excel(values = ValuationTableConfig.ORI_CURRENCY_MARKET_VALUE)
+    private String oriCurrencyMarketValue;
+
+    @Excel(values = ValuationTableConfig.ORI_CURRENCY_VALUE_ADDED)
+    private String oriCurrencyValueAdded;
+
+    @Excel(values = ValuationTableConfig.SUBJECT_LEVEL)
+    private String subjectLevel;
+
+    @Excel(values = ValuationTableConfig.ISSUER)
+    private String issuer;
+
+    @Excel(values = ValuationTableConfig.V_PROJECT_CODE)
+    private String vProjectCode;
+
+    @Excel(values = ValuationTableConfig.V_PROJECT_NAME)
+    private String vProjectName;
+
+    @Excel(values = ValuationTableConfig.COST_RATIO_ASSET)
+    private String costRatioAsset;
+
+    @Excel(values = ValuationTableConfig.MV_RATIO_ASSET)
+    private String mvRatioAsset;
+
+    @Excel(values = ValuationTableConfig.SPOT_PRICE)
+    private String spotPrice;
+
+    @Excel(values = ValuationTableConfig.REMARK)
+    private String remark;
+
+    @Excel(values = ValuationTableConfig.ASSUMING_COST)
+    private String assumingCost;
+
+    @Excel(values = ValuationTableConfig.HOLDING_STATE)
+    private String holdingState;
+
+    @Override
+    public AssetsValuationType getType() {
+        return AssetsValuationType.GeneralTemplate;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getSubjectName() {
+        return subjectName;
+    }
+
+    public void setSubjectName(String subjectName) {
+        this.subjectName = subjectName;
+    }
+
+    public String getAmount() {
+        return amount;
+    }
+
+    public void setAmount(String amount) {
+        this.amount = amount;
+    }
+
+    public String getUnitCost() {
+        return unitCost;
+    }
+
+    public void setUnitCost(String unitCost) {
+        this.unitCost = unitCost;
+    }
+
+    public String getCost() {
+        return cost;
+    }
+
+    public void setCost(String cost) {
+        this.cost = cost;
+    }
+
+    public String getRatioOfCostToNetWorth() {
+        return ratioOfCostToNetWorth;
+    }
+
+    public void setRatioOfCostToNetWorth(String ratioOfCostToNetWorth) {
+        this.ratioOfCostToNetWorth = ratioOfCostToNetWorth;
+    }
+
+    public String getMarketPrice() {
+        return marketPrice;
+    }
+
+    public void setMarketPrice(String marketPrice) {
+        this.marketPrice = marketPrice;
+    }
+
+    public String getMarketValue() {
+        return marketValue;
+    }
+
+    public void setMarketValue(String marketValue) {
+        this.marketValue = marketValue;
+    }
+
+    public String getRatioOfMarketValueToNetWorth() {
+        return ratioOfMarketValueToNetWorth;
+    }
+
+    public void setRatioOfMarketValueToNetWorth(String ratioOfMarketValueToNetWorth) {
+        this.ratioOfMarketValueToNetWorth = ratioOfMarketValueToNetWorth;
+    }
+
+    public String getValueAdded() {
+        return valueAdded;
+    }
+
+    public void setValueAdded(String valueAdded) {
+        this.valueAdded = valueAdded;
+    }
+
+    public String getSuspensionInfo() {
+        return suspensionInfo;
+    }
+
+    public void setSuspensionInfo(String suspensionInfo) {
+        this.suspensionInfo = suspensionInfo;
+    }
+
+    public String getRightAndInterestInfo() {
+        return rightAndInterestInfo;
+    }
+
+    public void setRightAndInterestInfo(String rightAndInterestInfo) {
+        this.rightAndInterestInfo = rightAndInterestInfo;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public void setCurrency(String currency) {
+        this.currency = currency;
+    }
+
+    public String getExchangeRate() {
+        return exchangeRate;
+    }
+
+    public void setExchangeRate(String exchangeRate) {
+        this.exchangeRate = exchangeRate;
+    }
+
+    public String getOriCurrencyCost() {
+        return oriCurrencyCost;
+    }
+
+    public void setOriCurrencyCost(String oriCurrencyCost) {
+        this.oriCurrencyCost = oriCurrencyCost;
+    }
+
+    public String getOriCurrencyMarketValue() {
+        return oriCurrencyMarketValue;
+    }
+
+    public void setOriCurrencyMarketValue(String oriCurrencyMarketValue) {
+        this.oriCurrencyMarketValue = oriCurrencyMarketValue;
+    }
+
+    public String getOriCurrencyValueAdded() {
+        return oriCurrencyValueAdded;
+    }
+
+    public void setOriCurrencyValueAdded(String oriCurrencyValueAdded) {
+        this.oriCurrencyValueAdded = oriCurrencyValueAdded;
+    }
+
+    public String getSubjectLevel() {
+        return subjectLevel;
+    }
+
+    public void setSubjectLevel(String subjectLevel) {
+        this.subjectLevel = subjectLevel;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    public void setIssuer(String issuer) {
+        this.issuer = issuer;
+    }
+
+    public String getvProjectCode() {
+        return vProjectCode;
+    }
+
+    public void setVProjectCode(String vProjectCode) {
+        this.vProjectCode = vProjectCode;
+    }
+
+    public String getvProjectName() {
+        return vProjectName;
+    }
+
+    public void setVProjectName(String vProjectName) {
+        this.vProjectName = vProjectName;
+    }
+
+    public String getCostRatioAsset() {
+        return costRatioAsset;
+    }
+
+    public void setCostRatioAsset(String costRatioAsset) {
+        this.costRatioAsset = costRatioAsset;
+    }
+
+    public String getMvRatioAsset() {
+        return mvRatioAsset;
+    }
+
+    public void setMvRatioAsset(String mvRatioAsset) {
+        this.mvRatioAsset = mvRatioAsset;
+    }
+
+    public String getSpotPrice() {
+        return spotPrice;
+    }
+
+    public void setSpotPrice(String spotPrice) {
+        this.spotPrice = spotPrice;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    public String getAssumingCost() {
+        return assumingCost;
+    }
+
+    public void setAssumingCost(String assumingCost) {
+        this.assumingCost = assumingCost;
+    }
+
+    public String getHoldingState() {
+        return holdingState;
+    }
+
+    public void setHoldingState(String holdingState) {
+        this.holdingState = holdingState;
+    }
+
+}

+ 202 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/PreAssetsValuationInfo.java

@@ -0,0 +1,202 @@
+package com.simuwang.base.pojo.valuation;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @author Rain
+ */
+public class PreAssetsValuationInfo<T extends PreAssetsValuationBase> {
+	private String userId;
+	private String excelOriginName;
+	private String excelNewName;
+	private String savePath;
+	private AssetsValuationType type;
+
+	private Header header;
+	private List<String> footer;
+	private List<T> data;
+	private Error error;
+
+	public Error getError() {
+		return error;
+	}
+
+	public void setError(Error error) {
+		this.error = error;
+	}
+
+	public class Header {
+		// 主标题
+		private String title;
+		// 子标题
+		private String subTitle;
+		private Date valuationDate;
+		private double unitNetValue;
+		private List<String> headerList;
+
+		public String getTitle() {
+			return title;
+		}
+
+		public void setTitle(String title) {
+			this.title = title;
+		}
+
+		public String getSubTitle() {
+			return subTitle;
+		}
+
+		public void setSubTitle(String subTitle) {
+			this.subTitle = subTitle;
+		}
+
+		public Date getValuationDate() {
+			return valuationDate;
+		}
+
+		public void setValuationDate(Date valuationDate) {
+			this.valuationDate = valuationDate;
+		}
+
+		public double getUnitNetValue() {
+			return unitNetValue;
+		}
+
+		public void setUnitNetValue(double unitNetValue) {
+			this.unitNetValue = unitNetValue;
+		}
+
+		public List<String> getHeaderList() {
+			return headerList;
+		}
+
+		public void setHeaderList(List<String> headerList) {
+			this.headerList = headerList;
+		}
+
+		@Override
+		public String toString() {
+			return "Header{" +
+					"title='" + title + '\'' +
+					", subTitle='" + subTitle + '\'' +
+					", valuationDate=" + valuationDate +
+					", unitNetValue=" + unitNetValue +
+					", headerList=" + headerList +
+					'}';
+		}
+	}
+
+	public class Error {
+		private int rowNum = -1;
+		private int cellNum = -1;
+		private String excelName;
+		private String msg;
+
+		public String getExcelName() {
+			return excelName;
+		}
+
+		public void setExcelName(String excelName) {
+			this.excelName = excelName;
+		}
+
+		public int getRowNum() {
+			return rowNum;
+		}
+
+		public Error setRowNum(int rowNum) {
+			this.rowNum = rowNum;
+			return this;
+		}
+
+		public int getCellNum() {
+			return cellNum;
+		}
+
+		public Error setCellNum(int cellNum) {
+			this.cellNum = cellNum;
+			return this;
+		}
+
+		public String getMsg() {
+			return msg;
+		}
+
+		public Error setMsg(String msg) {
+			this.msg = msg;
+			return this;
+		}
+
+		@Override
+		public String toString() {
+			return "Error [rowNum=" + rowNum + ", cellNum=" + cellNum + ", msg=" + msg + "]";
+		}
+	}
+
+	public String getUserId() {
+		return userId;
+	}
+
+	public void setUserId(String userId) {
+		this.userId = userId;
+	}
+
+	public String getExcelOriginName() {
+		return excelOriginName;
+	}
+
+	public void setExcelOriginName(String excelOriginName) {
+		this.excelOriginName = excelOriginName;
+	}
+
+	public String getExcelNewName() {
+		return excelNewName;
+	}
+
+	public void setExcelNewName(String excelNewName) {
+		this.excelNewName = excelNewName;
+	}
+
+	public String getSavePath() {
+		return savePath;
+	}
+
+	public void setSavePath(String savePath) {
+		this.savePath = savePath;
+	}
+
+	public AssetsValuationType getType() {
+		return type;
+	}
+
+	public void setType(AssetsValuationType type) {
+		this.type = type;
+	}
+
+	public Header getHeader() {
+		return header;
+	}
+
+	public void setHeader(Header header) {
+		this.header = header;
+	}
+
+	public List<T> getData() {
+		return data;
+	}
+
+	public void setData(List<T> data) {
+		this.data = data;
+	}
+
+	public List<String> getFooter() {
+		return footer;
+	}
+
+	public void setFooter(List<String> footer) {
+		this.footer = footer;
+	}
+
+}

+ 29 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationNeedParseParam.java

@@ -0,0 +1,29 @@
+package com.simuwang.base.pojo.valuation;
+
+import lombok.Data;
+
+import java.io.File;
+
+@Data
+public class ValuationNeedParseParam {
+    /**
+     * 文件
+     */
+    private File file;
+    /**
+     * 基金id
+     */
+    private String fundId;
+    /**
+     * 文件名称
+     */
+    private String originFileName;
+    /**
+     * 文件url
+     */
+    private String fileUrl;
+    /**
+     * 估值表来源: 1-邮件
+     */
+    private Integer fromEmail;
+}

+ 17 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationResult.java

@@ -0,0 +1,17 @@
+package com.simuwang.base.pojo.valuation;
+
+import com.simuwang.base.pojo.dos.FundPositionDetailDO;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ValuationResult {
+    private List<FundPositionDetailDO> list;
+    private Integer userId;
+    private String fundId;
+    private String valuationDate;
+    private String cumulativeNavWithdrawal;
+    private AssetsValuationResult.Record record;
+    private List<CmValuationTableAttribute> valuationTableAttributes;
+}

+ 88 - 0
service-base/src/main/java/com/simuwang/base/pojo/valuation/ValuationTableConfig.java

@@ -0,0 +1,88 @@
+package com.simuwang.base.pojo.valuation;
+
+/**
+ * @author Rain
+ * @date 2023/6/5 9:48
+ * @description Valuation table general template configuration
+ */
+public class ValuationTableConfig {
+	/*  含有冒号,一般情况为跨上下两行,冒号前为前一行,冒号后表示后一行 */
+	/*一律使用英文标点,估值表中的中文标点在解析的时候会转换成英文*/
+
+    public static final String SUBJECT_CODE = "科目代码,科目编码,科目格式代码";
+
+    public static final String SUBJECT_NAME = "科目名称";
+
+    public static final String SUBJECT_FORMAT_NAME = "科目格式名称";
+
+    public static final String CURRENCY = "币种";
+
+    public static final String EXCHANGE_RATE = "汇率";
+
+    public static final String AMOUNT = "数量,证券数量";
+
+    public static final String UNIT_COST = "单位成本,成本价格";
+
+    public static final String COST = "成本,证券成本,成本本币,成本-本币,成本(本位币),成本:本币,成本本位币,本位币成本";
+
+    public static final String RATIO_OF_COST_TO_NET_WORTH = "成本占净值%,成本占净值(%),成本占净值比(%),成本占净值比,成本占比,成本净值比例";
+
+    public static final String MARKET_PRICE = "市价,行情收市价,行情价格,行情收市价(本位币),行情";
+
+    public static final String MARKET_VALUE = "市值,证券市值,账面价值(本位币),市值本币,市值-本币,市值:本币,市值本位币,本位币市值";
+
+    public static final String RATIO_OF_MARKET_VALUE_TO_NET_WORTH = "市值占净值%,市值占净值(%),市值占净值比(%),市值占净值比,市值占比,市值净值比例";
+
+    public static final String VALUE_ADDED = "估值增值,估值增值本币,估值增值(本位币),估值增值-本币,估值增值:本币,估值增值本位币 ,本位币估值增值";
+
+    public static final String SUSPENSION_INFO = "停牌信息";
+
+    public static final String RIGHT_AND_INTEREST_INFO = "权益信息";
+
+    public static final String ORI_CURRENCY_COST = "成本原币,成本:原币,原币成本";
+
+    public static final String ORI_CURRENCY_MARKET_VALUE = "市值原币,市值:原币,原币市值";
+
+    public static final String ORI_CURRENCY_VALUE_ADDED = "估值增值原币,估值增值:原币,原币估值增值";
+
+	/*
+	科目级次
+	科目项目:
+		发行方
+		项目代码
+		项目名称
+	
+	成本占总资产%
+	市值占总资产%
+	即期价格
+	备注
+	假设费用
+	持仓状态*/
+
+	public static final String SUBJECT_LEVEL = "科目级次";
+
+	public static final String ISSUER = "科目项目:发行方";
+
+	public static final String V_PROJECT_CODE = "科目项目:项目代码";
+
+	public static final String V_PROJECT_NAME = "科目项目:项目名称";
+
+	public static final String COST_RATIO_ASSET = "成本占总资产%,成本占总资产(%),成本占总资产";
+
+	public static final String MV_RATIO_ASSET = "市值占总资产%,市值占总资产(%),市值占总资产";
+
+	public static final String SPOT_PRICE = "即期价格";
+
+	public static final String REMARK = "备注";
+
+	public static final String ASSUMING_COST = "假设费用";
+
+	public static final String HOLDING_STATE = "持仓状态";
+
+	// 跨行
+	private static final String[] specialItems = {"成本:本币", "市值:本币", "估值增值:本币", "成本:原币", "市值:原币", "估值增值:原币", "科目项目:发行方", "科目项目:项目代码", "科目项目:项目名称"};
+
+	public static String[] getSpecialCell() {
+		return specialItems;
+	}
+}

+ 4 - 2
service-base/src/main/resources/mapper/EmailFundAssetMapper.xml

@@ -10,6 +10,8 @@
         <result column="price_date" property="priceDate"/>
         <result column="asset_net" property="assetNet"/>
         <result column="asset_share" property="assetShare"/>
+        <result column="is_stored" property="isStored"/>
+        <result column="exception_status" property="exceptionStatus"/>
         <result column="isvalid" property="isvalid"/>
         <result column="creatorid" property="creatorId"/>
         <result column="createtime" property="createTime"/>
@@ -18,11 +20,11 @@
     </resultMap>
 
     <insert id="batchInsert" parameterType="com.simuwang.base.pojo.dos.EmailFundAssetDO">
-        insert into PPW_EMAIL.email_fund_asset(file_id, fund_id, fund_name,register_number,price_date,asset_net,asset_share,
+        insert into PPW_EMAIL.email_fund_asset(file_id, fund_id, fund_name,register_number,price_date,asset_net,asset_share,is_stored,exception_status,
                                               isvalid, creatorid, createtime, updaterid, updatetime)
         values
         <foreach collection="itemDoList" item="itemDo" index="index" separator=",">
-            (#{itemDo.fileId},#{itemDo.fundId},#{itemDo.fundName},#{itemDo.registerNumber},#{itemDo.priceDate},#{itemDo.assetNet},#{itemDo.assetShare},
+            (#{itemDo.fileId},#{itemDo.fundId},#{itemDo.fundName},#{itemDo.registerNumber},#{itemDo.priceDate},#{itemDo.assetNet},#{itemDo.assetShare},#{itemDo.isStored},#{itemDo.exceptionStatus},
             #{itemDo.isvalid}, #{itemDo.creatorId}, #{itemDo.createTime}, #{itemDo.updaterId}, #{itemDo.updateTime})
         </foreach>
 

+ 6 - 6
service-base/src/main/resources/mapper/FundAliasMapper.xml

@@ -16,23 +16,23 @@
     </resultMap>
 
 
-    <select id="queryFundByNameAndRegisterNumber" resultType="java.lang.String">
-        select target_fund_id
+    <select id="queryFundByNameAndRegisterNumber" resultMap="BaseResultMap">
+        select target_fund_id, target_fund_name, target_register_number
         from PPW_EMAIL.fund_alias
         where isvalid = 1
           and source_fund_name = #{fundName}
           and source_register_number = #{registerNumber}
     </select>
 
-    <select id="queryFundByName" resultType="java.lang.String">
-        select target_fund_id
+    <select id="queryFundByName" resultMap="BaseResultMap">
+        select target_fund_id, target_fund_name, target_register_number
         from PPW_EMAIL.fund_alias
         where isvalid = 1
           and source_fund_name = #{fundName}
     </select>
 
-    <select id="queryFundByRegisterNumber" resultType="java.lang.String">
-        select target_fund_id
+    <select id="queryFundByRegisterNumber" resultMap="BaseResultMap">
+        select target_fund_id, target_fund_name, target_register_number
         from PPW_EMAIL.fund_alias
         where isvalid = 1
           and source_register_number = #{registerNumber}

+ 12 - 6
service-base/src/main/resources/mapper/FundInfoMapper.xml

@@ -74,23 +74,29 @@
         </if>
     </select>
 
-    <select id="queryFundByNameAndRegisterNumber" resultType="java.lang.String">
-        select fund_id
+    <select id="queryFundByNameAndRegisterNumber" resultType="com.simuwang.base.pojo.dos.FundInfoDO">
+        select fund_id         as fundId,
+               fund_name       as fundName,
+               register_number as registerNumber
         from PPW_EMAIL.pvn_fund_info
         where isvalid = 1
           and fund_name = #{fundName}
           and register_number = #{registerNumber}
     </select>
 
-    <select id="queryFundByName" resultType="java.lang.String">
-        select fund_id
+    <select id="queryFundByName" resultType="com.simuwang.base.pojo.dos.FundInfoDO">
+        select fund_id         as fundId,
+               fund_name       as fundName,
+               register_number as registerNumber
         from PPW_EMAIL.pvn_fund_info
         where isvalid = 1
           and fund_name = #{fundName}
     </select>
 
-    <select id="queryFundByRegisterNumber" resultType="java.lang.String">
-        select fund_id
+    <select id="queryFundByRegisterNumber" resultType="com.simuwang.base.pojo.dos.FundInfoDO">
+        select fund_id         as fundId,
+               fund_name       as fundName,
+               register_number as registerNumber
         from PPW_EMAIL.pvn_fund_info
         where isvalid = 1
           and register_number = #{registerNumber}

+ 22 - 0
service-base/src/main/resources/mapper/FundPositionDetailMapper.xml

@@ -28,5 +28,27 @@
         <result column="createtime" property="createTime"/>
     </resultMap>
 
+    <delete id="deleteUnUsed">
+        delete
+        from PPW_EMAIL.fund_position_detail
+        where fund_id = #{fundId}
+          and valuation_date = #{valuationDate}
+    </delete>
+
+    <insert id="insertMulti" parameterType="com.simuwang.base.pojo.dos.FundPositionDetailDO">
+        INSERT INTO PPW_EMAIL.fund_position_detail (
+        fund_id,valuation_date,LEVEL,currency,exchange_rate,subject_code,
+        securities_amount, securities_code,securities_name, sec_type,market_value,
+        market_value_ratio,nature,subject_type,increment,halt_info,net_cost,net_cost_ratio,market_price,
+        unit_cost,isvalid,creatorid,createtime
+        ) VALUES
+        <foreach collection="details" index="index" item="detail" separator=",">
+            (#{detail.fundId},#{detail.valuationDate},#{detail.level}, #{detail.currency}, #{detail.exchangeRate}, #{detail.subjectCode},
+             #{detail.securitiesAmount},#{detail.securitiesCode}, #{detail.securitiesName}, #{detail.secType},#{detail.marketValue},
+             #{detail.marketValueRatio}, #{detail.nature},
+             #{detail.subjectType},#{detail.increment},#{detail.haltInfo},#{detail.netCost},#{detail.netCostRatio},#{detail.marketPrice},
+             #{detail.unitCost},#{detail.isvalid},0,#{detail.createTime})
+        </foreach>
+    </insert>
 
 </mapper>

+ 12 - 0
service-base/src/main/resources/mapper/PdfValuationRecordMapper.xml

@@ -18,5 +18,17 @@
         <result column="updatetime" property="updateTime"/>
     </resultMap>
 
+    <insert id="batchInsert" parameterType="com.simuwang.base.pojo.dos.PdfValuationRecordDO"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into PPW_EMAIL.pdf_valuation_record ( fund_id, upload_date, file_name, pdf_url,
+        excel_url,from_email,is_success,reason,
+        creatorid, createtime, updaterid, updatetime, isvalid)
+        values
+        <foreach collection="itemDoList" index="index" item="itemDo" separator=",">
+            (#{itemDo.fundId},#{itemDo.uploadDate},#{itemDo.fileName}, #{itemDo.pdfUrl},
+            #{itemDo.excelUrl},#{itemDo.fromEmail},#{itemDo.isSuccess},#{itemDo.reason},
+            #{itemDo.creatorId}, #{itemDo.createTime}, #{itemDo.updaterId}, #{itemDo.updateTime}, #{itemDo.isvalid})
+        </foreach>
+    </insert>
 
 </mapper>

+ 6 - 0
service-base/src/main/resources/mapper/ValuationMarketCodeMapper.xml

@@ -13,5 +13,11 @@
         <result column="updatetime" property="updateTime"/>
     </resultMap>
 
+    <select id="queryMarketCode" resultType="java.lang.String">
+        select market_code
+        from PPW_EMAIL.valuation_market_code
+        where isvalid = 1
+        order by sort_id
+    </select>
 
 </mapper>

+ 53 - 0
service-base/src/main/resources/mapper/ValuationTableAttributeMapper.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.simuwang.base.mapper.ValuationTableAttributeMapper">
+    <resultMap id="BaseResultMap" type="com.simuwang.base.pojo.dos.ValuationTableAttributeDO">
+        <id column="id" property="id"/>
+        <result column="valuation_id" property="valuationId"/>
+        <result column="subject_code" property="subjectCode"/>
+        <result column="subject_name" property="subjectName"/>
+        <result column="currency" property="currency"/>
+        <result column="exchange_rate" property="exchangeRate"/>
+        <result column="securities_amount" property="securitiesAmount"/>
+        <result column="unit_cost" property="unitCost"/>
+        <result column="ori_currency_cost" property="oriCurrencyCost"/>
+        <result column="net_cost" property="netCost"/>
+        <result column="net_cost_ratio" property="netCostRatio"/>
+        <result column="market_price" property="marketPrice"/>
+        <result column="ori_currency_market_value" property="oriCurrencyMarketValue"/>
+        <result column="market_value" property="marketValue"/>
+        <result column="market_value_ratio" property="marketValueRatio"/>
+        <result column="ori_currency_increment" property="oriCurrencyIncrement"/>
+        <result column="increment" property="increment"/>
+        <result column="halt_info" property="haltInfo"/>
+        <result column="rights_interests_info" property="rightsInterestsInfo"/>
+        <result column="isvalid" property="isvalid"/>
+        <result column="creatorid" property="creatorId"/>
+        <result column="createtime" property="createTime"/>
+        <result column="updaterid" property="updaterId"/>
+        <result column="updatetime" property="updateTime"/>
+    </resultMap>
+
+    <delete id="deleteByValuationId">
+        delete
+        from PPW_EMAIL.valuation_table_attribute
+        where valuation_id = #{valuationId}
+          and isvalid = 1
+    </delete>
+
+    <insert id="batchInsert" parameterType="com.simuwang.base.pojo.dos.ValuationTableAttributeDO">
+        insert into PPW_EMAIL.valuation_table_attribute(valuation_id, subject_code, subject_name, currency, exchange_rate, securities_amount,
+                                                        unit_cost, ori_currency_cost, net_cost, net_cost_ratio, market_price, ori_currency_market_value,
+                                                        market_value, market_value_ratio, ori_currency_increment, increment, halt_info, rights_interests_info,
+                                                        isvalid, creatorid, createtime, updaterid, updatetime)
+        values
+        <foreach collection="itemDoList" item="itemDo" index="index" separator=",">
+            (#{itemDo.valuationId},#{itemDo.subjectCode},#{itemDo.subjectName},#{itemDo.currency},#{itemDo.exchangeRate},#{itemDo.securitiesAmount},
+             #{itemDo.unitCost},#{itemDo.oriCurrencyCost},#{itemDo.netCost},#{itemDo.netCostRatio},#{itemDo.marketPrice},#{itemDo.oriCurrencyMarketValue},
+             #{itemDo.marketValue},#{itemDo.marketValueRatio},#{itemDo.oriCurrencyIncrement},#{itemDo.increment},#{itemDo.haltInfo},#{itemDo.rightsInterestsInfo},
+            #{itemDo.isvalid}, #{itemDo.creatorId}, #{itemDo.createTime}, #{itemDo.updaterId}, #{itemDo.updateTime})
+        </foreach>
+    </insert>
+
+
+</mapper>

+ 35 - 0
service-base/src/main/resources/mapper/ValuationTableMapper.xml

@@ -28,6 +28,41 @@
         <result column="updatetime" property="updateTime"/>
     </resultMap>
 
+    <update id="unValid">
+        update  PPW_EMAIL.valuation_table
+        set isvalid = 0
+        where fund_id = #{fundId}
+          and valuation_date = #{valuationDate}
+    </update>
 
+    <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.simuwang.base.pojo.dos.ValuationTableDO" useGeneratedKeys="true">
+        <!--@mbg.generated-->
+        insert into PPW_EMAIL.valuation_table (fund_id,
+        title, valuation_date, nav,
+        head_info, tail_info, original_file,
+        converted_file, file_url, table_type,
+        isvalid, creatorid, createtime,
+        updaterid, updatetime, is_analyzed,
+        total_market_value,net_assets_value,increment,from_email)
+        values (#{fundId,jdbcType=VARCHAR},
+        #{title,jdbcType=VARCHAR}, #{valuationDate,jdbcType=DATE}, #{nav,jdbcType=DECIMAL},
+        #{headInfo,jdbcType=VARCHAR}, #{tailInfo,jdbcType=VARCHAR}, #{originalFile,jdbcType=VARCHAR},
+        #{convertedFile,jdbcType=VARCHAR}, #{fileUrl,jdbcType=VARCHAR}, #{tableType,jdbcType=TINYINT},
+        #{isvalid,jdbcType=INTEGER}, #{creatorId,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP},
+        #{updaterId,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}, #{isAnalyzed,jdbcType=TINYINT},
+        #{totalMarketValue,jdbcType=DECIMAL},#{netAssetsValue,jdbcType=DECIMAL},#{increment,jdbcType=DECIMAL},
+        #{fromEmail,jdbcType=TINYINT})
+    </insert>
+
+    <update id="updateUpdateAnalyzed">
+        update PPW_EMAIL.valuation_table
+        set is_analyzed = 1,
+            has_stock = #{hasStock},
+            has_bond = #{hasBond},
+            has_future = #{hasFuture}
+        where valuation_date = #{valuationDate}
+          and fund_id = #{fundId}
+          and isvalid = 1
+    </update>
 
 </mapper>

+ 23 - 52
service-daq/src/main/java/com/simuwang/daq/service/EmailParseService.java

@@ -53,32 +53,29 @@ public class EmailParseService {
     private final EmailFieldMappingMapper emailFieldMapper;
     private final EmailParserFactory emailParserFactory;
     private final EmailParseInfoMapper emailParseInfoMapper;
-    private final FundInfoMapper fundInfoMapper;
     private final EmailFileInfoMapper emailFileInfoMapper;
     private final EmailFundNavMapper emailFundNavMapper;
     private final EmailFundAssetMapper emailFundAssetMapper;
-    private final FundAliasMapper fundAliasMapper;
     private final AssetMapper assetMapper;
     private final NavMapper navMapper;
+    private final FundService fundService;
 
     public EmailParseService(EmailTypeRuleMapper emailTypeRuleMapper, EmailRuleConfig emailRuleConfig,
                              EmailFieldMappingMapper emailFieldMapper, EmailParserFactory emailParserFactory,
-                             EmailParseInfoMapper emailParseInfoMapper, FundInfoMapper fundInfoMapper,
-                             EmailFileInfoMapper emailFileInfoMapper, EmailFundNavMapper emailFundNavMapper,
-                             EmailFundAssetMapper emailFundAssetMapper, FundAliasMapper fundAliasMapper,
-                             AssetMapper assetMapper, NavMapper navMapper) {
+                             EmailParseInfoMapper emailParseInfoMapper, EmailFileInfoMapper emailFileInfoMapper,
+                             EmailFundNavMapper emailFundNavMapper, EmailFundAssetMapper emailFundAssetMapper,
+                             AssetMapper assetMapper, NavMapper navMapper, FundService fundService) {
         this.emailTypeRuleMapper = emailTypeRuleMapper;
         this.emailRuleConfig = emailRuleConfig;
         this.emailFieldMapper = emailFieldMapper;
         this.emailParserFactory = emailParserFactory;
         this.emailParseInfoMapper = emailParseInfoMapper;
-        this.fundInfoMapper = fundInfoMapper;
         this.emailFileInfoMapper = emailFileInfoMapper;
         this.emailFundNavMapper = emailFundNavMapper;
         this.emailFundAssetMapper = emailFundAssetMapper;
-        this.fundAliasMapper = fundAliasMapper;
         this.assetMapper = assetMapper;
         this.navMapper = navMapper;
+        this.fundService = fundService;
     }
 
     /**
@@ -197,10 +194,10 @@ public class EmailParseService {
             List<String> dateList = navMapper.queryFundNavByDate(entry.getKey(), priceDateList);
             List<NavDO> updateNavDoList = navDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
             List<NavDO> insertNavDoList = navDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
-            if(CollUtil.isNotEmpty(insertNavDoList)){
+            if (CollUtil.isNotEmpty(insertNavDoList)) {
                 navMapper.batchInsert(insertNavDoList);
             }
-            if(CollUtil.isNotEmpty(updateNavDoList)){
+            if (CollUtil.isNotEmpty(updateNavDoList)) {
                 navMapper.batchUpdate(updateNavDoList);
             }
         }
@@ -217,10 +214,10 @@ public class EmailParseService {
             List<String> dateList = assetMapper.queryFundNavByDate(entry.getKey(), priceDateList);
             List<AssetDO> updateAssetDoList = assetDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
             List<AssetDO> insertAssetDoList = assetDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
-            if(CollUtil.isNotEmpty(insertAssetDoList)){
+            if (CollUtil.isNotEmpty(insertAssetDoList)) {
                 assetMapper.batchInsert(insertAssetDoList);
             }
-            if(CollUtil.isNotEmpty(updateAssetDoList)){
+            if (CollUtil.isNotEmpty(updateAssetDoList)) {
                 assetMapper.batchUpdate(updateAssetDoList);
             }
         }
@@ -233,6 +230,8 @@ public class EmailParseService {
         if (assetNet == null) {
             return fundAssetDOList;
         }
+        Integer isStored = fundNavDTO.getParseStatus() != null
+                && (fundNavDTO.getParseStatus().equals(NavParseStatusConst.ASSET_NET_NEGATIVE) || fundNavDTO.getParseStatus().equals(NavParseStatusConst.SUCCESS)) ? 1 : 0;
         Date priceDate = DateUtil.parse(fundNavDTO.getPriceDate(), DateConst.YYYY_MM_DD);
         if (CollUtil.isNotEmpty(fundNavDTO.getFundIdList())) {
             for (String fundId : fundNavDTO.getFundIdList()) {
@@ -244,6 +243,8 @@ public class EmailParseService {
                 emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
                 emailFundAssetDO.setAssetNet(assetNet);
                 emailFundAssetDO.setAssetShare(assetShare);
+                emailFundAssetDO.setIsStored(isStored);
+                emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
                 emailFundAssetDO.setIsvalid(1);
                 emailFundAssetDO.setCreatorId(0);
                 emailFundAssetDO.setCreateTime(parseDate);
@@ -259,6 +260,8 @@ public class EmailParseService {
             emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
             emailFundAssetDO.setAssetNet(assetNet);
             emailFundAssetDO.setAssetShare(assetShare);
+            emailFundAssetDO.setIsStored(isStored);
+            emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
             emailFundAssetDO.setIsvalid(1);
             emailFundAssetDO.setCreatorId(0);
             emailFundAssetDO.setCreateTime(parseDate);
@@ -343,12 +346,15 @@ public class EmailParseService {
             fundNavDTO.setParseStatus(NavParseStatusConst.NAV_DEFICIENCY);
             return;
         }
-        // 2.匹配基金
-        List<String> fundIdList = matchFund(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber());
-        if (CollUtil.isEmpty(fundIdList)) {
-            fundNavDTO.setParseStatus(NavParseStatusConst.NOT_MATCH);
+        // 2.匹配基金(考虑到解析估值表时已经匹配上基金的情况)
+        if (CollUtil.isEmpty(fundNavDTO.getFundIdList())) {
+            List<String> fundIdList = fundService.getFundIdByNamesAndCode(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber());
+            if (CollUtil.isEmpty(fundIdList)) {
+                fundNavDTO.setParseStatus(NavParseStatusConst.NOT_MATCH);
+                return;
+            }
+            fundNavDTO.setFundIdList(fundIdList);
         }
-        fundNavDTO.setFundIdList(fundIdList);
         // 考虑单独规模文件时 -> 无单位净值和累计净值
         // 3.单位净值或累计净值不大于0
         if (!emailTitle.contains("规模")) {
@@ -366,41 +372,6 @@ public class EmailParseService {
         fundNavDTO.setParseStatus(NavParseStatusConst.SUCCESS);
     }
 
-    private List<String> matchFund(String fundName, String registerNumber) {
-        // 1.基金名称 + 备案编码 一起进行匹配
-        List<String> fundIdList = fundInfoMapper.queryFundByNameAndRegisterNumber(fundName, registerNumber);
-        if (CollUtil.isNotEmpty(fundIdList)) {
-            return fundIdList;
-        }
-        fundIdList = fundAliasMapper.queryFundByNameAndRegisterNumber(fundName, registerNumber);
-        if (CollUtil.isNotEmpty(fundIdList)) {
-            return fundIdList;
-        }
-        // 2.基金名称匹配
-        if (StrUtil.isNotBlank(fundName)) {
-            List<String> fundIds = fundInfoMapper.queryFundByName(fundName);
-            if (CollUtil.isNotEmpty(fundIds)) {
-                return fundIdList;
-            }
-            fundIds = fundAliasMapper.queryFundByName(fundName);
-            if (CollUtil.isNotEmpty(fundIds)) {
-                return fundIds;
-            }
-        }
-        // 3.备案编码匹配
-        if (StrUtil.isNotBlank(fundName)) {
-            List<String> fundIds = fundInfoMapper.queryFundByRegisterNumber(registerNumber);
-            if (CollUtil.isNotEmpty(fundIds)) {
-                return fundIds;
-            }
-            fundIds = fundAliasMapper.queryFundByRegisterNumber(registerNumber);
-            if (CollUtil.isNotEmpty(fundIds)) {
-                return fundIds;
-            }
-        }
-        return CollUtil.newArrayList();
-    }
-
     private Integer saveEmailParseInfo(EmailParseInfoDO emailParseInfoDO) {
         if (emailParseInfoDO == null) {
             return null;

+ 87 - 0
service-daq/src/main/java/com/simuwang/daq/service/FundService.java

@@ -0,0 +1,87 @@
+package com.simuwang.daq.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.simuwang.base.mapper.FundAliasMapper;
+import com.simuwang.base.mapper.FundInfoMapper;
+import com.simuwang.base.pojo.dos.FundAliasDO;
+import com.simuwang.base.pojo.dos.FundInfoDO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class FundService {
+
+    private final FundInfoMapper fundInfoMapper;
+    private final FundAliasMapper fundAliasMapper;
+
+    public FundService(FundInfoMapper fundInfoMapper, FundAliasMapper fundAliasMapper) {
+        this.fundInfoMapper = fundInfoMapper;
+        this.fundAliasMapper = fundAliasMapper;
+    }
+
+    /**
+     * 根据基金名称和备案编码匹配基金信息
+     *
+     * @param fundName       基金名称
+     * @param registerNumber 备案编码
+     * @return 匹配上的基金id
+     */
+    public List<String> getFundIdByNamesAndCode(String fundName, String registerNumber) {
+        List<FundInfoDO> fundInfoDOList = getFundInfoByNamesAndCode(fundName, registerNumber);
+        return fundInfoDOList.stream().map(FundInfoDO::getFundId).distinct().collect(Collectors.toList());
+    }
+
+    /**
+     * 根据基金名称和备案编码匹配基金信息
+     *
+     * @param fundName       基金名称
+     * @param registerNumber 备案编码
+     * @return 匹配上的基金信息(基金id, 基金名称, 备案编码)
+     */
+    public List<FundInfoDO> getFundInfoByNamesAndCode(String fundName, String registerNumber) {
+        // 1.基金名称 + 备案编码 一起进行匹配
+        List<FundInfoDO> fundInfoDOList = fundInfoMapper.queryFundByNameAndRegisterNumber(fundName, registerNumber);
+        if (CollUtil.isNotEmpty(fundInfoDOList)) {
+            return fundInfoDOList;
+        }
+        List<FundAliasDO> fundAliasDOList = fundAliasMapper.queryFundByNameAndRegisterNumber(fundName, registerNumber);
+        if (CollUtil.isNotEmpty(fundAliasDOList)) {
+            return fundAliasDOList.stream().map(this::convertToFundInfoDO).collect(Collectors.toList());
+        }
+        // 2.基金名称匹配
+        if (StrUtil.isNotBlank(fundName)) {
+            List<FundInfoDO> fundInfos = fundInfoMapper.queryFundByName(fundName);
+            if (CollUtil.isNotEmpty(fundInfos)) {
+                return fundInfos;
+            }
+            List<FundAliasDO> fundAliasList = fundAliasMapper.queryFundByName(fundName);
+            if (CollUtil.isNotEmpty(fundAliasList)) {
+                return fundAliasList.stream().map(this::convertToFundInfoDO).collect(Collectors.toList());
+            }
+        }
+        // 3.备案编码匹配
+        if (StrUtil.isNotBlank(fundName)) {
+            List<FundInfoDO> fundInfos = fundInfoMapper.queryFundByRegisterNumber(registerNumber);
+            if (CollUtil.isNotEmpty(fundInfos)) {
+                return fundInfos;
+            }
+            List<FundAliasDO> fundAliasList = fundAliasMapper.queryFundByRegisterNumber(registerNumber);
+            if (CollUtil.isNotEmpty(fundAliasList)) {
+                return fundAliasList.stream().map(this::convertToFundInfoDO).collect(Collectors.toList());
+            }
+        }
+        return CollUtil.newArrayList();
+    }
+
+    public FundInfoDO convertToFundInfoDO(FundAliasDO fundAliasDO) {
+        FundInfoDO fundInfoDO = new FundInfoDO();
+        fundInfoDO.setFundId(fundAliasDO.getTargetFundId());
+        fundInfoDO.setFundName(fundAliasDO.getTargetFundName());
+        fundInfoDO.setRegisterNumber(fundAliasDO.getTargetRegisterNumber());
+        return fundInfoDO;
+    }
+
+}

+ 70 - 1
service-daq/src/main/java/com/simuwang/daq/service/ValuationEmailParser.java

@@ -1,12 +1,23 @@
 package com.simuwang.daq.service;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.util.StrUtil;
+import com.simuwang.base.common.conts.EmailTypeConst;
+import com.simuwang.base.common.util.ExcelUtil;
 import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
 import com.simuwang.base.pojo.dto.EmailFundNavDTO;
+import com.simuwang.base.pojo.valuation.AssetsValuationResult;
+import com.simuwang.base.pojo.valuation.ParseValuationInfo;
+import com.simuwang.base.pojo.valuation.ValuationNeedParseParam;
 import org.springframework.stereotype.Component;
 
+import java.io.File;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * @author mozuwen
@@ -16,12 +27,70 @@ import java.util.Map;
 @Component
 public class ValuationEmailParser extends AbstractEmailParser {
 
+    private final ValuationParseService valuationParseService;
+
+    public ValuationEmailParser(ValuationParseService valuationParseService) {
+        this.valuationParseService = valuationParseService;
+    }
+
+    @Override
+    public boolean isSupport(Integer emailType) {
+        return EmailTypeConst.VALUATION_EMAIL_TYPE.equals(emailType);
+    }
+
     @Override
     public List<EmailFundNavDTO> parse(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
         List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
+        if (emailContentInfoDTO == null || StrUtil.isBlank(emailContentInfoDTO.getFilePath())
+                || !ExcelUtil.isExcel(emailContentInfoDTO.getFileName())) {
+            return emailFundNavDTOList;
+        }
+        List<ValuationNeedParseParam> valuationNeedParseParams = buildValuationNeedParseParam(emailContentInfoDTO);
+        List<AssetsValuationResult.Record> recordList = valuationParseService.parseValuationExcel(valuationNeedParseParams);
+        if (CollUtil.isNotEmpty(recordList)) {
+            List<AssetsValuationResult.Record> parseSucessList = recordList.stream().filter(e -> e.getSuccess() == 1).collect(Collectors.toList());
+            EmailFundNavDTO fundNavDTO = convertToFundNavDTO(parseSucessList);
+            Optional.ofNullable(fundNavDTO).ifPresent(emailFundNavDTOList::add);
+        }
+        return emailFundNavDTOList;
+    }
 
+    private List<ValuationNeedParseParam> buildValuationNeedParseParam(EmailContentInfoDTO emailContentInfoDTO) {
+        File file = new File(emailContentInfoDTO.getFilePath());
+        ValuationNeedParseParam parseParam = new ValuationNeedParseParam();
+        parseParam.setFile(file);
+        parseParam.setFileUrl(emailContentInfoDTO.getFilePath());
+        parseParam.setOriginFileName(emailContentInfoDTO.getFileName());
+        parseParam.setFundId(null);
+        parseParam.setFromEmail(1);
+        return ListUtil.toList(parseParam);
+    }
 
-        return emailFundNavDTOList;
+    private EmailFundNavDTO convertToFundNavDTO(List<AssetsValuationResult.Record> parseSucessList) {
+        if (CollUtil.isEmpty(parseSucessList)) {
+            return null;
+        }
+        String fundName = parseSucessList.stream().map(AssetsValuationResult.Record::getParseValuationInfo)
+                .filter(Objects::nonNull).map(ParseValuationInfo::getFundName).distinct().findFirst().orElse(null);
+        String registerNumber = parseSucessList.stream().map(AssetsValuationResult.Record::getParseValuationInfo)
+                .filter(Objects::nonNull).map(ParseValuationInfo::getRegisterNumber).distinct().findFirst().orElse(null);
+        String valuationDate = parseSucessList.stream().map(AssetsValuationResult.Record::getDate).filter(Objects::nonNull).distinct().findFirst().orElse(null);
+        String nav = parseSucessList.stream().map(AssetsValuationResult.Record::getNav).filter(Objects::nonNull).distinct().findFirst().orElse(null);
+        String cumulativeNavWithdrawal = parseSucessList.stream().map(AssetsValuationResult.Record::getCumulativeNavWithdrawal).filter(Objects::nonNull).distinct().findFirst().orElse(null);
+        String assetNet = parseSucessList.stream().map(AssetsValuationResult.Record::getAssetNet).filter(Objects::nonNull).distinct().findFirst().orElse(null);
+        String assetShare = parseSucessList.stream().map(AssetsValuationResult.Record::getAssetShare).filter(Objects::nonNull).distinct().findFirst().orElse(null);
+        List<String> fundIdList = parseSucessList.stream().map(AssetsValuationResult.Record::getFundId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
+
+        EmailFundNavDTO fundNavDTO = new EmailFundNavDTO();
+        fundNavDTO.setFundName(fundName);
+        fundNavDTO.setRegisterNumber(registerNumber);
+        fundNavDTO.setPriceDate(valuationDate);
+        fundNavDTO.setNav(nav);
+        fundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
+        fundNavDTO.setAssetNet(assetNet);
+        fundNavDTO.setAssetShare(assetShare);
+        fundNavDTO.setFundIdList(fundIdList);
+        return fundNavDTO;
     }
 
 }

+ 603 - 0
service-daq/src/main/java/com/simuwang/daq/service/ValuationParseService.java

@@ -0,0 +1,603 @@
+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.setExcelName(valuationNeedParseParam.getOriginFileName());
+                                record.setMsg("未匹配基金");
+                                record.setDate(details.getValuationDate());
+                                record.setSuccess(0);
+                                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, 0, 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, 0, 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, Integer userId, 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(userId);
+        tableInfo.setUpdaterId(userId);
+        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);
+    }
+}