Browse Source

feat: 分红导入功能开发

chenjianhua 7 months ago
parent
commit
b4b7591a2b

+ 6 - 0
service-base/pom.xml

@@ -212,6 +212,12 @@
             <artifactId>commons-compress</artifactId>
             <version>1.27.1</version>
         </dependency>
+        <!--阿里巴巴excel解析库 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.1.1</version>
+        </dependency>
     </dependencies>
 
 <!--    <build>-->

+ 19 - 0
service-base/src/main/java/com/simuwang/base/common/conts/ExcelConst.java

@@ -0,0 +1,19 @@
+package com.simuwang.base.common.conts;
+
+/**
+ * FileName: ExcelConst
+ * Author:   chenjianhua
+ * Date:     2024/9/16 12:56
+ * Description: ${DESCRIPTION}
+ */
+public class ExcelConst {
+    public final static String REQUIRE_FIELD = "必填字段不能为空";
+
+    public final static String CASH_DIVIDENDS= "分红";
+    public final static String DIVIDENDS_SPLIT= "拆分";
+
+    public final static String CASH_DIVIDENDS_FAIL= "现金分红需为正数";
+    public final static String DIVIDENDS_SPLIT_FAIL= "份额分红不能为0";
+    public final static String NOT_MAPPING_FUND= "未匹配基金";
+    public final static String SAVE_FAIL= "数据入库失败";
+}

+ 46 - 0
service-base/src/main/java/com/simuwang/base/common/enums/DistributeType.java

@@ -0,0 +1,46 @@
+package com.simuwang.base.common.enums;
+
+import java.util.stream.Stream;
+
+/**
+ * FileName: DistributeType
+ * Author:   chenjianhua
+ * Date:     2024/9/16 13:30
+ * Description: ${DESCRIPTION}
+ */
+public enum DistributeType {
+    //单位分红/拆分比例
+    CASH_DIVIDENDS(1, "分红"), DIVIDENDS_SPLIT(2, "拆分");
+
+    private final Integer code;
+    private final String info;
+
+    DistributeType(Integer code, String info) {
+        this.code = code;
+        this.info = info;
+    }
+
+    public static DistributeType getDistributeTypeByCode(Integer code) {
+        if (null == code){
+            return null;
+        }
+        for(DistributeType s : DistributeType.values()){
+            if(code.equals(s.getCode())){
+                return s;
+            }
+        }
+        return null;
+    }
+
+    public static DistributeType getDistributeTypeByInfo(String info) {
+        return Stream.of(DistributeType.values()).filter(e -> e.info.equals(info)).findFirst().orElse(null);
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+}

+ 2 - 0
service-base/src/main/java/com/simuwang/base/mapper/FundInfoMapper.java

@@ -33,4 +33,6 @@ public interface FundInfoMapper {
     long countFundInfo(FundInfoPageQuery fundInfoPageQuery);
 
     String getFundNameByFundId(String fundId);
+
+    String queryFundIdByName(@Param("fundName") String fundName);
 }

+ 43 - 0
service-base/src/main/java/com/simuwang/base/pojo/dto/DistributionExcelData.java

@@ -0,0 +1,43 @@
+package com.simuwang.base.pojo.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * FileName: DistributionExcelData
+ * Author:   chenjianhua
+ * Date:     2024/9/16 10:57
+ * Description: ${DESCRIPTION}
+ */
+@Data
+public class DistributionExcelData {
+    /**
+     * 基金ID
+     */
+    private String fundId;
+    /**
+     * 基金名称
+     */
+    private String fundName;
+    /**
+     * 净值日期
+     */
+    private String priceDate;
+    /**
+     * 单位净值
+     */
+    private String nav;
+    /**
+     * 累计净值
+     */
+    private String cumulativeNavWithdrawal;
+    /**
+     * 分红方式
+     */
+    private String distributeType;
+    /**
+     * 分红/拆分比例
+     */
+    private String distribution;
+}

+ 36 - 0
service-base/src/main/java/com/simuwang/base/pojo/vo/ExcelFailDataVO.java

@@ -0,0 +1,36 @@
+package com.simuwang.base.pojo.vo;
+
+import lombok.Data;
+
+/**
+ * FileName: ExcelSuccessDataVO
+ * Author:   chenjianhua
+ * Date:     2024/9/16 12:41
+ * Description: ${DESCRIPTION}
+ */
+@Data
+public class ExcelFailDataVO {
+
+    /**
+     * 行号
+     */
+    private Integer rowNum;
+    /**
+     * 基金名称
+     */
+    private String fundName;
+    /**
+     * 净值日期
+     */
+    private String priceDate;
+    /**
+     * 分红类型
+     */
+    private String distributeType;
+    /**
+     * 单位分红/拆分比例
+     */
+    private String distribution;
+
+    private String failReason;
+}

+ 30 - 0
service-base/src/main/java/com/simuwang/base/pojo/vo/ExcelSuccessDataVO.java

@@ -0,0 +1,30 @@
+package com.simuwang.base.pojo.vo;
+
+import lombok.Data;
+
+/**
+ * FileName: ExcelSuccessDataVO
+ * Author:   chenjianhua
+ * Date:     2024/9/16 12:41
+ * Description: ${DESCRIPTION}
+ */
+@Data
+public class ExcelSuccessDataVO {
+
+    /**
+     * 基金名称
+     */
+    private String fundName;
+    /**
+     * 净值日期
+     */
+    private String priceDate;
+    /**
+     * 分红类型
+     */
+    private String distributeType;
+    /**
+     * 单位分红
+     */
+    private String distribution;
+}

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

@@ -145,5 +145,11 @@
     <select id="getFundNameByFundId" resultType="java.lang.String" parameterType="java.lang.String">
         select fund_name from PPW_EMAIL.pvn_fund_info where fund_id=#{fundId} and isvalid=1
     </select>
+    <select id="queryFundIdByName" resultType="java.lang.String" parameterType="java.lang.String">
+        select fund_id
+        from PPW_EMAIL.pvn_fund_info
+        where isvalid = 1
+          and fund_name = #{fundName} limit 1
+    </select>
 
 </mapper>

+ 27 - 3
service-manage/src/main/java/com/simuwang/manage/api/distribution/DistributionController.java

@@ -9,10 +9,11 @@ import com.simuwang.base.pojo.vo.IdListVO;
 import com.simuwang.base.pojo.vo.IdVO;
 import com.simuwang.manage.service.DistributionService;
 import com.smppw.common.pojo.ResultVo;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * FileName: DistributionController
@@ -37,15 +38,38 @@ public class DistributionController {
         return result;
     }
 
+    /**
+     * 保存分红信息
+     * @param distributionVO
+     * @return
+     */
     @RequestMapping("save-distribution")
     public ResultVo saveDistribution(@RequestBody DistributionVO distributionVO){
         ResultVo vo = distributionService.saveDistribution(distributionVO);
         return vo;
     }
 
+    /**
+     * 删除分红信息
+     * @param idListVO
+     * @return
+     */
     @RequestMapping("delete-distribution")
     public boolean deleteDistribution(IdListVO idListVO){
         distributionService.deleteDistribution(idListVO);
         return true;
     }
+
+    /**
+     * 上传分红信息
+     * @param file
+     * @param response
+     * @param request
+     * @return
+     */
+    @RequestMapping("upload-distribution")
+    public ResultVo uploadDistribution(@RequestParam(value = "file",required = false) MultipartFile file, HttpServletResponse response, HttpServletRequest request){
+        ResultVo vo = distributionService.uploadDistribution(file);
+        return vo;
+    }
 }

