1. 服务概述  150

定时任务处理利息_java

定时任务处理利息_apache_02

2. corn表达式  151

资料在E:\java学习\盈利宝\资料\资料\08-spring-task

格式: 

Seconds Minutes Hours DayofMonth Month DayofWeek Year 或 

Seconds Minutes Hours DayofMonth Month DayofWeek 

Seconds:可出现 ,-  *  /  四个字符,有效范围为0-59的整数    

Minutes:可出现 ,-  *  /  四个字符,有效范围为0-59的整数    

Hours:可出现 ,-  *  /  四个字符,有效范围为0-23的整数    

DayofMonth:可出现 ,-  *  / ? L W C 八个字符,有效范围为1-31的整数     

Month:可出现 ,-  *  /  四个字符,有效范围为1-12的整数或JAN-DEC

DayofWeek:可出现  ,-  *  / ? L C # 四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推    

Year:可出现 ,-  *  /  四个字符,有效范围为1970-2099年   


2.1 含义   152

每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:


(1) * :表示匹配该域的任意值,假如在Minutes域使用*,即表示每分钟都会触发事件。    


(2) ? :只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。

例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13  13 15 20 * ?,其中最后一位只能用?,而不能使用*,

如果使用*表示不管星期几都会触发,实际上并不是这样。    


(3)  -  :表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次    


(4)   /  :表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.    


(5)  ,   :表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。    


(6)   L  :表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。    


(7)  W  :表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。

例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;

如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份    

(8)  LW  :这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。    

(9)  #  :用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。   

2.2 举几个例子:  152

0 0  2  1 *  ? *  表示在每月的1日的凌晨2点调度任务     

0 15 10 ? *  MON-FRI 表示周一到周五每天上午10:15执行作业             

0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作业   

3. 创建一个新的模块  153

定时任务处理利息_apache_03

定时任务处理利息_apache_04

定时任务处理利息_spring_05

直接放在course目录下即可

4. 处理定时任务  153

4.1 举例  153

启动类

定时任务处理利息_apache_06

TaskManager

micr-task

//每天的16点59分30秒执行  153
    @Scheduled(cron = "30 59 16 * * ?")
    public void testCron(){
        System.out.println("执行了定时任务的方法:"+ new Date());
    }

定时任务处理利息_java_07

//每天的每小时得每分钟每隔5秒执行一次  153
    @Scheduled(cron = "*/5 * * * * ?")
    public void testCron2(){
        System.out.println("执行了定时任务的方法:"+ new Date());
    }

定时任务处理利息_apache_08

5. 项目环境搭建  153

5.1 pom.xml

micr-task

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<!--自己的父项目-->
	<parent>
		<groupId>com.bjpowernode</groupId>
		<artifactId>micr-parent</artifactId>
		<version>1.0.0</version>
		<relativePath/>
	</parent>

	<groupId>com.bjpowernode</groupId>
	<artifactId>micr-task</artifactId>
	<version>1.0.0</version>
	
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>

		<!--dubbo公共项目依赖  153-->
		<dependency>
			<groupId>com.bjpowernode</groupId>
			<artifactId>micr-api</artifactId>
			<version>1.0.0</version>
		</dependency>

		<!--dubbo起步依赖-->
		<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
		</dependency>

		<!--zookeeper起步依赖-->
		<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo-dependencies-zookeeper</artifactId>
			<type>pom</type>
		</dependency>

		<dependency>
			<groupId>com.bjpowernode</groupId>
			<artifactId>micr-common</artifactId>
			<version>1.0.0</version>
			<scope>compile</scope>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

5.2 配置文件  153

micr-task

application.yml

spring:
  application:
    name: micr-task
#  153
dubbo:
  scan:
    base-packages: com.bjpowernode.task
  registry:
    address: zookeeper://localhost:2181
  consumer:
    timeout: 50000
    check: false
    retries: 0

5.3 启动类  153

micr-task

定时任务处理利息_spring_09

package com.bjpowernode;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableDubbo  //启动dubbo   153
@EnableScheduling  //启用定时任务  153
@SpringBootApplication
public class MicrTaskApplication {

