健康管理系统数据可视化:会员增长趋势与套餐预约分析
会员数量趋势图表实现
会员数据是体检平台的核心资产,通过分析会员增长情况可以评估业务发展状况。本节将实现过去一年内会员数量的趋势展示,采用折线图形式直观呈现数据变化。
1. 前端页面配置
会员趋势图表对应页面为/views/member_trend.html。
1.1 引入图表库
首先需要将ECharts库文件放置在项目的static/js目录下,然后在HTML页面中引入:
<script src="../static/js/echarts.min.js"></script>
1.2 创建图表容器
在页面中添加一个DOM元素作为图表容器:
<div class="chart-container">
<div id="memberChart" style="width: 100%; height: 500px;"></div>
</div>
1.3 初始化图表并加载数据
<script type="text/javascript">
// 初始化图表实例
var chartInstance = echarts.init(document.getElementById('memberChart'));
// 获取动态数据
fetch('/api/report/memberGrowth')
.then(response => response.json())
.then(data => {
chartInstance.setOption({
title: {
text: '会员增长趋势'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: data.months
},
yAxis: {
type: 'value'
},
series: [{
name: '会员总数',
type: 'line',
smooth: true,
data: data.memberCounts,
areaStyle: {}
}]
});
});
</script>
后端API应返回以下格式的数据:
{
"months": ["2023.01", "2023.02", "2023.03", "2023.04"],
"memberCounts": [120, 150, 180, 220]
}
2. 后端实现
2.1 控制器层
@RestController
@RequestMapping("/api/report")
public class DataVisualizationController {
@Autowired
private MemberDataService memberDataService;
@GetMapping("/memberGrowth")
public ResponseData getMemberGrowthData() {
Map<String, Object> resultData = new HashMap<>();
// 生成过去12个月的月份列表
List<String> monthList = generateMonthList(12);
resultData.put("months", monthList);
// 获取每月会员数量
List<Integer> memberCounts = memberDataService.getMonthlyMemberCounts(monthList);
resultData.put("memberCounts", memberCounts);
return ResponseData.success(resultData);
}
private List<String> generateMonthList(int count) {
List<String> months = new ArrayList<>();
LocalDate currentDate = LocalDate.now();
for (int i = count - 1; i >= 0; i--) {
LocalDate monthDate = currentDate.minusMonths(i);
months.add(monthDate.format(DateTimeFormatter.ofPattern("yyyy.MM")));
}
return months;
}
}
2.2 服务接口与实现
服务接口定义:
public interface MemberDataService {
List<Integer> getMonthlyMemberCounts(List<String> months);
}
服务实现类:
@Service
public class MemberDataServiceImpl implements MemberDataService {
@Autowired
private MemberDataRepository memberDataRepository;
@Override
public List<Integer> getMonthlyMemberCounts(List<String> months) {
List<Integer> counts = new ArrayList<>();
for (String month : months) {
// 构造月份最后一天
String endDate = month + ".31";
int count = memberDataRepository.countMembersBeforeDate(endDate);
counts.add(count);
}
return counts;
}
}
2.3 数据访问层
数据访问接口:
@Repository
public interface MemberDataRepository {
int countMembersBeforeDate(String date);
}
SQL映射配置:
<select id="countMembersBeforeDate" parameterType="string" resultType="int">
SELECT COUNT(*) FROM t_member WHERE registration_time <= #{date}
</select>
套餐预约占比图表实现
套餐预约数据反映了不同健康体检套餐的受欢迎程度,通过饼图可以直观展示各套餐的预约占比情况。
1. 前端页面配置
套餐占比图表对应页面为/views/package_proportion.html。
1.1 创建图表容器
<div class="chart-container">
<div id="packageChart" style="width: 100%; height: 500px;"></div>
</div>
1.2 初始化图表并加载数据
<script type="text/javascript">
// 初始化图表实例
var packageChart = echarts.init(document.getElementById('packageChart'));
// 获取动态数据
fetch('/api/report/packageProportion')
.then(response => response.json())
.then(data => {
packageChart.setOption({
title: {
text: '套餐预约占比',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: data.packageNames
},
series: [
{
name: '预约占比',
type: 'pie',
radius: '60%',
center: ['60%', '50%'],
data: data.packageData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
});
</script>
后端API应返回以下格式的数据:
{
"packageNames": ["基础体检", "全面体检", "高端体检"],
"packageData": [
{"name": "基础体检", "value": 35},
{"name": "全面体检", "value": 45},
{"name": "高端体检", "value": 20}
]
}
2. 后端实现
2.1 控制器层
在DataVisualizationController中添加新方法:
@Autowired
private PackageDataService packageDataService;
@GetMapping("/packageProportion")
public ResponseData getPackageProportionData() {
try {
Map<String, Object> resultData = new HashMap<>();
// 获取套餐统计数据
List<Map<String, Object>> packageStats = packageDataService.getPackageStatistics();
resultData.put("packageData", packageStats);
// 提取套餐名称列表
List<String> packageNames = packageStats.stream()
.map(stat -> (String) stat.get("name"))
.collect(Collectors.toList());
resultData.put("packageNames", packageNames);
return ResponseData.success(resultData);
} catch (Exception e) {
return ResponseData.error("获取套餐统计数据失败");
}
}
2.2 服务接口与实现
服务接口定义:
public interface PackageDataService {
List<Map<String, Object>> getPackageStatistics();
}
服务实现类:
@Service
public class PackageDataServiceImpl implements PackageDataService {
@Autowired
private PackageDataRepository packageDataRepository;
@Override
public List<Map<String, Object>> getPackageStatistics() {
return packageDataRepository.findPackageBookingStats();
}
}
2.3 数据访问层
数据访问接口:
@Repository
public interface PackageDataRepository {
List<Map<String, Object>> findPackageBookingStats();
}
SQL映射配置:
<select id="findPackageBookingStats" resultType="map">
SELECT
p.name AS name,
COUNT(o.id) AS value
FROM
t_order o
JOIN t_package p ON o.package_id = p.id
GROUP BY
p.name
ORDER BY
value DESC
</select>