+ 4 - 0
service-manage/src/main/java/com/simuwang/manage/service/DistributionService.java

@@ -6,6 +6,7 @@ import com.simuwang.base.pojo.vo.DistributionTablePageVO;
 import com.simuwang.base.pojo.vo.DistributionVO;
 import com.simuwang.base.pojo.vo.IdListVO;
 import com.smppw.common.pojo.ResultVo;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * FileName: DistributionService
@@ -14,9 +15,12 @@ import com.smppw.common.pojo.ResultVo;
  * Description: ${DESCRIPTION}
  */
 public interface DistributionService {
+
     MybatisPage<DistributionTablePageVO> searchDistributionList(DistributionPageQuery distributionPageQuery);
 
     ResultVo saveDistribution(DistributionVO distributionVO);
 
     void deleteDistribution(IdListVO idListVO);
+
+    ResultVo uploadDistribution(MultipartFile file);
 }

+ 148 - 4
service-manage/src/main/java/com/simuwang/manage/service/impl/DistributionServiceImpl.java

@@ -1,26 +1,43 @@
 package com.simuwang.manage.service.impl;
 
 import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.read.listener.PageReadListener;
+import com.alibaba.excel.support.ExcelTypeEnum;
 import com.simuwang.base.common.conts.DateConst;
+import com.simuwang.base.common.conts.ExcelConst;
+import com.simuwang.base.common.enums.DistributeType;
 import com.simuwang.base.common.support.MybatisPage;
 import com.simuwang.base.common.util.DateUtils;
+import com.simuwang.base.common.util.ExcelUtil;
 import com.simuwang.base.common.util.StringUtil;
 import com.simuwang.base.mapper.DistributionMapper;
