ソースを参照

feat: 净值振幅检测任务开发

chenjianhua 4 ヶ月 前
コミット
9934ea592e

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

@@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Map;
 
 @Mapper
 public interface EmailFundNavMapper {
@@ -43,4 +44,12 @@ public interface EmailFundNavMapper {
     EmailFundNavDO selectFundNavById(@Param("id")Integer id);
 
     void updateEmailNav(EmailFundNavDO emailFundNavDO);
+
+    Integer getNavAmplitudeErrorCount();
+
+    List<EmailFundNavDO> getNavAmplitudeErrorList();
+
+    Map<String, Long> selectNavAmplitudeErrorMaxMinId();
+
+    List<EmailFundNavDO> getNavAmplitudeErrorListById(@Param("startIdx") long startIdx, @Param("startIdx")long endIdx);
 }

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

@@ -32,4 +32,6 @@ public interface NavMapper {
     Long countNavTotal();
 
     NavDO queryNavByFundIdDate(@Param("fundId")String fundId, @Param("priceDate")String priceDate);
+
+    NavDO queryLastNavByFundIdDate(@Param("fundId")String fundId, @Param("priceDate")String priceDate);
 }

+ 52 - 2
service-base/src/main/resources/mapper/daq/EmailFundNavMapper.xml

@@ -108,7 +108,7 @@
                 on nav.fund_id=info.fund_id and info.isvalid=1
         WHERE nav.isvalid = 1  and file.isvalid=1 and parse.isvalid=1
         <if test="fundName != null and fundName !=''">
-            and nav.fund_name like concat('%',#{fundName},'%')
+            and (nav.fund_name like concat('%',#{fundName},'%') or nav.register_number like concat('%',#{fundName},'%'))
         </if>
         <if test="priceStartDate != null and priceStartDate !=''">
             and nav.price_date >= #{priceStartDate}
@@ -172,7 +172,7 @@
         on nav.fund_id=info.fund_id and info.isvalid=1
         WHERE nav.isvalid = 1  and file.isvalid=1 and parse.isvalid=1
         <if test="fundName != null and fundName !=''">
-            and nav.fund_name like concat('%',#{fundName},'%')
+            and (nav.fund_name like concat('%',#{fundName},'%') or nav.register_number like concat('%',#{fundName},'%'))
         </if>
         <if test="priceStartDate != null and priceStartDate !=''">
             and nav.price_date >= #{priceStartDate}
@@ -337,6 +337,56 @@
         where nav.isvalid =1
           and nav.id=#{id}
     </select>
+    <select id="getNavAmplitudeErrorCount" resultType="java.lang.Integer">
+        select count(*) from email_fund_nav where exception_status=7 and isvalid=1;
+    </select>
+    <select id="getNavAmplitudeErrorList" resultMap="BaseResultMap">
+        select nav.id,
+               nav.file_id,
+               nav.fund_id,
+               nav.fund_name,
+               nav.register_number,
+               nav.price_date,
+               nav.nav,
+               nav.cumulative_nav_withdrawal,
+               nav.exception_status,
+               nav.is_stored,
+               nav.isvalid,
+               nav.creatorid,
+               nav.updaterid,
+               nav.updatetime,
+               nav.createtime,
+               nav.remark
+        from email_fund_nav nav
+        where nav.isvalid =1 and nav.exception_status=7
+        order by nav.price_date
+    </select>
+    <select id="selectNavAmplitudeErrorMaxMinId" resultType="java.util.Map">
+        select max(nav.id) as maxId,min(nav.id) as minId from email_fund_nav nav
+        where nav.isvalid =1 and nav.exception_status=7
+    </select>
+    <select id="getNavAmplitudeErrorListById" resultType="com.simuwang.base.pojo.dos.EmailFundNavDO">
+        select nav.id,
+               nav.file_id,
+               nav.fund_id,
+               nav.fund_name,
+               nav.register_number,
+               nav.price_date,
+               nav.nav,
+               nav.cumulative_nav_withdrawal,
+               nav.exception_status,
+               nav.is_stored,
+               nav.isvalid,
+               nav.creatorid,
+               nav.updaterid,
+               nav.updatetime,
+               nav.createtime,
+               nav.remark
+        from email_fund_nav nav
+        where nav.isvalid =1 and nav.exception_status=7
+        and nav.id <![CDATA[ >= ]]> #{startIdx} and nav.id <![CDATA[ <= ]]> #{endIdx}
+        order by nav.price_date
+    </select>
 
 
 </mapper>

+ 1 - 0
service-base/src/main/resources/mapper/daq/EmailTemplateMappingMapper.xml

@@ -68,6 +68,7 @@
         <if test="status != null">
             and mapping.status=#{status}
         </if>
+        order by mapping.updatetime desc
         limit #{offset},#{pageSize}
     </select>
     <select id="countTemplateSettingList" resultType="java.lang.Long">

+ 10 - 1
service-base/src/main/resources/mapper/daq/NavMapper.xml

@@ -108,10 +108,19 @@
                isvalid, creatorid, createtime, updaterid, updatetime
         from nav
         where isvalid = 1
-          and fund_id = #{fundId} and price_date <![CDATA[ < ]]> #{priceDate}
+          and fund_id = #{fundId} and price_date = #{priceDate}
         order by price_date desc
         limit 1
     </select>
+    <select id="queryLastNavByFundIdDate" resultType="com.simuwang.base.pojo.dos.NavDO">
+        SELECT id, fund_id,price_date,nav,cumulative_nav,cumulative_nav_withdrawal,
+               isvalid, creatorid, createtime, updaterid, updatetime
+        from nav
+        where isvalid = 1
+          and fund_id = #{fundId} and price_date <![CDATA[ < ]]> #{priceDate}
+        order by price_date desc
+            limit 1
+    </select>
 
 
 </mapper>

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

@@ -822,7 +822,7 @@ public class EmailParseService {
         String fundId = navDO.getFundId();
         String priceDate = DateUtils.format(navDO.getPriceDate(), DateUtils.YYYY_MM_DD);
         //上一期单位净值
-        NavDO preNavDO = navMapper.queryNavByFundIdDate(fundId,priceDate);
+        NavDO preNavDO = navMapper.queryLastNavByFundIdDate(fundId,priceDate);
         if(preNavDO == null){
             return BigDecimal.valueOf(0);
         }

+ 31 - 7
service-manage/src/main/java/com/simuwang/manage/init/CompleteScheduleConfig.java

@@ -3,6 +3,7 @@ package com.simuwang.manage.init;
 import com.simuwang.base.common.util.DateUtils;
 import com.simuwang.base.mapper.daq.system.SysConfigMapper;
 import com.simuwang.manage.task.FundDeletionTask;
+import com.simuwang.manage.task.NavAmplitudeTask;
 import com.simuwang.manage.task.SendCompanyEmailTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,6 +38,9 @@ public class CompleteScheduleConfig implements SchedulingConfigurer {
 
     @Autowired
     private SysConfigMapper sysConfigMapper;
+
+    @Autowired
+    private NavAmplitudeTask navAmplitudeTask;
     /**
      * 执行定时任务.
      */
@@ -52,38 +56,58 @@ public class CompleteScheduleConfig implements SchedulingConfigurer {
                 () -> {
                     try {
                         logger.info("缺失统计任务开始"+ DateUtils.getTime());
-                        fundDeletionTask.computeDeletion();//定时拉取数据
+                        fundDeletionTask.computeDeletion();
                         logger.info("缺失统计任务结束"+ DateUtils.getTime());
                     } catch (Exception e) {
                         logger.error("缺失统计任务异常========="+e.getMessage()+"----" +DateUtils.getTime(),e);
                     }
                 },
                 //2.设置执行周期(Trigger)
-                loadFtpFileTriggerContext -> {
+                triggerContext -> {
                     String cron = sysConfigMapper.selectConfigByKey("deletion_cron");
                     logger.info("缺失统计定时任务执行时间:"+cron);
                     //2.2 返回执行周期(Date)
-                    return new CronTrigger(cron).nextExecutionTime(loadFtpFileTriggerContext).toInstant();
+                    return new CronTrigger(cron).nextExecutionTime(triggerContext).toInstant();
                 }
         );
-        //增量补数
+        //发送基金缺失预警
         taskRegistrar.addTriggerTask(
                 //1.添加任务内容(Runnable)
                 () -> {
                     try {
                         logger.info("给管理人发送基金缺失预警邮件定时任务开始"+DateUtils.getTime());
-                        sendCompanyEmailTask.sendEmail();//定时拉取数据
+                        sendCompanyEmailTask.sendEmail();
                         logger.info("给管理人发送基金缺失预警邮件定时任务结束========="+ DateUtils.getTime());
                     } catch (Exception e) {
                         logger.error("给管理人发送基金缺失预警邮件定时任务异常========="+e.getMessage()+ DateUtils.getTime(),e);
                     }
                 },
                 //2.设置执行周期(Trigger)
-                loadFtpFileUpdateTriggerContext -> {
+                triggerContext -> {
                     String cron = sysConfigMapper.selectConfigByKey("send_company_email");
                     logger.info("给管理人发送基金缺失预警邮件定时任务轮训时间:"+cron);
                     //2.2 返回执行周期(Date)
-                    return new CronTrigger(cron).nextExecutionTime(loadFtpFileUpdateTriggerContext).toInstant();
+                    return new CronTrigger(cron).nextExecutionTime(triggerContext).toInstant();
+                }
+        );
+        //发送基金缺失预警
+        taskRegistrar.addTriggerTask(
+                //1.添加任务内容(Runnable)
+                () -> {
+                    try {
+                        logger.info("计算净值振幅任务开始"+DateUtils.getTime());
+                        navAmplitudeTask.navAmplitude();
+                        logger.info("计算净值振幅任务结束========="+ DateUtils.getTime());
+                    } catch (Exception e) {
+                        logger.error("计算净值振幅任务异常========="+e.getMessage()+ DateUtils.getTime(),e);
+                    }
+                },
+                //2.设置执行周期(Trigger)
+                triggerContext -> {
+                    String cron = sysConfigMapper.selectConfigByKey("nav_amplitude_time");
+                    logger.info("计算净值振幅任务定时任务轮训时间:"+cron);
+                    //2.2 返回执行周期(Date)
+                    return new CronTrigger(cron).nextExecutionTime(triggerContext).toInstant();
                 }
         );
     }

+ 15 - 0
service-manage/src/main/java/com/simuwang/manage/service/EmailFundNavService.java

@@ -1,5 +1,10 @@
 package com.simuwang.manage.service;
 
+import com.simuwang.base.pojo.dos.EmailFundNavDO;
+
+import java.util.List;
+import java.util.Map;
+
 /**
  * FileName: EmailFileNavService
  * Author:   chenjianhua
@@ -8,4 +13,14 @@ package com.simuwang.manage.service;
  */
 public interface EmailFundNavService {
     void reparseFileNav(String sourceFundName,String registerNumber, String targetFundId,String sourceFundId);
+
+    Integer getNavAmplitudeErrorCount();
+
+    List<EmailFundNavDO> getNavAmplitudeErrorList();
+
+    void update(EmailFundNavDO fundNavDO);
+
+    Map<String, Long> selectNavAmplitudeErrorMaxMinId();
+
+    List<EmailFundNavDO> getNavAmplitudeErrorListById(long startIdx, long endIdx);
 }

+ 25 - 0
service-manage/src/main/java/com/simuwang/manage/service/impl/EmailFundNavServiceImpl.java

@@ -72,4 +72,29 @@ public class EmailFundNavServiceImpl implements EmailFundNavService {
             }
         }
     }
+
+    @Override
+    public Integer getNavAmplitudeErrorCount() {
+        return emailFundNavMapper.getNavAmplitudeErrorCount();
+    }
+
+    @Override
+    public List<EmailFundNavDO> getNavAmplitudeErrorList() {
+        return emailFundNavMapper.getNavAmplitudeErrorList();
+    }
+
+    @Override
+    public void update(EmailFundNavDO fundNavDO) {
+        emailFundNavMapper.update(fundNavDO);
+    }
+
+    @Override
+    public Map<String, Long> selectNavAmplitudeErrorMaxMinId() {
+        return emailFundNavMapper.selectNavAmplitudeErrorMaxMinId();
+    }
+
+    @Override
+    public List<EmailFundNavDO> getNavAmplitudeErrorListById(long startIdx, long endIdx) {
+        return emailFundNavMapper.getNavAmplitudeErrorListById(startIdx,endIdx);
+    }
 }

+ 116 - 0
service-manage/src/main/java/com/simuwang/manage/task/NavAmplitudeTask.java

@@ -0,0 +1,116 @@
+package com.simuwang.manage.task;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.simuwang.base.common.conts.NavParseStatusConst;
+import com.simuwang.base.common.util.DateUtils;
+import com.simuwang.base.mapper.daq.NavMapper;
+import com.simuwang.base.pojo.dos.EmailFundNavDO;
+import com.simuwang.base.pojo.dos.NavDO;
+import com.simuwang.manage.service.EmailFundNavService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * FileName: NavAmplitudeTask
+ * Author:   chenjianhua
+ * Date:     2024/12/9 15:46
+ * Description: ${DESCRIPTION}
+ */
+@Component
+public class NavAmplitudeTask {
+    private static final Logger log = LoggerFactory.getLogger(NavAmplitudeTask.class);
+    @Autowired
+    private EmailFundNavService emailFundNavService;
+    @Autowired
+    private NavMapper navMapper;
+    private static final Integer maxNum = 100000;
+    public void navAmplitude(){
+        //获取净值振幅错误的数据总量
+        Integer total = emailFundNavService.getNavAmplitudeErrorCount();
+        if(total==0){
+            return;
+        }
+        //不超过10W数据,就一次查询处理,超过就分批次处理
+        if(total <= maxNum){
+            List<EmailFundNavDO> emailFundNavDOList = emailFundNavService.getNavAmplitudeErrorList();
+            emailFundNavDOList.stream().forEach(e -> emailNavAmplitude(e));
+        }else{
+            //如果大于10W,分批次处理
+            Map<String,Long> idMap = emailFundNavService.selectNavAmplitudeErrorMaxMinId();
+            Long minId = idMap.get("minId");
+            Long maxId = idMap.get("maxId");
+            long times = (maxId-minId)%maxNum==0?(maxId-minId)/maxNum:(maxId-minId)/maxNum+1;
+            long startIdx = minId;
+            for(int idx=1;idx <= times;idx++){
+                long endIdx = startIdx+idx*maxNum;
+                if(endIdx >= maxId){
+                    endIdx = maxId;
+                }
+                List<EmailFundNavDO> emailFundNavDOList = emailFundNavService.getNavAmplitudeErrorListById(startIdx,endIdx);
+                emailFundNavDOList.stream().forEach(e -> emailNavAmplitude(e));
+                startIdx = endIdx;
+            }
+        }
+    }
+
+    private void emailNavAmplitude(EmailFundNavDO emailFundNavDO) {
+        try{
+            String fundId = emailFundNavDO.getFundId();
+            String priceDate = DateUtils.format(emailFundNavDO.getPriceDate(), DateUtils.YYYY_MM_DD);
+            //上一期单位净值
+            NavDO preNavDO = navMapper.queryLastNavByFundIdDate(fundId,priceDate);
+            if(preNavDO == null){
+                saveEmailFundNav(emailFundNavDO,NavParseStatusConst.SUCCESS,1);
+                saveEmailFundNavToNav(emailFundNavDO);
+                return;
+            }
+            //|(当期-上期)/ 上期 |
+            BigDecimal amplitudeRate = emailFundNavDO.getNav().subtract(preNavDO.getNav()).divide(preNavDO.getNav(),4, BigDecimal.ROUND_HALF_UP).abs();
+            if(amplitudeRate.compareTo(BigDecimal.valueOf(1)) >= 0){
+                //振幅超过100%不可以入库
+                return;
+            }else{
+                //振幅超过20%可以入库,但要给出提示信息
+                if(amplitudeRate.compareTo(BigDecimal.valueOf(0.2)) >= 0){
+                    saveEmailFundNav(emailFundNavDO,NavParseStatusConst.AMPLITUDE_EXCEPTION,1);
+                }else{
+                    saveEmailFundNav(emailFundNavDO,NavParseStatusConst.SUCCESS,1);
+                }
+                saveEmailFundNavToNav(emailFundNavDO);
+            }
+        }catch (Exception e){
+            log.error(e.getMessage(),e);
+        }
+    }
+
+    private void saveEmailFundNavToNav(EmailFundNavDO emailFundNavDO) {
+        String fundId = emailFundNavDO.getFundId();
+        String priceDate = DateUtils.format(emailFundNavDO.getPriceDate(), DateUtils.YYYY_MM_DD);
+        NavDO oldNavDO = navMapper.queryNavByFundIdDate(fundId,priceDate);
+        if(oldNavDO != null){
+            oldNavDO.setNav(emailFundNavDO.getNav());
+            oldNavDO.setCumulativeNavWithdrawal(emailFundNavDO.getCumulativeNavWithdrawal());
+            oldNavDO.setUpdateTime(new Date());
+            navMapper.updateNav(oldNavDO);
+        }else{
+            NavDO insertDO = BeanUtil.copyProperties(emailFundNavDO, NavDO.class);
+            insertDO.setCreateTime(new Date());
+            insertDO.setUpdateTime(new Date());
+            navMapper.saveNav(insertDO);
+        }
+    }
+
+    private void saveEmailFundNav(EmailFundNavDO insertDO,Integer exceptionStatus,Integer isStored) {
+        insertDO.setExceptionStatus(exceptionStatus);
+        insertDO.setIsStored(isStored);
+        insertDO.setUpdateTime(new Date());
+        emailFundNavService.update(insertDO);
+    }
+}