知识目标
-
了解ECharts,能够说出ECharts的作用
-
掌握会员数量统计的实现,能够使用Echarts绘制会员数量统计图形报表
-
掌握套餐预约占比统计的实现,能够使用Echarts绘制套餐预约占比统计图形报表
-
掌握运营数据报表的实现
通过对数据进行统计分析,可以更直观地反映出健康管理机构的运营情况,有利于管理者了解用户需求,完善体检产品,从而提高服务质量。医疗健康管理端的统计分析包括会员数量统计、套餐预约占比统计和运营数据统计。接下来,本模块将对管理端的统计分析进行详细讲解。
9-1 会员数量统计
会员信息是医疗健康的核心数据之一,其中会员数量的变化趋势能够反映出医疗健康的部分运营情况。本任务需要在report_member.html 页面通过折线图展示最近一年内每月的会员数量。
ECharts下载
ECharts(Enterprise Charts,商业级数据图表)是一个基于 JavaScript 的开源可视化图表库,它最初由百度团队开发,于 2018 年初捐赠给 Apache 基金会,成为 ASF 孵化级项目。ECharts 可以流畅地运行在 PC 和移动设备上,兼容当前绝大部分浏览器,例如Chrome、Firefox、Safari 等,它的底层依赖轻量级的矢量图形库 ZRender,可提供直观、交互丰富、可高度个性化定制的数据可视化图表。
在 ECharts 官网首页,单击菜单栏“下载”下拉列表框中的“下载”按钮,进入 ECharts 下载页面。
从 GitHub 下载 ECharts时,ECharts 提供了多个扩展名为.js 的文件。其中,echarts.common.js 包含常见的图表和组件;echarts.min.js 包含最常用的图表和组件;echarts.js 包含所有的图表和组件,是最完整的,建议初学者选择 echarts.js 文件下载。
将下载的 echarts.js添加到 backend的plugins/echarts 目录下。
ECharts官方示例
在ECharts官网中提供了很多的示例,可以通过这些官方示例查看图表的使用方法和图表效果。在ECharts 官网首页,单击“示例”按钮,进入ECharts图表示例页面。
ECharts 图表示例页面左侧导航栏展示了多种图表分类,用户可以根据类型进行查看,每种类型的图表有多种展示方式,如果想查看某一图表的 JavaScript 代码,直接单击对应图表即可查看。
可以通过编辑代码中的数据,测试折线图的图表效果。例如,将 series 中 data 数组的值全部修改为 100,效果如下。
ECharts快速上手
(1)引入ECharts
创建一个名称为echarts_example的HTML文件,在文件中引入echarts.js,并定义一个DOM容器。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="D:/echarts/echarts.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
</body>
</html>
(2)绘制图表
使用折线图展示最近一周内最高气温的变化情况。 在echarts_example.html页面中绘制折线图图表,基于DOM使用echarts.init( )方法初始化 echarts 实例,并通过 setOption( )方法生成简单的折线图。
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
var option = {
title: { text: 'ECharts入门示例' },
tooltip: {},
legend: { data: ['一周内的温度变化'] },
xAxis: {//横轴
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
name:'星期'
},
yAxis: { name:'摄氏度' },//纵轴
series: [
{ name: '一周内的温度变化',
type: 'line',
data: [10, 5, 11, 17, 1, 8, 9]
}
]
};
myChart.setOption(option);
</script>
(3)测试效果
(1)提交查询会员数量的请求
在report_member.html页面中,可以通过 echarts.js中的 echarts.init( )方法初始化容器 chart1,然后使用 Axios 发送异步请求获取图表需要的会员数量,最后调用setOption( )方法生成折线图。
<script src="../js/axios-0.18.0.js"></script>
<script src="../plugins/echarts/echarts.js"></script>
<script type="text/javascript">
// 基于准备好的DOM,初始化echarts实例
var myChart1 = echarts.init(document.getElementById('chart1'));
axios.get("/report/getMemberReport.do").then((res)=>{
myChart1.setOption(
{
......省略title、tooltip、legend
xAxis: { data: res.data.data.months },
yAxis: { type:'value' },
series: [{
name: '会员数量',
type: 'line',
data: res.data.data.memberCount
}]
});});
</script>
(2)实现查询会员数量控制器
在 controller 包下创建控制器类 ReportController,在类中定义getMemberReport( )方法,用于接收和处理获取会员数量的请求。
(1)动态获取当前日期之前 12 个月的年份和月份,并封装到集合中;
(2)根据月份查询对应的会员数量;
(3)返回查询提示信息和会员数据。
/**
* 数据统计分析管理
*/
@RestController
@RequestMapping("/report")
public class ReportController {
@Autowired
private MemberService memberService;
@Autowired
private SetmealService setmealService;
//获取会员数量统计数据
@RequestMapping("/getMemberReport")
public Result getMemberReport(){
try{
//动态获取过去1年的12个月,并封装到集合中
List<String> months = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH,-12);//add方法用于计算
for(int i = 0;i<12;i++){
calendar.add(Calendar.MONTH,1);//add方法用于计算
months.add(DateUtils.parseDate2String(
calendar.getTime(),"yyyy.MM"));
}
//根据月份查询对应的会员数量
List<Integer> memberCount = memberService.
findMemberCountByMonths(months);
Map<String,Object> data = new HashMap<>();//用于封装结果
data.put("months",months);//月份
data.put("memberCount",memberCount);//会员数量
return new Result(true, MessageConstant.
GET_MEMBER_NUMBER_REPORT_SUCCESS,data);
}catch (Exception e){
e.printStackTrace();
return new Result(false, MessageConstant.
GET_MEMBER_NUMBER_REPORT_FAIL);
}
}
}
(3)创建查询会员数量服务
在 MemberService 接口中定义 findMemberCountByMonths( )方法,用于查询会员数量。
List<Integer> findMemberCountByMonths(List<String> months); //获取会员数量统计数据
(4)实现查询会员数量服务
在 MemberServiceImpl 类中重写MemberService 接口的 findMemberCountByMonths( )方法,用于查询会员数量。
//根据月份查询会员数量
public List<Integer> findMemberCountByMonths(List<String> months) {
List<Integer> memberCounts = new ArrayList<>();
if(months != null && months.size() > 0){
for (String month : months) {//2019.01
String beginTime = month + ".1";//每月第一天
String endTime = month + ".31";//每月最后一天
String[] strings = month.split("\\.");//分割数据month
String[] strings1 = {"4","6","9","11","04","06","09"};//30天
if (Integer.parseInt(strings[0]) % 4 != 0 && (strings[1].equals("02") || strings[1].equals("2"))){
endTime = month + ".28";//不是闰年
}else if (Integer.parseInt(strings[0]) % 4 == 0 && (strings[1].equals("02") || strings[1].equals("2"))){
endTime = month + ".29";//闰年
}else if (Arrays.asList(strings1).contains(strings[1])){
endTime = month + ".30";//每月只有30天
}
Map<String,String> map = new HashMap<>();//封装查询参数
map.put("begin",beginTime);
map.put("end",endTime);
Integer memberCount = memberDao.findMemberCountBeforeDate(map);
memberCounts.add(memberCount);
}
}
return memberCounts;
}
(5)实现持久层查询会员数量
在 MemberDao 接口中定义 findMemberCountBeforeDate( )方法,用于根据指定日期范围查询会员数量。
//根据月份查找会员
Integer findMemberCountBeforeDate(Map<String,String> map);
在 MemberDao.xml 映射文件中使用select元素映射查询语句,根据指定日期范围查询会员数量。
<!--根据日期统计会员数,统计指定日期之前的会员数-->
<select id="findMemberCountBeforeDate" parameterType="map" resultType="int">
SELECT count(id) FROM t_member WHERE regTime BETWEEN #{begin} AND #{end}
</select>
给数据库表中增加模拟数据
INSERT INTO `t_order` VALUES ('1', '1', '2021-10-08', '微信预约', '未到诊', '1');
INSERT INTO `t_order` VALUES ('2', '2', '2021-10-11', '客户端预约', '未到诊', '1');
INSERT INTO `t_order` VALUES ('3', '3', '2021-10-11', '微信预约', '未到诊', '1');
INSERT INTO `t_order` VALUES ('4', '4', '2021-10-11', '客户端预约', '未到诊', '1');
INSERT INTO `t_order` VALUES ('5', '5', '2021-10-13', '微信预约', '未到诊', '1');
INSERT INTO `t_order` VALUES ('6', '6', '2021-10-15', '微信预约', '未到诊', '2');
INSERT INTO `t_order` VALUES ('7', '7', '2021-10-09', '微信预约', '未到诊', '2');
INSERT INTO `t_order` VALUES ('8', '8', '2021-10-20', '微信预约', '未到诊', '2');
INSERT INTO `t_order` VALUES ('9', '9', '2021-10-18', '微信预约', '未到诊', '2');
INSERT INTO `t_order` VALUES ('10', '10', '2021-10-08', '微信预约', '未到诊', '2');
INSERT INTO `t_order` VALUES ('11', '1', '2021-11-08', '微信预约', '未到诊', '3');
INSERT INTO `t_order` VALUES ('12', '2', '2021-10-17', '客户端预约', '已到诊', '3');
INSERT INTO `t_order` VALUES ('13', '3', '2021-10-16', '微信预约', '未到诊', '3');
INSERT INTO `t_order` VALUES ('14', '4', '2021-10-18', '客户端预约', '已到诊', '4');
INSERT INTO `t_order` VALUES ('15', '5', '2021-10-19', '微信预约', '未到诊', '4');
INSERT INTO `t_order` VALUES ('16', '6', '2021-10-25', '微信预约', '未到诊', '4');
INSERT INTO `t_order` VALUES ('17', '7', '2021-10-19', '微信预约', '未到诊', '6');
INSERT INTO `t_order` VALUES ('18', '8', '2021-10-22', '微信预约', '未到诊', '6');
INSERT INTO `t_order` VALUES ('19', '9', '2021-10-28', '微信预约', '未到诊', '6');
INSERT INTO `t_order` VALUES ('20', '10', '2021-10-28', '微信预约', '未到诊', '6');
INSERT INTO `t_order` VALUES ('21', '2', '2021-10-28', '微信预约', '未到诊', '6');
(6)测试会员数量统计
启动服务,在浏览器中访问http://localhost:8080/backend/pages/report_member.html。
9-2 套餐预约占比统计
体检套餐是健康管理机构的重要产品,通过分析各个套餐的预约占比,可以了解不同群体的体检需求,进而可优化套餐结构。图形报表中的饼图可以对各项数据的大小和其占比情况进行直观展示,因此我们可以通过饼图展示套餐预约占比。
(1)提交查询套餐预约数量的请求
可以通过 echarts. js中的echarts.init( )方法初始化在 report_setmeal.html 页面的DOM容器chart1,然后使用Axios发送异步请求获取套餐预约数量,最后调用setOption( )方法生成饼图。
(2)实现查询套餐预约数量控制器
在 ReportController 类中定义 getSetmealReport( )方法,用于接收和处理获取套餐预约数量的请求。
//获取套餐占比统计数据
@RequestMapping("/getSetmealReport")
public Result getSetmealReport(){
try{
List<String> setmealNames = new ArrayList<>();//封装套餐名称集合
//套餐名称和预约数量集合
List<Map> setmealCount = setmealService.getSetmealReport();
if(setmealCount != null && setmealCount.size() > 0){
for (Map map : setmealCount) {
String name = (String) map.get("name");//套餐名称
setmealNames.add(name);
}
}
Map<String,Object> map = new HashMap<>();//封装返回结果
map.put("setmealNames",setmealNames);
map.put("setmealCount",setmealCount);
return new Result(true, MessageConstant.
GET_SETMEAL_COUNT_REPORT_SUCCESS,map);
}catch (Exception e){
e.printStackTrace();
return new Result(false, MessageConstant.
GET_SETMEAL_COUNT_REPORT_FAIL);
}
}
(3)创建查询套餐预约数量服务
在 SetmealService 接口中定义 getSetmealReport( )方法,用于查询套餐预约数量。
List<Map> getSetmealReport();//查询套餐预约占比情况
(4)实现查询套餐预约数量服务
在SetmealServiceImpl 类中重写 SetmealService 接口的 getSetmealReport( )方法,用于查询套餐预约数量。
//查询套餐预约占比情况
public List<Map> getSetmealReport() {
return setmealDao.getSetmealReport();//调用持久化方法,查询套餐预约占比
}
(5)实现持久层查询套餐预约数量
在 SetmealDao 接口中定义 getSetmealReport( )方法,用于查询套餐预约数量。
List<Map> getSetmealReport(); //查询套餐预约占比
在 SetmealDao.xml 映射文件中使用select元素映射查询语句,根据套餐id查询套餐预约数量。
<!--查询套餐预约占比数据-->
<select id="getSetmealReport" resultType="map">
SELECT s.name,count(o.setmeal_id) AS value FROM t_order o,t_setmeal s
WHERE o.setmeal_id = s.id
GROUP BY s.name
</select>
(6)测试套餐预约占比统计
启动服务,在浏览器中访http://localhost:8080/backend/pages/report_setmeal.html。
9-3 运营数据统计
医疗健康的运营数据包括会员数据、预约到诊数据和热门套餐三部分内容,通过对运营数据进行统计与分析,能够及时发现运营过程中存在的问题,从而更好地完善体检产品和服务。
在浏览器中访问report_business.html 页面。
(1)提交查询运营数据的请求
要实现访问 report_business.html 页面时展示运营数据,可以将查询操作定义在钩子函数 created( )中, created( )函数在 Vue 对象初始化完成后自动执行。
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
......
created() {
axios.get("/report/getBusinessReportData.do").then((res)=>{
this.reportData = res.data.data;
})
}
})
</script>
(2)实现查询运营数据控制器
在ReportController 类中定义 getBusinessReportData( )方法,用于接收和处理查询运营数据的请求。
@Autowired //注入服务
private ReportService reportService;
//获取运营统计数据
@RequestMapping("/getBusinessReportData")
public Result getBusinessReportData(){
try{
Map<String,Object> map = reportService.getBusinessReportData();
return new Result(true, MessageConstant.
GET_BUSINESS_REPORT_SUCCESS,map);
}catch (Exception e){
e.printStackTrace();
return new Result(false, MessageConstant.GET_BUSINESS_REPORT_FAIL);
}
}
(3)创建查询运营数据服务
在service 包下创建服务接口 ReportService,在接口中定义getBusinessReportData( )方法,用于查询运营数据。
/**
* 数据统计分析接口
*/
public interface ReportService {
//获取运营数据
Map<String,Object> getBusinessReportData()throws Exception;
}
(4)实现查询运营数据服务
在service.impl 包下创建 ReportService 接口的实现类ReportServiceImpl,重写 ReportService 接口的 getBusinessReportData( )方法,用于查询运营数据。 首先动态获取系统当前时间、本周一的时间、本月第一天的时间,用于查询数据; 调用接口MemberDao、OrderDao中的方法查询表格中需要的数据; 将查询返回的结果封装到 Map 集合中返回。
/**
* 数据统计分析服务接口实现类
*/
@Service("reportService")
@Transactional
public class ReportServiceImpl implements ReportService {
@Autowired
private MemberDao memberDao;
@Autowired
private OrderDao orderDao;
//获取运营数据
public Map<String, Object> getBusinessReportData() throws Exception {
//获得当前系统时间
String today = DateUtils.parseDate2String(DateUtils.getToday());
//动态获得本周一的日期
String thisWeekMonday = DateUtils.
parseDate2String(DateUtils.getThisWeekMonday());
//动态获得本月第一天的日期
String firstDay4ThisMonth = DateUtils.
parseDate2String(DateUtils.getFirstDay4ThisMonth());
//今日新增会员数
Integer todayNewMember = memberDao.findMemberCountByDate(today);
//总会员数据
Integer totalMember = memberDao.findMemberTotalCount();
//本周新增会员数
Integer thisWeekNewMember = memberDao.
findMemberCountAfterDate(thisWeekMonday);
//本月新增会员数
Integer thisMonthNewMember = memberDao.
findMemberCountAfterDate(firstDay4ThisMonth);
//今日预约数
Integer todayOrderNumber = orderDao.findOrderCountByDate(today);
//今日到诊数
Integer todayVisitsNumber = orderDao.findVisitsCountByDate(today);
//本周预约数
Integer thisWeekOrderNumber = orderDao.
findOrderCountAfterDate(thisWeekMonday);
//本周到诊数
Integer thisWeekVisitsNumber = orderDao.
findVisitsCountAfterDate(thisWeekMonday);
//本月预约数
Integer thisMonthOrderNumber = orderDao.
findOrderCountAfterDate(firstDay4ThisMonth);
//本月到诊数
Integer thisMonthVisitsNumber = orderDao.
findVisitsCountAfterDate(firstDay4ThisMonth);
List<Map> hotSetmeal = orderDao.findHotSetmeal();//热门套餐
//定义map集合,将所有查询结果封装到集合中
Map<String,Object> result = new HashMap<>();
result.put("reportDate",today);
result.put("todayNewMember",todayNewMember);
result.put("totalMember",totalMember);
result.put("thisWeekNewMember",thisWeekNewMember);
result.put("thisMonthNewMember",thisMonthNewMember);
result.put("todayOrderNumber",todayOrderNumber);
result.put("thisWeekOrderNumber",thisWeekOrderNumber);
result.put("thisMonthOrderNumber",thisMonthOrderNumber);
result.put("todayVisitsNumber",todayVisitsNumber);
result.put("thisWeekVisitsNumber",thisWeekVisitsNumber);
result.put("thisMonthVisitsNumber",thisMonthVisitsNumber);
result.put("hotSetmeal",hotSetmeal);
return result;//返回查询结果
}
}
(5)实现持久层查询会员数量
在 MemberDao 接口中定义查询会员数量的相关方法。
Integer findMemberCountByDate(String date);//今日新增会员数
Integer findMemberTotalCount();//总会员数
//根据日期统计会员数,统计指定日期之后的会员数
Integer findMemberCountAfterDate(String thisWeekMonday);
在 MemberDao.xml 映射文件中使用select元素映射查询语句,分别根据日期查询新增会员数、查询总会员数、根据指定日期范围查询会员数。
<!--根据日期统计会员数-->
<select id="findMemberCountByDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_member WHERE regTime = #{value}
</select>
<!--总会员数-->
<select id="findMemberTotalCount" resultType="java.lang.Integer">
SELECT count(id) FROM t_member
</select>
<!--根据日期统计会员数,统计指定日期之后的会员数-->
<select id="findMemberCountAfterDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_member WHERE regTime >= #{value}
</select>
(6)实现持久层查询体检预约数
在 OrderDao 接口中定义查询预约数和到诊数的相关方法。
Integer findOrderCountByDate(String today);//今日预约数
Integer findVisitsCountByDate(String today);//今日到诊数
//本周、本月预约数
Integer findOrderCountAfterDate(String thisWeekMonday);
//本周、本月到诊数
Integer findVisitsCountAfterDate(String thisWeekMonday);
List<Map> findHotSetmeal();//热门套餐
在OrderDao.xml 映射文件中使用select元素映射查询语句,查询运营数据。
<!--根据日期统计预约数-->
<select id="findOrderCountByDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_order WHERE orderDate = #{value}
</select>
<!--根据日期统计到诊数-->
<select id="findVisitsCountByDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_order
WHERE orderDate = #{value} AND orderStatus = '已到诊'
</select>
<!--根据日期统计预约数,统计指定日期之后的预约数-->
<select id="findOrderCountAfterDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_order WHERE orderDate >= #{value}
</select>
<!--根据日期统计到诊数,统计指定日期之后的到诊数-->
<select id="findVisitsCountAfterDate" parameterType="string" resultType="int">
SELECT count(id) FROM t_order
WHERE orderDate >= #{value} AND orderStatus = '已到诊'
</select>
<!--热门套餐,查询前5条-->
<select id="findHotSetmeal" resultType="map">
SELECT s.name, count(o.id) setmeal_count ,count(o.id)/(SELECT count(id)
FROM t_order) proportion
FROM t_order o INNER JOIN t_setmeal s ON s.id = o.setmeal_id
GROUP BY o.setmeal_id
ORDER BY setmeal_count DESC
LIMIT 0,4
</select>
(7)测试运营数据统计
启动服务,在浏览器中访问 http://localhost:8080/backend/pages/report_business.html。
模块小结
本模块主要对管理端的统计分析进行了讲解。首先讲解了 ECharts 的使用,然后运用 ECharts 实现了会员数量统计和套餐预约占比统计,最后实现了运营数据统计。希望通过本模块的学习,可以熟悉 ECharts的使用,能够独立实现统计分析模块功能。