	public static void main(String[] args) {
		SpringApplication.run(MicrTaskApplication.class, args);
	}

}

6. 计算时间测试类  155

MyTest

micr-common

package com.bjpowernode;

import org.apache.commons.lang3.time.DateUtils;
import org.junit.Test;

import java.util.Calendar;
import java.util.Date;

//测试计算时间  155
public class MyTest {
    @Test
    public void test01(){
        Date cur = new Date();
        System.out.println("cur="+cur);//当前时间

        //开始 14 : 00 :00:00
        Date yes = DateUtils.addDays(cur, -1); //当前时间减一天
        System.out.println("yes="+yes); //昨天时间
        
        //需要零点时间
        //开始时间
        Date truncate = DateUtils.truncate(DateUtils.addDays(cur, -1), Calendar.DATE);
        System.out.println("truncate = " + truncate);

        //truncate截断时间
        //比如 cur=Tue Aug 01 17:46:28 CST 2023
        //截断为cur=Tue Aug 01 00:00:00 CST 2023
        //截止时间
        Date end = DateUtils.truncate(cur, Calendar.DATE);
        System.out.println("end = " + end);

    }
}

定时任务处理利息_apache_10

7. 实现产生收益计划功能  155

定时任务处理利息_spring_11

7.1 业务接口  155

IncomeService

micr-api

package com.bjpowernode.api.service;

//关于收益的业务接口  155
public interface IncomeService {
    //收益计划   155
    void generateIncomePlan();
}

7.2 业务接口实现类  155

7.2.1 定义常量类  157

micr-common

定时任务处理利息_spring_12

7.2.2 再mapper中定义放方法

micr-dataservice

满标的理财产品列表  156

ProductInfoMapper

//满标的理财产品列表  156
    List<ProductInfo> selectFullTimeProducts(@Param("beginTime") Date beginTime, @Param("endTime") Date endTime);
 查询某个产品的投资记录  156

BidInfoMapper

//查询某个产品的投资记录  156
    List<BidInfo> selectByProdId(@Param("productId") Integer productId);
更新产品状态   157

ProductInfoMapper

//更新产品状态   157
    int updateStatus(@Param("id") Integer id, @Param("newStatus") int newStatus);

7.2.3 编写sql

micr-dataservice

满标的理财产品列表  156

ProductInfoMapper.xml

<!--满标的理财产品列表  156-->
  <select id="selectFullTimeProducts" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    FROM
    b_product_info
    WHERE
    product_status = 1
    AND product_full_time >= #{beginTime}  AND product_full_time < #{endTime}
    order by id
  </select>
 查询某个产品的投资记录  156

BidInfoMapper.xml

<!--  查询某个产品的投资记录  156-->
  <select id="selectByProdId" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    FROM
    b_bid_info
    WHERE
    prod_id = #{productId}  AND bid_status = 1
    ORDER BY  id
  </select>
更新产品状态   157

ProductInfoMapper.xml

<!--更新产品状态   157-->
  <update id="updateStatus">
    update  b_product_info  set  product_status = #{newStatus} where id = #{id}
  </update>

7.2.4 实现类  IncomeServiceImpl155

我来详细解释一下这个类的功能,目的就是产生收益计划,将数据存入收益表中,它是会对过去一天且满标的产品产生收益计划,不会影响其他未满标或者已经满标得产品有影响,他也不会因为产品的类型不同而重复对此产品产生收益,因为它只在每天凌晨一点执行一次

micr-dataservice

package com.bjpowernode.dataservice.service;

import com.bjpowernode.api.model.BidInfo;
import com.bjpowernode.api.model.IncomeRecord;
import com.bjpowernode.api.model.ProductInfo;
import com.bjpowernode.api.service.IncomeService;
import com.bjpowernode.common.constants.YLBConstant;
import com.bjpowernode.dataservice.mapper.BidInfoMapper;
import com.bjpowernode.dataservice.mapper.IncomeRecordMapper;
import com.bjpowernode.dataservice.mapper.ProductInfoMapper;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

//收益业务实现类  155
@DubboService(interfaceClass = IncomeService.class,version = "1.0")
public class IncomeServiceImpl implements IncomeService {

