1. 服务概述 150
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
直接放在course目录下即可
4. 处理定时任务 153
4.1 举例 153
启动类
TaskManager
micr-task
//每天的16点59分30秒执行 153
@Scheduled(cron = "30 59 16 * * ?")
public void testCron(){
System.out.println("执行了定时任务的方法:"+ new Date());
}
//每天的每小时得每分钟每隔5秒执行一次 153
@Scheduled(cron = "*/5 * * * * ?")
public void testCron2(){
System.out.println("执行了定时任务的方法:"+ new Date());
}
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
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);
}
}
7. 实现产生收益计划功能 155
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
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
先改个名
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
修改新手宝的数据方便我们测试
新手宝的原始数据
执行程序
查看收益表,成功
产品表,状态为2代表开始产生收益
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
debug模式启动micr-dataservice
正常模式启动micr-task
修改几条记录方便我们测试返还利息
状态变为1 表示已返还
资金表加了金额,成功