KMP OpenHarmony 健康数据监测与分析高级工具库(手表优化) - 跨平台可穿戴设备解决方案
本文介绍了一个基于Kotlin Multiplatform (KMP)和OpenHarmony平台的健康数据监测工具库,支持多平台开发。该库提供心率监测、睡眠分析、运动追踪、健康评分等核心功能,通过KMP技术实现代码复用。核心模块包括数据采集、分析算法和结果展示,其中Kotlin实现部分展示了心率数据分析、睡眠质量评估、运动数据统计以及综合健康评分计算等关键功能。该方案特别针对智能手表优化,可在O

项目概述
健康数据监测与分析是现代可穿戴设备应用的核心功能。无论是在心率监测、睡眠分析、运动追踪还是健康评估中,都需要进行各种健康数据的处理和分析。然而,不同的编程语言和平台对健康数据的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的健康数据监测与分析高级工具库,特别针对智能手表进行了优化。这个工具库提供了一套完整的健康监测能力,包括心率分析、睡眠监测、运动追踪、健康评分等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用(手表优化)
核心功能模块
- 心率监测: 监测和分析心率数据
- 睡眠分析: 分析睡眠质量和模式
- 运动追踪: 追踪运动数据和卡路里消耗
- 健康评分: 计算综合健康评分
- 数据同步: 同步多个设备的数据
- 趋势分析: 分析健康数据趋势
- 告警提醒: 异常数据告警
- 数据导出: 导出健康数据报告
Kotlin 实现
核心健康监测类
// 文件: src/commonMain/kotlin/HealthMonitor.kt
/**
* 健康数据监测与分析工具类
* 提供心率、睡眠、运动等健康数据处理功能
*/
class HealthMonitor {
data class HeartRateData(
val timestamp: Long,
val bpm: Int,
val quality: Int = 100
)
data class SleepData(
val date: String,
val duration: Int,
val deepSleep: Int,
val lightSleep: Int,
val quality: Double
)
data class ActivityData(
val date: String,
val steps: Int,
val distance: Double,
val calories: Double,
val activeMinutes: Int
)
data class HealthScore(
val overall: Double,
val heartHealth: Double,
val sleepQuality: Double,
val activityLevel: Double,
val stress: Double
)
/**
* 分析心率数据
* @param heartRates 心率数据列表
* @return 心率分析结果
*/
fun analyzeHeartRate(heartRates: List<HeartRateData>): Map<String, Any> {
if (heartRates.isEmpty()) return emptyMap()
val bpms = heartRates.map { it.bpm.toDouble() }
val average = bpms.average()
val min = bpms.minOrNull() ?: 0.0
val max = bpms.maxOrNull() ?: 0.0
val variance = bpms.map { (it - average) * (it - average) }.sum() / bpms.size
val stdDev = Math.sqrt(variance)
val heartHealthScore = when {
average < 60 -> 95.0
average < 70 -> 100.0
average < 80 -> 90.0
average < 100 -> 75.0
else -> 60.0
}
return mapOf(
"average" to average,
"min" to min,
"max" to max,
"stdDev" to stdDev,
"healthScore" to heartHealthScore,
"dataPoints" to heartRates.size
)
}
/**
* 分析睡眠数据
* @param sleepData 睡眠数据
* @return 睡眠分析结果
*/
fun analyzeSleep(sleepData: SleepData): Map<String, Any> {
val totalSleep = sleepData.deepSleep + sleepData.lightSleep
val deepSleepRatio = if (totalSleep > 0) (sleepData.deepSleep.toDouble() / totalSleep) else 0.0
val sleepQuality = when {
sleepData.quality >= 90 -> "优秀"
sleepData.quality >= 75 -> "良好"
sleepData.quality >= 60 -> "中等"
else -> "需要改善"
}
return mapOf(
"totalDuration" to sleepData.duration,
"deepSleep" to sleepData.deepSleep,
"lightSleep" to sleepData.lightSleep,
"deepSleepRatio" to String.format("%.2f", deepSleepRatio * 100),
"quality" to sleepData.quality,
"qualityLevel" to sleepQuality
)
}
/**
* 分析运动数据
* @param activityData 运动数据
* @return 运动分析结果
*/
fun analyzeActivity(activityData: ActivityData): Map<String, Any> {
val caloriesPerStep = if (activityData.steps > 0) activityData.calories / activityData.steps else 0.0
val averageSpeed = if (activityData.activeMinutes > 0) activityData.distance / activityData.activeMinutes else 0.0
val activityLevel = when {
activityData.activeMinutes >= 60 -> "高"
activityData.activeMinutes >= 30 -> "中"
else -> "低"
}
return mapOf(
"steps" to activityData.steps,
"distance" to String.format("%.2f", activityData.distance),
"calories" to String.format("%.2f", activityData.calories),
"activeMinutes" to activityData.activeMinutes,
"caloriesPerStep" to String.format("%.4f", caloriesPerStep),
"averageSpeed" to String.format("%.2f", averageSpeed),
"activityLevel" to activityLevel
)
}
/**
* 计算综合健康评分
* @param heartScore 心率评分
* @param sleepScore 睡眠评分
* @param activityScore 运动评分
* @param stressLevel 压力水平(0-100)
* @return 健康评分
*/
fun calculateHealthScore(
heartScore: Double,
sleepScore: Double,
activityScore: Double,
stressLevel: Double
): HealthScore {
val overall = (heartScore * 0.25 + sleepScore * 0.35 + activityScore * 0.25 + (100 - stressLevel) * 0.15)
return HealthScore(
overall = overall,
heartHealth = heartScore,
sleepQuality = sleepScore,
activityLevel = activityScore,
stress = stressLevel
)
}
/**
* 检测异常心率
* @param heartRates 心率数据列表
* @param threshold 阈值
* @return 异常心率列表
*/
fun detectAbnormalHeartRate(heartRates: List<HeartRateData>, threshold: Int = 20): List<HeartRateData> {
if (heartRates.isEmpty()) return emptyList()
val average = heartRates.map { it.bpm }.average()
return heartRates.filter { Math.abs(it.bpm - average) > threshold }
}
/**
* 计算周健康趋势
* @param dailyScores 每日健康评分列表
* @return 趋势信息
*/
fun calculateWeeklyTrend(dailyScores: List<Double>): Map<String, Any> {
if (dailyScores.isEmpty()) return emptyMap()
val average = dailyScores.average()
val trend = if (dailyScores.size >= 2) {
val firstHalf = dailyScores.take(dailyScores.size / 2).average()
val secondHalf = dailyScores.drop(dailyScores.size / 2).average()
if (secondHalf > firstHalf) "上升" else if (secondHalf < firstHalf) "下降" else "平稳"
} else {
"数据不足"
}
return mapOf(
"weeklyAverage" to String.format("%.2f", average),
"trend" to trend,
"dataPoints" to dailyScores.size
)
}
/**
* 生成健康报告
* @param heartScore 心率评分
* @param sleepScore 睡眠评分
* @param activityScore 运动评分
* @return 报告字符串
*/
fun generateHealthReport(heartScore: Double, sleepScore: Double, activityScore: Double): String {
val report = StringBuilder()
report.append("健康数据报告\n")
report.append("=".repeat(40)).append("\n")
report.append("心率评分: ${String.format("%.1f", heartScore)}\n")
report.append("睡眠评分: ${String.format("%.1f", sleepScore)}\n")
report.append("运动评分: ${String.format("%.1f", activityScore)}\n")
report.append("综合评分: ${String.format("%.1f", (heartScore + sleepScore + activityScore) / 3)}\n")
report.append("\n建议:\n")
if (heartScore < 75) report.append("- 建议增加有氧运动以改善心血管健康\n")
if (sleepScore < 75) report.append("- 建议保证充足睡眠,建立规律作息\n")
if (activityScore < 75) report.append("- 建议增加日常活动量和运动时间\n")
return report.toString()
}
/**
* 同步健康数据
* @param localData 本地数据
* @param remoteData 远程数据
* @return 合并后的数据
*/
fun syncHealthData(localData: Map<String, Any>, remoteData: Map<String, Any>): Map<String, Any> {
val merged = localData.toMutableMap()
merged.putAll(remoteData)
return merged
}
}
Kotlin 实现的核心特点
Kotlin 实现中的健康监测功能充分利用了 Kotlin 标准库的数据处理和数学计算能力。心率分析使用了统计学方法。睡眠分析使用了数据比例计算。
运动追踪使用了距离和卡路里的计算。健康评分使用了加权平均算法。异常检测使用了标准差的倍数判断。趋势分析使用了时间序列比较。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)
/**
* HealthMonitor 类的 JavaScript 版本
* 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
*/
class HealthMonitor {
/**
* 分析心率数据
* @param {Object[]} heartRates - 心率数据列表
* @returns {Object} 心率分析结果
*/
analyzeHeartRate(heartRates) {
if (heartRates.length === 0) return {};
const bpms = heartRates.map(h => h.bpm);
const average = bpms.reduce((a, b) => a + b, 0) / bpms.length;
const min = Math.min(...bpms);
const max = Math.max(...bpms);
const variance = bpms.reduce((sum, bpm) => sum + Math.pow(bpm - average, 2), 0) / bpms.length;
const stdDev = Math.sqrt(variance);
let heartHealthScore;
if (average < 60) heartHealthScore = 95;
else if (average < 70) heartHealthScore = 100;
else if (average < 80) heartHealthScore = 90;
else if (average < 100) heartHealthScore = 75;
else heartHealthScore = 60;
return {
average: average,
min: min,
max: max,
stdDev: stdDev,
healthScore: heartHealthScore,
dataPoints: heartRates.length
};
}
/**
* 分析睡眠数据
* @param {Object} sleepData - 睡眠数据
* @returns {Object} 睡眠分析结果
*/
analyzeSleep(sleepData) {
const totalSleep = sleepData.deepSleep + sleepData.lightSleep;
const deepSleepRatio = totalSleep > 0 ? (sleepData.deepSleep / totalSleep) : 0;
let sleepQuality;
if (sleepData.quality >= 90) sleepQuality = '优秀';
else if (sleepData.quality >= 75) sleepQuality = '良好';
else if (sleepData.quality >= 60) sleepQuality = '中等';
else sleepQuality = '需要改善';
return {
totalDuration: sleepData.duration,
deepSleep: sleepData.deepSleep,
lightSleep: sleepData.lightSleep,
deepSleepRatio: (deepSleepRatio * 100).toFixed(2),
quality: sleepData.quality,
qualityLevel: sleepQuality
};
}
/**
* 分析运动数据
* @param {Object} activityData - 运动数据
* @returns {Object} 运动分析结果
*/
analyzeActivity(activityData) {
const caloriesPerStep = activityData.steps > 0 ? activityData.calories / activityData.steps : 0;
const averageSpeed = activityData.activeMinutes > 0 ? activityData.distance / activityData.activeMinutes : 0;
let activityLevel;
if (activityData.activeMinutes >= 60) activityLevel = '高';
else if (activityData.activeMinutes >= 30) activityLevel = '中';
else activityLevel = '低';
return {
steps: activityData.steps,
distance: activityData.distance.toFixed(2),
calories: activityData.calories.toFixed(2),
activeMinutes: activityData.activeMinutes,
caloriesPerStep: caloriesPerStep.toFixed(4),
averageSpeed: averageSpeed.toFixed(2),
activityLevel: activityLevel
};
}
/**
* 计算综合健康评分
* @param {number} heartScore - 心率评分
* @param {number} sleepScore - 睡眠评分
* @param {number} activityScore - 运动评分
* @param {number} stressLevel - 压力水平
* @returns {Object} 健康评分
*/
calculateHealthScore(heartScore, sleepScore, activityScore, stressLevel) {
const overall = heartScore * 0.25 + sleepScore * 0.35 + activityScore * 0.25 + (100 - stressLevel) * 0.15;
return {
overall: overall,
heartHealth: heartScore,
sleepQuality: sleepScore,
activityLevel: activityScore,
stress: stressLevel
};
}
/**
* 检测异常心率
* @param {Object[]} heartRates - 心率数据列表
* @param {number} threshold - 阈值
* @returns {Object[]} 异常心率列表
*/
detectAbnormalHeartRate(heartRates, threshold = 20) {
if (heartRates.length === 0) return [];
const average = heartRates.reduce((sum, h) => sum + h.bpm, 0) / heartRates.length;
return heartRates.filter(h => Math.abs(h.bpm - average) > threshold);
}
/**
* 计算周健康趋势
* @param {number[]} dailyScores - 每日健康评分列表
* @returns {Object} 趋势信息
*/
calculateWeeklyTrend(dailyScores) {
if (dailyScores.length === 0) return {};
const average = dailyScores.reduce((a, b) => a + b, 0) / dailyScores.length;
let trend = '数据不足';
if (dailyScores.length >= 2) {
const firstHalf = dailyScores.slice(0, Math.floor(dailyScores.length / 2)).reduce((a, b) => a + b, 0) / Math.floor(dailyScores.length / 2);
const secondHalf = dailyScores.slice(Math.floor(dailyScores.length / 2)).reduce((a, b) => a + b, 0) / (dailyScores.length - Math.floor(dailyScores.length / 2));
if (secondHalf > firstHalf) trend = '上升';
else if (secondHalf < firstHalf) trend = '下降';
else trend = '平稳';
}
return {
weeklyAverage: average.toFixed(2),
trend: trend,
dataPoints: dailyScores.length
};
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的数组方法和 Math 对象提供了必要的健康数据处理能力。
reduce 方法用于数据聚合。filter 方法用于异常检测。toFixed 方法用于数值格式化。
ArkTS 调用代码
OpenHarmony 应用集成(手表优化)
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/HealthMonitorPage.ets
import { HealthMonitor } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct HealthMonitorPage {
@State selectedMetric: string = 'heart';
@State result: string = '';
@State resultTitle: string = '';
@State currentScore: number = 0;
private monitor = new HealthMonitor();
private metrics = [
{ name: '❤️ 心率', value: 'heart' },
{ name: '😴 睡眠', value: 'sleep' },
{ name: '🏃 运动', value: 'activity' },
{ name: '📊 评分', value: 'score' },
{ name: '⚠️ 异常', value: 'abnormal' },
{ name: '📈 趋势', value: 'trend' },
{ name: '📋 报告', value: 'report' },
{ name: '🔄 同步', value: 'sync' }
];
build() {
Column() {
// 标题
Text('🏥 健康监测')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(16)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
// 当前评分显示(手表优化)
Column() {
Text('今日评分')
.fontSize(12)
.fontColor('#666666')
.margin({ bottom: 8 })
Text(this.currentScore.toFixed(1))
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#1A237E')
Text('分')
.fontSize(16)
.fontColor('#1A237E')
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.margin({ bottom: 16 })
Scroll() {
Column() {
// 指标选择(网格布局)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.metrics, (metric: { name: string; value: string }) => {
Button(metric.name)
.layoutWeight(1)
.height(48)
.margin({ right: 8, bottom: 8 })
.backgroundColor(this.selectedMetric === metric.value ? '#1A237E' : '#E0E0E0')
.fontColor(this.selectedMetric === metric.value ? '#FFFFFF' : '#333333')
.fontSize(12)
.onClick(() => {
this.selectedMetric = metric.value;
this.result = '';
this.resultTitle = '';
})
})
}
.width('100%')
.padding(12)
// 结果显示
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(11)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(200)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
// 操作按钮
Row() {
Button('分析')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(12)
.borderRadius(6)
.onClick(() => this.analyzeMetric())
Blank()
.width(8)
Button('清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.result = '';
this.resultTitle = '';
})
}
.width('100%')
.padding(12)
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private analyzeMetric() {
try {
switch (this.selectedMetric) {
case 'heart':
const heartRates = [
{ timestamp: 1000, bpm: 72, quality: 100 },
{ timestamp: 2000, bpm: 75, quality: 100 },
{ timestamp: 3000, bpm: 70, quality: 100 },
{ timestamp: 4000, bpm: 78, quality: 100 },
{ timestamp: 5000, bpm: 73, quality: 100 }
];
const heartAnalysis = this.monitor.analyzeHeartRate(heartRates);
this.resultTitle = '❤️ 心率分析';
this.result = `平均心率: ${heartAnalysis.average.toFixed(1)} bpm\n最小: ${heartAnalysis.min} bpm\n最大: ${heartAnalysis.max} bpm\n心率评分: ${heartAnalysis.healthScore.toFixed(1)}`;
this.currentScore = heartAnalysis.healthScore;
break;
case 'sleep':
const sleepData = { date: '2025-12-02', duration: 480, deepSleep: 120, lightSleep: 360, quality: 85 };
const sleepAnalysis = this.monitor.analyzeSleep(sleepData);
this.resultTitle = '😴 睡眠分析';
this.result = `总睡眠: ${sleepAnalysis.totalDuration} 分钟\n深睡眠: ${sleepAnalysis.deepSleep} 分钟\n浅睡眠: ${sleepAnalysis.lightSleep} 分钟\n质量: ${sleepAnalysis.qualityLevel}`;
this.currentScore = sleepData.quality;
break;
case 'activity':
const activityData = { date: '2025-12-02', steps: 8500, distance: 6.2, calories: 450, activeMinutes: 45 };
const activityAnalysis = this.monitor.analyzeActivity(activityData);
this.resultTitle = '🏃 运动分析';
this.result = `步数: ${activityAnalysis.steps}\n距离: ${activityAnalysis.distance} km\n卡路里: ${activityAnalysis.calories} kcal\n活动时间: ${activityAnalysis.activeMinutes} 分钟`;
this.currentScore = 80;
break;
case 'score':
const healthScore = this.monitor.calculateHealthScore(90, 85, 80, 30);
this.resultTitle = '📊 综合评分';
this.result = `综合评分: ${healthScore.overall.toFixed(1)}\n心率: ${healthScore.heartHealth.toFixed(1)}\n睡眠: ${healthScore.sleepQuality.toFixed(1)}\n运动: ${healthScore.activityLevel.toFixed(1)}\n压力: ${healthScore.stress.toFixed(1)}`;
this.currentScore = healthScore.overall;
break;
case 'abnormal':
const heartRates2 = [
{ timestamp: 1000, bpm: 72, quality: 100 },
{ timestamp: 2000, bpm: 120, quality: 80 },
{ timestamp: 3000, bpm: 70, quality: 100 }
];
const abnormal = this.monitor.detectAbnormalHeartRate(heartRates2, 20);
this.resultTitle = '⚠️ 异常检测';
this.result = `异常心率个数: ${abnormal.length}\n${abnormal.map(a => `${a.bpm} bpm`).join(', ')}`;
break;
case 'trend':
const dailyScores = [75, 78, 82, 85, 88, 90, 92];
const trend = this.monitor.calculateWeeklyTrend(dailyScores);
this.resultTitle = '📈 周趋势';
this.result = `周平均: ${trend.weeklyAverage}\n趋势: ${trend.trend}\n数据点: ${trend.dataPoints}`;
break;
case 'report':
const report = this.monitor.generateHealthReport(90, 85, 80);
this.resultTitle = '📋 健康报告';
this.result = report;
break;
case 'sync':
const local = { heart: 72, steps: 8500 };
const remote = { sleep: 480, calories: 450 };
const synced = this.monitor.syncHealthData(local, remote);
this.resultTitle = '🔄 数据同步';
this.result = `同步数据: ${JSON.stringify(synced, null, 2)}`;
break;
}
} catch (e) {
this.resultTitle = '❌ 分析出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 手表应用中集成健康监测工具库需要考虑屏幕空间有限和电池续航的问题。我们设计了一个紧凑的 UI,采用网格布局来展示多个指标。
当前评分显示使用了大字体以便在手表屏幕上清晰显示。指标选择使用了 Flex 布局来自适应不同的屏幕尺寸。结果显示使用了较小的字体以节省空间。
所有操作都经过了手表优化,确保在小屏幕上的可用性。
工作流程详解
健康监测的完整流程
- 数据收集: 从传感器收集心率、睡眠、运动等数据
- 指标选择: 用户在 ArkTS UI 中选择要查看的健康指标
- 分析执行: 调用 HealthMonitor 的相应方法进行分析
- 结果展示: 将分析结果显示在手表屏幕上
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,健康数据分析的逻辑和结果都是完全相同的。
实际应用场景
智能手表应用
在智能手表上,需要实时监测和显示健康数据。这个工具库提供了手表优化的健康监测功能。
健身追踪应用
在健身追踪应用中,需要分析运动和活动数据。这个工具库提供了运动分析功能。
医疗健康应用
在医疗健康应用中,需要监测和分析患者的健康指标。这个工具库提供了专业的健康分析能力。
健康管理平台
在健康管理平台中,需要聚合多个设备的健康数据。这个工具库提供了数据同步和聚合功能。
性能优化
数据缓存
在频繁访问相同的健康数据时,可以缓存分析结果以避免重复计算。
后台处理
在手表上,应该在后台进行数据分析以节省电池。
安全性考虑
数据加密
在同步健康数据时,应该进行加密以保护用户隐私。
访问控制
在共享健康数据时,应该实施严格的访问控制。
总结
这个 KMP OpenHarmony 健康数据监测与分析高级工具库展示了如何使用现代的跨平台技术来处理可穿戴设备的健康监测任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
健康监测是可穿戴设备的核心功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种健康监测和分析功能,从而为用户提供更好的健康管理体验。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更复杂的健康算法、实现更全面的数据可视化等高级特性。同时,定期进行性能测试和优化,确保应用在手表上的性能和电池续航。
更多推荐



所有评论(0)