鸿蒙健康报告生成(周/月数据汇总图表)详解
【摘要】 一、引言在个人健康管理中,定期回顾健康数据(如心率、步数、睡眠时长、血压等)是评估身体状况、调整生活习惯的重要依据。传统健康报告生成方式(如手动记录、医院体检报告)存在 数据分散(不同设备/APP的数据孤立)、分析复杂(需专业工具处理大量数据)、可视化不足(仅文字描述,缺乏直观图表)等问题。鸿蒙操作系统(HarmonyOS)凭借 分布式数据管理、多...
一、引言
二、技术背景
1. 鸿蒙核心技术支撑
-
分布式健康数据服务(Distributed Health Data Service):支持跨设备(手机、智能手表、手环)同步健康数据(如步数、心率、睡眠、血压),提供统一的数据接口(如 HealthKit
),开发者无需关心数据来源,直接获取聚合后的健康数据。 -
图表可视化组件(@ohos.charts):鸿蒙原生支持的图表库,提供 折线图(LineChart)、柱状图(BarChart)、饼图(PieChart) 等常用图表类型,支持数据绑定、动态更新和自定义样式,便于开发者快速构建健康数据可视化界面。 -
分布式数据管理(DDS):通过分布式数据库(如 Preferences
或RelationalStore
)存储用户健康数据的本地缓存和历史记录,结合分布式软总线实现多设备数据同步,确保周/月报告的数据完整性。 -
定时任务与周期触发:通过鸿蒙的 定时器(Timer) 或 AlarmManager 定期(如每周日23:59、每月最后一天23:59)触发健康报告生成逻辑,自动生成并推送报告至用户设备。
2. 健康报告的业务需求
-
多源数据整合:整合来自不同设备(如手机计步器、智能手表心率监测、手环睡眠监测)的健康数据,消除数据孤岛,提供统一的周/月数据视图。 -
周期性与自动化:按周/月维度自动生成报告(如每周一生成上周报告,每月1日生成上月报告),无需用户手动操作。 -
直观可视化:通过图表(如步数折线图、睡眠时长柱状图)展示数据趋势(如“本周步数较上周增长10%”“本月深睡比例提升5%”),辅助用户快速理解健康状况。 -
个性化适配:支持用户自定义报告内容(如关注“心率”“血压”或“运动时长”),并根据用户健康目标(如“每日步数目标8000”)提供达成率分析。
三、应用使用场景
1. 周健康报告(短期趋势分析)
2. 月健康报告(长期趋势分析)
3. 跨设备数据同步报告
四、不同场景下详细代码实现
场景 1:周健康报告生成(手机端)
1.1 核心代码实现(WeeklyHealthReport.ets)
// src/main/ets/pages/WeeklyHealthReport.ets
import healthManager from '../utils/HealthDataManager.ets';
import { LineChart, BarChart } from '@ohos.charts';
@Entry
@Component
struct WeeklyHealthReport {
@State private healthManager: healthManager.HealthDataManager = new healthManager.HealthDataManager(this.context);
@State private weeklyData: WeeklyHealthData | null = null; // 周健康数据
@State private selectedTab: number = 0; // 0: 步数趋势, 1: 睡眠时长
// 周健康数据结构
interface WeeklyHealthData {
totalSteps: number; // 总步数
avgHeartRate: number; // 平均心率(bpm)
totalSleepDuration: number; // 睡眠总时长(分钟)
deepSleepRatio: number; // 深睡比例(%)
dailySteps: Array<{ date: string; steps: number }>; // 每日步数(用于折线图)
dailySleepDuration: Array<{ date: string; duration: number }>; // 每日睡眠时长(用于柱状图)
}
aboutToAppear() {
this.loadWeeklyData();
}
// 加载上周健康数据(从分布式健康数据服务获取)
private async loadWeeklyData() {
try {
// 获取上周的日期范围(示例:3月18日-3月24日)
const dateRange = this.getLastWeekDateRange();
// 调用健康数据管理工具类获取数据
this.weeklyData = await this.healthManager.getWeeklyHealthData(dateRange.startDate, dateRange.endDate);
} catch (error) {
console.error('加载周健康数据失败:', error);
this.weeklyData = null;
}
}
// 计算上周的日期范围(工具方法)
private getLastWeekDateRange(): { startDate: string; endDate: string } {
const today = new Date();
const lastSunday = new Date(today);
lastSunday.setDate(today.getDate() - today.getDay() - 7); // 上周日
const lastSaturday = new Date(lastSunday);
lastSaturday.setDate(lastSunday.getDate() + 6); // 上周六
const startDate = `${lastSunday.getFullYear()}-${String(lastSunday.getMonth() + 1).padStart(2, '0')}-${String(lastSunday.getDate()).padStart(2, '0')}`;
const endDate = `${lastSaturday.getFullYear()}-${String(lastSaturday.getMonth() + 1).padStart(2, '0')}-${String(lastSaturday.getDate()).padStart(2, '0')}`;
return { startDate, endDate };
}
// 切换图表标签(步数趋势/睡眠时长)
private switchTab(index: number) {
this.selectedTab = index;
}
build() {
Column() {
Text('上周健康报告')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 关键指标卡片
if (this.weeklyData) {
Row() {
this.buildMetricCard('总步数', `${this.weeklyData.totalSteps.toLocaleString()}步`, '#007bff');
this.buildMetricCard('平均心率', `${this.weeklyData.avgHeartRate}bpm`, '#28a745');
this.buildMetricCard('睡眠总时长', `${Math.floor(this.weeklyData.totalSleepDuration / 60)}h${this.weeklyData.totalSleepDuration % 60}m`, '#ffc107');
this.buildMetricCard('深睡比例', `${this.weeklyData.deepSleepRatio}%`, '#dc3545');
}
.width('100%')
.margin({ bottom: 30 });
// 图表标签切换
Row() {
Text(this.selectedTab === 0 ? '● 步数趋势' : '○ 步数趋势')
.fontSize(16)
.fontWeight(this.selectedTab === 0 ? FontWeight.Bold : FontWeight.Normal)
.onClick(() => this.switchTab(0));
Text(this.selectedTab === 1 ? '● 睡眠时长' : '○ 睡眠时长')
.fontSize(16)
.fontWeight(this.selectedTab === 1 ? FontWeight.Bold : FontWeight.Normal)
.onClick(() => this.switchTab(1));
}
.width('100%')
.justifyContent(FlexAlign.Center)
.margin({ bottom: 20 });
// 图表展示(根据标签切换)
if (this.selectedTab === 0 && this.weeklyData?.dailySteps) {
this.buildStepTrendChart();
} else if (this.selectedTab === 1 && this.weeklyData?.dailySleepDuration) {
this.buildSleepDurationChart();
}
} else {
Text('暂无上周健康数据')
.fontSize(16)
.fontColor('#999')
.margin({ top: 50 });
}
}
.width('100%')
.height('100%')
.padding(20);
}
// 构建关键指标卡片
@Builder
buildMetricCard(title: string, value: string, color: string) {
Column() {
Text(value)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor(color);
Text(title)
.fontSize(14)
.fontColor('#666')
.margin({ top: 5 });
}
.width('25%')
.padding(15)
.backgroundColor('#f8f9fa')
.borderRadius(8)
.alignItems(HorizontalAlign.Center);
}
// 构建步数趋势折线图
@Builder
buildStepTrendChart() {
if (!this.weeklyData?.dailySteps) return;
LineChart({
data: this.weeklyData.dailySteps.map(item => ({ x: item.date, y: item.steps })),
xAxis: { title: '日期', data: this.weeklyData.dailySteps.map(item => item.date) },
yAxis: { title: '步数' },
lineColor: '#007bff',
lineWidth: 2,
pointColor: '#007bff',
backgroundColor: '#ffffff',
padding: { top: 20, bottom: 40, left: 40, right: 20 }
})
.width('100%')
.height(300)
.margin({ bottom: 20 });
}
// 构建睡眠时长柱状图
@Builder
buildSleepDurationChart() {
if (!this.weeklyData?.dailySleepDuration) return;
BarChart({
data: this.weeklyData.dailySleepDuration.map(item => ({ x: item.date, y: item.duration })),
xAxis: { title: '日期', data: this.weeklyData.dailySleepDuration.map(item => item.date) },
yAxis: { title: '睡眠时长(分钟)' },
barColor: '#28a745',
backgroundColor: '#ffffff',
padding: { top: 20, bottom: 40, left: 40, right: 20 }
})
.width('100%')
.height(300);
}
}
1.2 分布式健康数据管理工具类(HealthDataManager.ets)
// src/main/ets/utils/HealthDataManager.ets
import distributedHealth from '@ohos.healthkit'; // 鸿蒙健康数据服务(示例,实际API可能不同)
import { BusinessError } from '@ohos.base';
export class HealthDataManager {
private context: any; // 鸿蒙上下文
constructor(context: any) {
this.context = context;
}
// 获取指定日期范围的周健康数据(总步数、平均心率、睡眠总时长、深睡比例、每日明细)
public async getWeeklyHealthData(startDate: string, endDate: string): Promise<WeeklyHealthReport.ets['WeeklyHealthData']> {
try {
// 1. 获取总步数(示例:通过 HealthKit 的步数数据类型)
const stepData = await distributedHealth.getStepCount(this.context, startDate, endDate);
const totalSteps = stepData.totalSteps || 0;
// 2. 获取平均心率(示例:通过 HealthKit 的心率数据类型)
const heartRateData = await distributedHealth.getHeartRateAverage(this.context, startDate, endDate);
const avgHeartRate = heartRateData.avgHeartRate || 0;
// 3. 获取睡眠总时长和深睡比例(示例:通过 HealthKit 的睡眠数据类型)
const sleepData = await distributedHealth.getSleepSummary(this.context, startDate, endDate);
const totalSleepDuration = sleepData.totalSleepDuration || 0; // 分钟
const deepSleepRatio = sleepData.deepSleepRatio || 0; // 百分比
// 4. 获取每日步数(用于折线图)
const dailySteps = await distributedHealth.getDailyStepCount(this.context, startDate, endDate);
const dailyStepsFormatted = dailySteps.map((item: any) => ({
date: item.date,
steps: item.steps || 0
}));
// 5. 获取每日睡眠时长(用于柱状图)
const dailySleep = await distributedHealth.getDailySleepDuration(this.context, startDate, endDate);
const dailySleepFormatted = dailySleep.map((item: any) => ({
date: item.date,
duration: item.duration || 0 // 分钟
}));
return {
totalSteps,
avgHeartRate,
totalSleepDuration,
deepSleepRatio,
dailySteps: dailyStepsFormatted,
dailySleepDuration: dailySleepFormatted
};
} catch (error) {
const err = error as BusinessError;
console.error(`获取周健康数据失败(${startDate}-${endDate}),错误码: ${err.code}, 消息: ${err.message}`);
throw new Error('分布式健康数据服务读取失败');
}
}
}
-
用户点击“周健康报告”后,应用自动获取上周的健康数据,展示总步数、平均心率、睡眠总时长、深睡比例等关键指标卡片; -
用户可通过标签切换查看 步数趋势折线图(显示每日步数波动)或 睡眠时长柱状图(显示每日睡眠时长对比); -
数据来源于分布式健康数据服务,自动整合手机、智能手表等多设备数据。
场景 2:月健康报告生成(手机端)
2.1 核心代码实现(MonthlyHealthReport.ets)
// src/main/ets/pages/MonthlyHealthReport.ets
import healthManager from '../utils/HealthDataManager.ets';
import { LineChart, PieChart } from '@ohos.charts';
@Entry
@Component
struct MonthlyHealthReport {
@State private healthManager: healthManager.HealthDataManager = new healthManager.HealthDataManager(this.context);
@State private monthlyData: MonthlyHealthData | null = null; // 月健康数据
@State private selectedTab: number = 0; // 0: 心率趋势, 1: 睡眠分布
// 月健康数据结构
interface MonthlyHealthData {
totalSteps: number; // 月累计步数
avgDailyHeartRate: number; // 平均每日心率(bpm)
sleepStageDistribution: Array<{ stage: string; ratio: number }>; // 睡眠阶段分布(深睡/浅睡/清醒)
monthlyHeartRate: Array<{ date: string; rate: number }>; // 每日心率(用于折线图)
}
aboutToAppear() {
this.loadMonthlyData();
}
// 加载上月健康数据
private async loadMonthlyData() {
try {
const dateRange = this.getLastMonthDateRange();
this.monthlyData = await this.healthManager.getMonthlyHealthData(dateRange.startDate, dateRange.endDate);
} catch (error) {
console.error('加载月健康数据失败:', error);
this.monthlyData = null;
}
}
// 计算上月的日期范围(工具方法)
private getLastMonthDateRange(): { startDate: string; endDate: string } {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDayOfLastMonth = new Date(firstDayOfMonth);
lastDayOfLastMonth.setDate(lastDayOfLastMonth.getDate() - 1); // 上月最后一天
const firstDayOfLastMonth = new Date(lastDayOfLastMonth);
firstDayOfLastMonth.setDate(1); // 上月第一天
const startDate = `${firstDayOfLastMonth.getFullYear()}-${String(firstDayOfLastMonth.getMonth() + 1).padStart(2, '0')}-${String(firstDayOfLastMonth.getDate()).padStart(2, '0')}`;
const endDate = `${lastDayOfLastMonth.getFullYear()}-${String(lastDayOfLastMonth.getMonth() + 1).padStart(2, '0')}-${String(lastDayOfLastMonth.getDate()).padStart(2, '0')}`;
return { startDate, endDate };
}
// 切换图表标签(心率趋势/睡眠分布)
private switchTab(index: number) {
this.selectedTab = index;
}
build() {
Column() {
Text('上月健康报告')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 关键指标卡片
if (this.monthlyData) {
Row() {
this.buildMetricCard('月累计步数', `${this.monthlyData.totalSteps.toLocaleString()}步`, '#007bff');
this.buildMetricCard('平均心率', `${this.monthlyData.avgDailyHeartRate}bpm`, '#28a745');
// 可扩展更多指标(如血压、体重)
}
.width('100%')
.margin({ bottom: 30 });
// 图表标签切换
Row() {
Text(this.selectedTab === 0 ? '● 心率趋势' : '○ 心率趋势')
.fontSize(16)
.fontWeight(this.selectedTab === 0 ? FontWeight.Bold : FontWeight.Normal)
.onClick(() => this.switchTab(0));
Text(this.selectedTab === 1 ? '● 睡眠分布' : '○ 睡眠分布')
.fontSize(16)
.fontWeight(this.selectedTab === 1 ? FontWeight.Bold : FontWeight.Normal)
.onClick(() => this.switchTab(1));
}
.width('100%')
.justifyContent(FlexAlign.Center)
.margin({ bottom: 20 });
// 图表展示
if (this.selectedTab === 0 && this.monthlyData?.monthlyHeartRate) {
this.buildHeartRateTrendChart();
} else if (this.selectedTab === 1 && this.monthlyData?.sleepStageDistribution) {
this.buildSleepStagePieChart();
}
} else {
Text('暂无上月健康数据')
.fontSize(16)
.fontColor('#999')
.margin({ top: 50 });
}
}
.width('100%')
.height('100%')
.padding(20);
}
// 构建关键指标卡片(同周报告)
@Builder
buildMetricCard(title: string, value: string, color: string) {
Column() {
Text(value)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor(color);
Text(title)
.fontSize(14)
.fontColor('#666')
.margin({ top: 5 });
}
.width('33.33%')
.padding(15)
.backgroundColor('#f8f9fa')
.borderRadius(8)
.alignItems(HorizontalAlign.Center);
}
// 构建心率趋势折线图
@Builder
buildHeartRateTrendChart() {
if (!this.monthlyData?.monthlyHeartRate) return;
LineChart({
data: this.monthlyData.monthlyHeartRate.map(item => ({ x: item.date, y: item.rate })),
xAxis: { title: '日期', data: this.monthlyData.monthlyHeartRate.map(item => item.date) },
yAxis: { title: '心率(bpm)' },
lineColor: '#dc3545',
lineWidth: 2,
pointColor: '#dc3545',
backgroundColor: '#ffffff',
padding: { top: 20, bottom: 40, left: 40, right: 20 }
})
.width('100%')
.height(300)
.margin({ bottom: 20 });
}
// 构建睡眠阶段饼图
@Builder
buildSleepStagePieChart() {
if (!this.monthlyData?.sleepStageDistribution) return;
PieChart({
data: this.monthlyData.sleepStageDistribution.map(item => ({ label: item.stage, value: item.ratio })),
colors: ['#28a745', '#ffc107', '#dc3545'], // 深睡/浅睡/清醒
backgroundColor: '#ffffff',
padding: { top: 20, bottom: 20, left: 20, right: 20 }
})
.width('100%')
.height(300);
}
}
2.2 分布式健康数据管理工具类(扩展)
// 在 HealthDataManager.ets 中扩展月健康数据方法
// ...(其他代码同周报告)
// 获取月健康数据(总步数、平均心率、睡眠分布、每日心率)
public async getMonthlyHealthData(startDate: string, endDate: string): Promise<MonthlyHealthReport.ets['MonthlyHealthData']> {
try {
// 1. 月累计步数
const stepData = await distributedHealth.getStepCount(this.context, startDate, endDate);
const totalSteps = stepData.totalSteps || 0;
// 2. 平均每日心率
const heartRateData = await distributedHealth.getHeartRateAverage(this.context, startDate, endDate);
const avgDailyHeartRate = heartRateData.avgHeartRate || 0;
// 3. 睡眠阶段分布(深睡/浅睡/清醒占比)
const sleepStageData = await distributedHealth.getSleepStageDistribution(this.context, startDate, endDate);
const sleepStageDistribution = sleepStageData.distribution || [
{ stage: '深睡', ratio: 0 },
{ stage: '浅睡', ratio: 0 },
{ stage: '清醒', ratio: 0 }
];
// 4. 每日心率(用于折线图)
const monthlyHeartRate = await distributedHealth.getDailyHeartRate(this.context, startDate, endDate);
const monthlyHeartRateFormatted = monthlyHeartRate.map((item: any) => ({
date: item.date,
rate: item.rate || 0
}));
return {
totalSteps,
avgDailyHeartRate,
sleepStageDistribution,
monthlyHeartRate: monthlyHeartRateFormatted
};
} catch (error) {
const err = error as BusinessError;
console.error(`获取月健康数据失败(${startDate}-${endDate}),错误码: ${err.code}, 消息: ${err.message}`);
throw new Error('分布式健康数据服务读取失败');
}
}
-
用户点击“月健康报告”后,应用展示月累计步数、平均每日心率等关键指标; -
用户可通过标签切换查看 心率趋势折线图(显示每日心率波动,辅助分析心率异常)或 睡眠阶段饼图(显示深睡/浅睡/清醒占比,评估睡眠质量); -
数据整合多设备监测结果,提供全面的月度健康视图。
五、原理解释
1. 鸿蒙健康报告生成的核心流程
-
数据采集与整合: -
通过 分布式健康数据服务(HealthKit) 获取用户的多设备健康数据(如手机计步器、智能手表心率监测、手环睡眠监测),自动整合为统一的周/月数据集合(如总步数=手机步数+手表步数)。 -
数据按时间范围(上周/上月)筛选,提取关键指标(步数、心率、睡眠时长)和明细数据(每日步数、每日睡眠时长)。
-
-
报告生成与存储: -
应用调用 HealthDataManager
工具类,从分布式健康数据服务获取指定时间范围的健康数据,计算衍生指标(如平均心率=总心率/天数、深睡比例=深睡时长/总睡眠时长)。 -
生成 周健康报告 或 月健康报告,包含关键指标卡片(总步数、平均心率等)和可视化图表(折线图、柱状图、饼图)。
-
-
可视化展示: -
使用鸿蒙的 图表组件(@ohos.charts) 将健康数据绑定到图表(
-
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)