    @Resource
    private ProductInfoMapper productInfoMapper;

    @Resource
    private BidInfoMapper bidInfoMapper;

    @Resource
    private IncomeRecordMapper incomeMapper; //157

    /*收益计划   156 */
    @Transactional(rollbackFor = Exception.class) //158
    @Override
    public synchronized void generateIncomePlan() {

        //1.获取要处理的理财产品记录(满标的)  156
        Date currentDate = new Date();
        //开始时间
        Date beginTime = DateUtils.truncate(DateUtils.addDays(currentDate, -1), Calendar.DATE);
        //截止时间
        Date endTime = DateUtils.truncate(currentDate, Calendar.DATE);
        //获取要处理的理财产品记录(满标的)  156
        List<ProductInfo> productInfoList = productInfoMapper.selectFullTimeProducts(beginTime,endTime);

        //2.查询每个理财产品的多个投资记录   156
        int rows  = 0 ;

        BigDecimal income =  null;//利息
        BigDecimal dayRate = null;//日利率
        BigDecimal cycle  = null; //周期

        Date incomeDate  = null;//到期时间

        for(ProductInfo product:productInfoList){
            
            //日利率   157
            //new BigDecimal("360"),10, RoundingMode.HALF_UP(计算出来时百分比)  除以360天 保留10为小数  RoundingMode.HALF_UP四舍五入
            //继续除 变成小数 .divide(new BigDecimal("100"),10, RoundingMode.HALF_UP);
            dayRate = product.getRate().divide(new BigDecimal("360"),10, RoundingMode.HALF_UP)
                    .divide(new BigDecimal("100"),10, RoundingMode.HALF_UP);

            //产品类型不同,周期不同 天 ,月
            //新手包按天算
            if( product.getProductType() == YLBConstant.PRODUCT_TYPE_XINSHOUBAO){  //天为单位
                //周期  157
                cycle = new BigDecimal(product.getCycle());
                //到期时间   157
                //到期时间=产品满标时间开始+周期   举例14号满标要从15号开始算
                incomeDate = DateUtils.addDays(product.getProductFullTime(),(1+product.getCycle()));
            } else {//其他的按月算
                cycle = new BigDecimal(product.getCycle() * 30);
                incomeDate = DateUtils.addDays(product.getProductFullTime(),(1+ product.getCycle() * 30 ));
            }

            //查询某个产品的投资记录  156
            //我们的产品每一条投资记录独立且都会产生利息
            List<BidInfo> bidList  = bidInfoMapper.selectByProdId(product.getId());
            //3.计算每笔投资的 利息 和 到期时间   157
            for(BidInfo bid : bidList){
                //利息 = 本金  * 周期 * 利率
                income = bid.getBidMoney().multiply(cycle).multiply(dayRate);
                // 创建收益记录
                IncomeRecord incomeRecord = new IncomeRecord();
                incomeRecord.setBidId(bid.getId());//投资id
                incomeRecord.setBidMoney(bid.getBidMoney());//投资金额
                incomeRecord.setIncomeDate(incomeDate);//到期时间
                incomeRecord.setIncomeStatus(YLBConstant.INCOME_STATUS_PLAN);//收益状态
                incomeRecord.setProdId(product.getId());//产品id
                incomeRecord.setIncomeMoney(income);//利息
                incomeRecord.setUid(bid.getUid());//投资人id
                //将数据添加进收益表   157
                incomeMapper.insertSelective(incomeRecord);
            }


            //更新产品的状态   157
            rows = productInfoMapper.updateStatus(product.getId(),YLBConstant.PRODUCT_STATUS_PLAN);
            if(rows < 1 ){
                throw new RuntimeException("生成收益计划,更新产品状态为2失败");
            }
        }
    }
}

7.3 先做个小调试   158

TaskManager

micr-task

//测试生成收益计划   158
    @DubboReference(interfaceClass = IncomeService.class, version = "1.0")
    private IncomeService incomeService;

    @Scheduled(cron = "0 0 1 * * ?")//每天凌晨1点
    public void invokeGenerateIncomePlan() {
        incomeService.generateIncomePlan();
    }

改造一下micr-task得启动类方便我们测试  158