+import com.simuwang.base.mapper.FundInfoMapper;
 import com.simuwang.base.mapper.NavMapper;
 import com.simuwang.base.pojo.dos.DistributionDO;
 import com.simuwang.base.pojo.dos.DistributionTablePageDO;
+import com.simuwang.base.pojo.dos.FundInfoDO;
 import com.simuwang.base.pojo.dos.NavDO;
+import com.simuwang.base.pojo.dto.DistributionExcelData;
 import com.simuwang.base.pojo.dto.query.DistributionPageQuery;
-import com.simuwang.base.pojo.vo.DistributionTablePageVO;
-import com.simuwang.base.pojo.vo.DistributionVO;
-import com.simuwang.base.pojo.vo.IdListVO;
+import com.simuwang.base.pojo.vo.*;
 import com.simuwang.manage.service.DistributionService;
 import com.smppw.common.pojo.ResultVo;
+import com.smppw.common.pojo.enums.status.ResultCode;
+import com.smppw.common.pojo.enums.status.StatusCode;
 import org.apache.poi.ss.formula.functions.Na;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -32,11 +49,17 @@ import java.util.stream.Collectors;
 @Service
 public class DistributionServiceImpl implements DistributionService {
 
+    private static final Logger logger = LoggerFactory.getLogger(DistributionServiceImpl.class);
+
+
     @Autowired
     private DistributionMapper distributionMapper;
 
     @Autowired
     private NavMapper navMapper;
+
+    @Autowired
+    private FundInfoMapper fundInfoMapper;
     @Override
     public MybatisPage<DistributionTablePageVO> searchDistributionList(DistributionPageQuery distributionPageQuery) {
         List<DistributionTablePageDO> distributionTablePageDOList = distributionMapper.searchDistributionList(distributionPageQuery);
@@ -57,7 +80,7 @@ public class DistributionServiceImpl implements DistributionService {
         distributionDO.setUpdateTime(DateUtils.getNowDate());
         DistributionDO oldDistributionDO = distributionMapper.selectDistributionByDate(distributionVO.getFundId(),DateUtils.parse(distributionVO.getDistributeDate(),DateUtils.YYYY_MM_DD));
         if(!StringUtil.isNull(oldDistributionDO)){
-            if(!oldDistributionDO.getId().equals(distributionVO.getId())){
+            if(distributionVO.getId() != null && !oldDistributionDO.getId().equals(distributionVO.getId())){
                 vo.setData(false);
                 vo.setMsg("同一基金在同一天不能已存在分红数据");
                 return vo;
@@ -92,4 +115,125 @@ public class DistributionServiceImpl implements DistributionService {
     public void deleteDistribution(IdListVO idListVO) {
         distributionMapper.deleteDistribution(idListVO.getIdList());
     }
+
+    @Override
+    public ResultVo uploadDistribution(MultipartFile excelFile) {
+        ResultVo vo = new ResultVo(ResultCode.SUCCESS);
+        File file = null;
+        try{
+            file = excelFile.getResource().getFile();
+            List<DistributionExcelData> list = parseDistributionFile(file);
+            vo.setData(parseResult(list));
+        }catch (Exception e){
+            logger.error(e.getMessage(),e);
+            vo.setMsg("文件解析异常");
+            vo.setData(false);
+            return vo;
+        }
+
+        return vo;
+    }
+
+    private Map<String,Object> parseResult(List<DistributionExcelData> list) {
+        Map<String,Object> result = new HashMap<>();
+        int startRow = 3;
+        List<ExcelSuccessDataVO> successDataVOList = new ArrayList<>();
+        List<ExcelFailDataVO> excelFailDataVOList = new ArrayList<>();
+        for(int dataIdx=1;dataIdx<list.size(); dataIdx++){
+            DistributionExcelData excelData = list.get(dataIdx);
+            if((StringUtil.isEmpty(excelData.getFundName()) && StringUtil.isEmpty(excelData.getFundId()))
+            || StringUtil.isEmpty(excelData.getPriceDate()) || StringUtil.isEmpty(excelData.getNav())
+            || StringUtil.isEmpty(excelData.getDistributeType()) || StringUtil.isEmpty(excelData.getDistribution())
+            || StringUtil.isEmpty(excelData.getCumulativeNavWithdrawal())){
+                ExcelFailDataVO failDataVO = new ExcelFailDataVO();
+                failDataVO.setFailReason(ExcelConst.REQUIRE_FIELD);
+                failDataVO.setRowNum(dataIdx+startRow);
+                failDataVO.setDistribution(excelData.getDistribution());
+                failDataVO.setFundName(excelData.getFundName());
+                failDataVO.setPriceDate(excelData.getPriceDate());
+                failDataVO.setDistributeType(excelData.getDistributeType());
+                excelFailDataVOList.add(failDataVO);
+                continue;
+            }
+            if(excelData.getDistributeType().trim().equals(ExcelConst.CASH_DIVIDENDS)){
+                if(Double.valueOf(excelData.getDistribution()) < 0f){
+                    ExcelFailDataVO failDataVO = toExcelFailDataVO(excelData,ExcelConst.CASH_DIVIDENDS_FAIL,dataIdx+startRow);
+                    excelFailDataVOList.add(failDataVO);
+                    continue;
+                }
+            }else{
+                if(Double.valueOf(excelData.getDistribution()) == 0f){
+                    ExcelFailDataVO failDataVO = toExcelFailDataVO(excelData,ExcelConst.DIVIDENDS_SPLIT_FAIL,dataIdx+startRow);
+                    excelFailDataVOList.add(failDataVO);
+                    continue;
+                }
+            }
+            if(StringUtil.isNotEmpty(excelData.getFundId())){
+                String fundName = fundInfoMapper.getFundNameByFundId(excelData.getFundId());
+                if(StringUtil.isEmpty(fundName)){
+                    ExcelFailDataVO failDataVO = toExcelFailDataVO(excelData,ExcelConst.NOT_MAPPING_FUND,dataIdx+startRow);
+                    excelFailDataVOList.add(failDataVO);
+                    continue;
+                }
+            }else{
+                String fundId = fundInfoMapper.queryFundIdByName(excelData.getFundName());
+                if(StringUtil.isEmpty(fundId)){
+                    ExcelFailDataVO failDataVO = toExcelFailDataVO(excelData,ExcelConst.NOT_MAPPING_FUND,dataIdx+startRow);
+                    excelFailDataVOList.add(failDataVO);
+                    continue;
+                }else{
+                    excelData.setFundId(fundId);
+                }
+            }
+            //开始处理成功数据
+            DistributionVO distributionVO = new DistributionVO();
+            distributionVO.setFundId(excelData.getFundId());
+            distributionVO.setDistributeDate(excelData.getPriceDate());
+            distributionVO.setDistributeType(DistributeType.getDistributeTypeByInfo(excelData.getDistributeType()).getCode());
+            distributionVO.setDistribution(BigDecimal.valueOf(Double.valueOf(excelData.getDistribution())));
+            distributionVO.setNav(BigDecimal.valueOf(Double.valueOf(excelData.getNav())));
+            distributionVO.setCumulativeNavWithdrawal(BigDecimal.valueOf(Double.valueOf(excelData.getCumulativeNavWithdrawal())));
+            ResultVo vo = saveDistribution(distributionVO);
+            if((boolean)vo.getData() == true){
+                ExcelSuccessDataVO successDataVO = new ExcelSuccessDataVO();
+                successDataVO.setDistribution(excelData.getDistribution());
+                successDataVO.setFundName(excelData.getFundName());
+                successDataVO.setPriceDate(excelData.getPriceDate());
+                successDataVO.setDistributeType(excelData.getDistributeType());
+                successDataVOList.add(successDataVO);
+            }else{
+                ExcelFailDataVO failDataVO = toExcelFailDataVO(excelData,vo.getMsg(),dataIdx+startRow);
+                excelFailDataVOList.add(failDataVO);
+            }
+        }
+        result.put("success",successDataVOList);
+        result.put("fail",excelFailDataVOList);
+        return result;
+    }
+
+    private ExcelFailDataVO toExcelFailDataVO(DistributionExcelData excelData, String msg,Integer rowNum) {
+        ExcelFailDataVO failDataVO = new ExcelFailDataVO();
+        failDataVO.setFailReason(msg);
+        failDataVO.setRowNum(rowNum);
+        failDataVO.setDistribution(excelData.getDistribution());
+        failDataVO.setFundName(excelData.getFundName());
+        failDataVO.setPriceDate(excelData.getPriceDate());
+        failDataVO.setDistributeType(excelData.getDistributeType());
+        return failDataVO;
+    }
+
+    private List<DistributionExcelData> parseDistributionFile(File file) {
+        // 创建一个 list 存储每行的数据,即 ExcelData 对象
+        List<DistributionExcelData> list = new ArrayList<>();
+        // 直接使用 EasyExcel 的 read 方法,同时定义表头的类型,以便将列中数据映射为 ExcelData 对象
+        EasyExcel.read(file, DistributionExcelData.class, new PageReadListener<DistributionExcelData>(dataList -> {
+            // 并且每行数据,并将其 add 至 list 中
+            for (DistributionExcelData excelData : dataList) {
+                if (excelData != null) {
+                    list.add(excelData);
+                }
+            }
+        })).excelType(ExcelTypeEnum.XLSX).sheet().doRead();
+        return list;
+    }
 }