14-1-首页基本数据展示优化(实现)
大约 5 分钟
14-1-首页基本数据展示优化(实现)
任务14-更高的要求 🚀
- 对基本信息部分的数据进行代码优化,这部分的代码要先确定,然后提出自己的优化方案
- 使用 并发异步编程CompletableFuture 优化首页基本数据的统计代码。
需求
需求:查询当前用户的线索,商机,和的线索和商机

提示:对于销售主管来说,主要的工作是分配线索和商机,对于销售人员来说主要是跟进线索和商机
步骤思路
提示
- 确定接口代码的位置
- 分析代码
- 确定可优化点
- 提出优化方案
- 修改代码
1️⃣ 确定接口代码位置
从前端来确定后端接口


- 调用的接口是:
/index/getBaseInfo
- 请求方式:
Get请求
- 查询对应的后端接口位置:
2️⃣ 阅读接口代码
IndexController
/**
* 首页--基础数据统计
* @param beginCreateTime
* @param endCreateTime
* @return
*/
@GetMapping("/getBaseInfo")
public AjaxResult getBaseInfo(@RequestParam("beginCreateTime") String beginCreateTime,
@RequestParam("endCreateTime") String endCreateTime){
return AjaxResult.success(reportService.getBaseInfo(beginCreateTime,endCreateTime));
}
查看对应的service层
IReportService
/**
* 首页基本数据展示
* @param beginCreateTime
* @param endCreateTime
* @return
*/
IndexBaseInfoVO getBaseInfo(String beginCreateTime, String endCreateTime);
可以看到返回的VO对象是一个叫IndexBaseInfoVO的类
package com.huike.report.domain.vo;
import lombok.Data;
/**
* 首页基本数据VO对象
*/
@Data
public class IndexBaseInfoVO {
private Integer cluesNum=0; //线索数目
private Integer businessNum=0; //商机数目
private Integer contractNum=0; //合同数目
private Double salesAmount=0.0; //销售金额
}
查看业务层代码来封装数据
ReportServiceImpl
/**
* 获取首页基本数据
* @param beginCreateTime
* @param endCreateTime
* @return
*/
@Override
public IndexBaseInfoVO getBaseInfo(String beginCreateTime, String endCreateTime) {
//1)构建一个空的结果集对象
IndexBaseInfoVO result = new IndexBaseInfoVO();
//2 封装结果集属性
//由于查询需要用到用户名 调用工具类获取用户名
String username = SecurityUtils.getUsername();
//4 封装结果集对象
//4.1 查询线索数量
result.setCluesNum(reportMpper.getCluesNum(beginCreateTime, endCreateTime, username));
//4.2 查询商机数量
result.setBusinessNum(reportMpper.getBusinessNum(beginCreateTime, endCreateTime, username));
//4.3 查询合同数量
result.setContractNum(reportMpper.getContractNum(beginCreateTime, endCreateTime, username);
//4.4 查询销售金额
result.setSalesAmount(reportMpper.getSalesAmount(beginCreateTime, endCreateTime, username));
//5 返回结果集对象
return result;
}
可以看到service层的代码,是依次查询线索数量,商机数量,合同数量,销售金额这几部分数据
对应的Mapper层的查询如下:
ReportMapper
/**=========================================基本数据========================================*/
/**
* 获取线索数量
* @param beginCreateTime 开始时间
* @param endCreateTime 结束时间
* @param username 用户名
* @return
*/
Integer getCluesNum(@Param("startTime") String beginCreateTime,
@Param("endTime") String endCreateTime,
@Param("username") String username);
/**
* 获取商机数量
* @param beginCreateTime 开始时间
* @param endCreateTime 结束时间
* @param username 用户名
* @return
*/
Integer getBusinessNum(@Param("startTime") String beginCreateTime,
@Param("endTime") String endCreateTime,
@Param("username") String username);
/**
* 获取合同数量
* @param beginCreateTime 开始时间
* @param endCreateTime 结束时间
* @param username 用户名
* @return
*/
Integer getContractNum(@Param("startTime") String beginCreateTime,
@Param("endTime") String endCreateTime,
@Param("username") String username);
/**
* 获取合同金额
* @param beginCreateTime 开始时间
* @param endCreateTime 结束时间
* @param username 用户名
* @return
*/
Double getSalesAmount(@Param("startTime") String beginCreateTime,
@Param("endTime") String endCreateTime,
@Param("username") String username);
ReportMapper.xml
<select id="getCluesNum" resultType="Integer">
SELECT COUNT(id) AS cluesNum FROM `tb_clue`
WHERE id IN (
SELECT
assign_id
FROM
`tb_assign_record`
WHERE
`create_time` BETWEEN #{startTime}
AND #{endTime}
AND `type` = 0
AND `latest` = 1
AND `user_name` = #{username}
)
AND `status` IN (1, 2)
</select>
<select id="getBusinessNum" resultType="Integer">
SELECT
COUNT(DISTINCT(id)) AS businessNum
FROM
`tb_business`
WHERE
id IN (
SELECT
assign_id
FROM
`tb_assign_record`
WHERE
`create_time` BETWEEN #{startTime}
AND #{endTime}
AND `type` = 1
AND `latest` = 1
AND `user_name` = #{username}) AND `status` IN (1,2)
</select>
<select id="getContractNum" resultType="Integer">
SELECT
COUNT(DISTINCT(id)) AS contractNum
FROM
`tb_contract`
WHERE
`create_time` BETWEEN #{startTime}
AND #{endTime}
AND create_by = #{username}
AND `status` = 4
</select>
<select id="getSalesAmount" resultType="Double">
SELECT
CAST(
IFNULL(SUM(h.`contract_order`),0) AS DECIMAL (30, 0)
) AS salesAmount
FROM
`tb_contract` h
WHERE
h.create_by = #{username}
AND DATE_FORMAT(h.create_time,'%Y-%m-%d') BETWEEN #{startTime}
AND #{endTime}
</select>
3️⃣ 分析优化的点的所在位置
接收请求参数和返回参数给前端和sql语句部分有没有问题
看起来都是为了完成基本功能的需求,感觉没有什么问题啊
⚠️现在我们要注意,我们是对代码进行性能调优,功能并不是不能用,而是要在其原有代码上进行优化
我们可以看到他整体的代码是可以正常运行的,但是他的service层的代码是一个串行执行的
因为是串行执行的当第一个代码执行完了以后,才会继续执行第二个sql语句这种串行的方式显然是不合理的,那么如何来优化呢?能否采用并行执行的方式呢?
4️⃣ 新的技术点CompletableFuture
var aFuture = CompletableFuture.supplyAsync(()->{
//.....
return xxx;
});
var bFuture = CompletableFuture.supplyAsync(()->{
//.....
return xxx;
});
var cFuture = CompletableFuture.supplyAsync(()->{
//.....
return xxx;
});
//并行处理
CompletableFuture
.allOf(aFuture,
bFuture,
cFuture)
.join();
//取值
var a= aFuture.get();
var b= bFuture.get();
var c= cFuture.get();
5️⃣ 使用CompletableFuture
按照CompletableFuture来修改代码,修改后的代码:
/**
* 获取首页基本数据
* @param beginCreateTime
* @param endCreateTime
* @return
*/
@Override
public IndexBaseInfoVO getBaseInfo(String beginCreateTime, String endCreateTime) {
//1)构建一个空的结果集对象
IndexBaseInfoVO result = new IndexBaseInfoVO();
//2 封装结果集属性
// 2.1 由于查询需要用到用户名 调用工具类获取用户名
String username = SecurityUtils.getUsername();
try {
CompletableFuture<Integer> clueNums = CompletableFuture.supplyAsync(()->{
// 2.2 开始查询第一个属性 线索数量
return reportMpper.getCluesNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Integer> bussinessNum = CompletableFuture.supplyAsync(()->{
// 2.3 开始查询第一个属性 商机数量
return reportMpper.getBusinessNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Integer> contractNum = CompletableFuture.supplyAsync(()->{
// 2.4 开始查询第一个属性 合同数量
return reportMpper.getContractNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Double> saleAmount = CompletableFuture.supplyAsync(()->{
// 2.5 开始查询第一个属性 销售金额数量
return reportMpper.getSalesAmount(beginCreateTime, endCreateTime, username);
});
//3 join等待所有线程全部执行完成
CompletableFuture
.allOf(clueNums,
bussinessNum,
contractNum,
saleAmount)
.join();
//4 封装结果集对象
result.setCluesNum(clueNums.get());
result.setBusinessNum(bussinessNum.get());
result.setContractNum(contractNum.get());
result.setSalesAmount(saleAmount.get());
}catch (Exception e) {
e.printStackTrace();
return null;
}
//5 返回结果集对象
return result;
}