先改个名

定时任务处理利息_java_13

定时任务处理利息_apache_14

package com.bjpowernode;

import com.bjpowernode.task.TaskManager;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableDubbo  //启动dubbo   153
@EnableScheduling  //启用定时任务  153
@SpringBootApplication
public class MicrTaskApplication {

	public static void main(String[] args) {
		//测试收益计划  158
		ApplicationContext ctx = SpringApplication.run(MicrTaskApplication.class, args);
		TaskManager tm = (TaskManager) ctx.getBean("taskManager");
		tm.invokeGenerateIncomePlan();
	}

}

debug模式启动micr-dataservice

正常模式启动micr-task

修改新手宝的数据方便我们测试

定时任务处理利息_java_15

新手宝的原始数据

定时任务处理利息_apache_16

执行程序

查看收益表,成功

定时任务处理利息_spring_17

产品表,状态为2代表开始产生收益

定时任务处理利息_spring_18

8. 收益返还   159

8.1 业务接口  159

IncomeService

micr-api

//收益返还   159
    void generateIncomeBack();

8.2 业务接口实现类  159

8.2.1 在mapper中定义方法  159

到期收益记录   159

IncomeRecordMapper

micr-dataservice

//到期收益记录   159
    List<IncomeRecord> selectExpiredIncome(@Param("expiredDate") Date expiredDate);

收益返还,更新资金   160

FinanceAccountMapper

//收益返还,更新资金   160
    int ujapdateAvailableMoneyByIncomeBack(@Param("uid") Integer uid, 
                                         @Param("bidMoney") BigDecimal bidMoney, 
                                         @Param("incomeMoney") BigDecimal incomeMoney);

8.2.2 编写sql  159

到期收益记录   159

IncomeRecordMapper.xml

micr-dataservice

<!--到期收益记录   159-->
  <select id="selectExpiredIncome" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    FROM
    b_income_record
    WHERE
    income_status = 0
    AND income_date = #{expiredDate}
  </select>

收益返还,更新资金   160

FinanceAccountMapper.xml

<!--收益返还,更新资金  160-->
  <update id="updateAvailableMoneyByIncomeBack">
    update  u_finance_account set  available_money = available_money + #{bidMoney} + #{incomeMoney}
    where uid = #{uid}
  </update>

8.2.3 实现类   159

IncomeServiceImpl

micr-dataservice

//实现收益返还  159
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized void generateIncomeBack() {
        //1.获取要处理的到期的收益记录  159
        Date curDate = new Date();
        Date expiredDate = DateUtils.truncate(DateUtils.addDays(curDate, -1),Calendar.DATE);
        System.out.println("expiredDate="+expiredDate);
        List<IncomeRecord> incomeRecordList = incomeMapper.selectExpiredIncome(expiredDate);

        int rows  = 0;
        //2.把每个收益,进行返还, 本金 + 利息   160
        for(IncomeRecord ir : incomeRecordList){
            rows  = accountMapper.updateAvailableMoneyByIncomeBack(ir.getUid(),ir.getBidMoney(),ir.getIncomeMoney());
            if( rows < 1 ){
                throw new RuntimeException("收益返还,更新账号资金失败");
            }

            //3.更新收益记录的状态为 1代表收益返还   160
            ir.setIncomeStatus(YLBConstant.INCOME_STATUS_BACK);
            rows = incomeMapper.updateByPrimaryKey(ir);
            if( rows <1) {
                throw new RuntimeException("收益返还,更新收益记录的状态失败");
            }
        }

    }

8.3 做个小测试   160

TaskManager

micr-task

//测试生成收益返还  160
    @Scheduled(cron = "0 0 2 * * ?")//每天凌晨2点
    public void invokeGenerateIncomeBack() {
        incomeService.generateIncomeBack();
    }

改造一下micr-task得启动类方便我们测试  160

定时任务处理利息_apache_19

debug模式启动micr-dataservice

正常模式启动micr-task

修改几条记录方便我们测试返还利息

定时任务处理利息_apache_20

状态变为1 表示已返还

定时任务处理利息_apache_21

资金表加了金额,成功

定时任务处理利息_java_22