问题描述
如何在鸿蒙应用中实现智能数据分析功能?本文以人情管理系统为例,实现以下智能洞察功能:
- 月度收支趋势预测
- 人情往来健康度评分
- 待回礼智能提醒与紧急度评分
- 关系维护建议生成
这些功能涉及复杂的业务逻辑和算法设计,是鸿蒙应用开发中的高级话题。
技术要点
- 数据分析算法设计
- Promise.all 并发优化
- 业务规则引擎实现
- 评分算法设计
- 智能建议生成
完整实现代码
/**
* 智能数据洞察服务
* 提供数据分析、趋势预测、健康度评分等功能
*/
import { DataService } from './DataService';
import { HumanRecord, Person, RecordType, RelationshipType } from '../model/DataModels';
/**
* 数据洞察接口
*/
export interface DataInsight {
monthlyTrend: MonthlyTrendInsight; // 月度趋势
healthScore: number; // 健康度评分(0-100)
pendingReciprocations: RecipocationItem[]; // 待回礼列表
relationshipSuggestions: RelationshipSuggestion[]; // 关系维护建议
}
/**
* 月度趋势洞察
*/
export interface MonthlyTrendInsight {
predictedReceived: number; // 本月预计总收入
predictedSent: number; // 本月预计总支出
actualReceived: number; // 本月实际总收入
actualSent: number; // 本月实际总支出
receivedChange: number; // 与上月对比(百分比)
sentChange: number; // 与上月对比(百分比)
trendDescription: string; // 趋势描述
}
/**
* 待回礼项
*/
export interface RecipocationItem {
recordId: string; // 记录ID
personId: string; // 人物ID
personName: string; // 人物姓名
receivedAmount: number; // 收到的金额
suggestedAmount: number; // 建议回礼金额
receivedTime: number; // 收到的时间
eventType: string; // 事件类型
daysSince: number; // 距离天数
urgency: number; // 紧急程度(1-5)
}
/**
* 关系维护建议
*/
export interface RelationshipSuggestion {
personId: string; // 人物ID
personName: string; // 人物姓名
relationshipType: RelationshipType; // 关系类型
lastInteractionTime: number; // 最后往来时间
daysSince: number; // 距离天数
suggestionType: string; // 建议类型
suggestionText: string; // 建议内容
priority: number; // 优先级(1-5)
}
export class InsightService {
private static instance: InsightService;
private dataService: DataService;
private constructor() {
this.dataService = DataService.getInstance();
}
public static getInstance(): InsightService {
if (!InsightService.instance) {
InsightService.instance = new InsightService();
}
return InsightService.instance;
}
/**
* 获取完整的数据洞察(并发加载提升性能)
*/
public async getDataInsight(): Promise<DataInsight> {
try {
// 使用Promise.all并发执行,提升性能
const results = await Promise.all([
this.getMonthlyTrendInsight(),
this.calculateHealthScore(),
this.getPendingReciprocations(),
this.getRelationshipSuggestions()
]);
return {
monthlyTrend: results[0],
healthScore: results[1],
pendingReciprocations: results[2],
relationshipSuggestions: results[3]
};
} catch (error) {
console.error('获取数据洞察失败:', JSON.stringify(error));
throw new Error('获取数据洞察失败');
}
}
/**
* 获取月度趋势洞察(含预测)
*/
public async getMonthlyTrendInsight(): Promise<MonthlyTrendInsight> {
try {
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
const allRecords = await this.dataService.getAllRecords();
// 手动过滤本月记录
const currentMonthRecords = allRecords.filter(record =>
record.eventTime >= monthStart.getTime() && record.eventTime <= monthEnd.getTime()
);
// 获取上月数据
const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1, 0, 0, 0, 0);
const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
const lastMonthRecords = allRecords.filter(record =>
record.eventTime >= lastMonthStart.getTime() && record.eventTime <= lastMonthEnd.getTime()
);
// 计算本月实际数据
let actualReceived = 0;
let actualSent = 0;
for (const record of currentMonthRecords) {
if (record.type === RecordType.RECEIVED) {
actualReceived += record.amount;
} else {
actualSent += record.amount;
}
}
// 计算上月数据
let lastMonthReceived = 0;
let lastMonthSent = 0;
for (const record of lastMonthRecords) {
if (record.type === RecordType.RECEIVED) {
lastMonthReceived += record.amount;
} else {
lastMonthSent += record.amount;
}
}
// 预测本月剩余数据(基于当前进度)
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
const currentDay = now.getDate();
const progressRatio = currentDay / daysInMonth;
const predictedReceived = progressRatio > 0 ? Math.round(actualReceived / progressRatio) : actualReceived;
const predictedSent = progressRatio > 0 ? Math.round(actualSent / progressRatio) : actualSent;
// 计算变化百分比
const receivedChange = lastMonthReceived > 0
? Math.round(((predictedReceived - lastMonthReceived) / lastMonthReceived) * 100)
: 0;
const sentChange = lastMonthSent > 0
? Math.round(((predictedSent - lastMonthSent) / lastMonthSent) * 100)
: 0;
// 生成趋势描述
const trendDescription = this.generateTrendDescription(receivedChange, sentChange);
return {
predictedReceived,
predictedSent,
actualReceived,
actualSent,
receivedChange,
sentChange,
trendDescription
};
} catch (error) {
console.error('获取月度趋势失败:', JSON.stringify(error));
return {
predictedReceived: 0,
predictedSent: 0,
actualReceived: 0,
actualSent: 0,
receivedChange: 0,
sentChange: 0,
trendDescription: '暂无数据'
};
}
}
/**
* 生成趋势描述
*/
private generateTrendDescription(receivedChange: number, sentChange: number): string {
const descriptions: string[] = [];
if (receivedChange > 20) {
descriptions.push('收入显著增长');
} else if (receivedChange > 0) {
descriptions.push('收入稳步增长');
} else if (receivedChange < -20) {
descriptions.push('收入明显下降');
} else {
descriptions.push('收入保持稳定');
}
if (sentChange > 20) {
descriptions.push('支出显著增加');
} else if (sentChange > 0) {
descriptions.push('支出稳步增加');
} else if (sentChange < -20) {
descriptions.push('支出明显减少');
} else {
descriptions.push('支出保持稳定');
}
return descriptions.join(',');
}
/**
* 计算人情往来健康度评分(0-100)
*/
public async calculateHealthScore(): Promise<number> {
try {
let score = 100;
// 获取最近6个月的数据
const sixMonthsAgo = Date.now() - (180 * 24 * 60 * 60 * 1000);
const allRecords = await this.dataService.getAllRecords();
const records = allRecords.filter(record => record.eventTime >= sixMonthsAgo);
if (records.length === 0) {
return 50; // 无数据时返回中等分数
}
// 1. 收支平衡度(30分)
let totalReceived = 0;
let totalSent = 0;
for (const record of records) {
if (record.type === RecordType.RECEIVED) {
totalReceived += record.amount;
} else {
totalSent += record.amount;
}
}
const balanceRatio = totalReceived > 0 ? totalSent / totalReceived : 0;
if (balanceRatio >= 0.8 && balanceRatio <= 1.2) {
// 收支平衡,满分
} else if (balanceRatio >= 0.5 && balanceRatio <= 1.5) {
score -= 10;
} else if (balanceRatio >= 0.3 && balanceRatio <= 2.0) {
score -= 20;
} else {
score -= 30;
}
// 2. 往来频率(25分)
const avgMonthlyRecords = records.length / 6;
if (avgMonthlyRecords >= 4) {
// 频率适中
} else if (avgMonthlyRecords >= 2) {
score -= 10;
} else if (avgMonthlyRecords >= 1) {
score -= 15;
} else {
score -= 25;
}
// 3. 待回礼情况(25分)
const pendingReciprocations = await this.getPendingReciprocations();
const urgentCount = pendingReciprocations.filter(item => item.urgency >= 4).length;
if (urgentCount === 0) {
// 无紧急待回礼
} else if (urgentCount <= 2) {
score -= 10;
} else if (urgentCount <= 5) {
score -= 15;
} else {
score -= 25;
}
// 4. 关系维护(20分)
const suggestions = await this.getRelationshipSuggestions();
const highPriorityCount = suggestions.filter(item => item.priority >= 4).length;
if (highPriorityCount === 0) {
// 关系维护良好
} else if (highPriorityCount <= 2) {
score -= 8;
} else if (highPriorityCount <= 5) {
score -= 12;
} else {
score -= 20;
}
return Math.max(0, Math.min(100, score));
} catch (error) {
console.error('计算健康度评分失败:', JSON.stringify(error));
return 50;
}
}
/**
* 获取待回礼列表(智能计算)
*/
public async getPendingReciprocations(): Promise<RecipocationItem[]> {
try {
const persons = await this.dataService.getAllPersons();
const recipocations: RecipocationItem[] = [];
const now = Date.now();
const allRecords = await this.dataService.getAllRecords();
for (const person of persons) {
// 过滤该人物的所有记录
const records = allRecords.filter(record => record.personId === person.id);
records.sort((a, b) => b.eventTime - a.eventTime);
// 计算收支差额
let balance = 0;
let lastReceivedRecord: HumanRecord | null = null;
for (const record of records) {
if (record.type === RecordType.RECEIVED) {
balance += record.amount;
if (!lastReceivedRecord) {
lastReceivedRecord = record;
}
} else {
balance -= record.amount;
}
}
// 如果余额为正且有收入记录,说明需要回礼
if (balance > 0 && lastReceivedRecord) {
const daysSince = Math.floor((now - lastReceivedRecord.eventTime) / (24 * 60 * 60 * 1000));
// 计算紧急程度(基于时间)
let urgency = 1;
if (daysSince > 180) {
urgency = 5; // 超过半年,非常紧急
} else if (daysSince > 90) {
urgency = 4; // 超过3个月,紧急
} else if (daysSince > 30) {
urgency = 3; // 超过1个月,较紧急
} else if (daysSince > 7) {
urgency = 2; // 超过1周,一般
}
// 建议回礼金额(略高于收到的金额)
const suggestedAmount = Math.round(balance * 1.2);
recipocations.push({
recordId: lastReceivedRecord.id,
personId: person.id,
personName: person.name,
receivedAmount: balance,
suggestedAmount,
receivedTime: lastReceivedRecord.eventTime,
eventType: lastReceivedRecord.eventType,
daysSince,
urgency
});
}
}
// 按紧急程度和时间排序
recipocations.sort((a, b) => {
if (a.urgency !== b.urgency) {
return b.urgency - a.urgency;
}
return b.daysSince - a.daysSince;
});
return recipocations;
} catch (error) {
console.error('获取待回礼列表失败:', JSON.stringify(error));
return [];
}
}
/**
* 获取关系维护建议
*/
public async getRelationshipSuggestions(): Promise<RelationshipSuggestion[]> {
try {
const persons = await this.dataService.getAllPersons();
const suggestions: RelationshipSuggestion[] = [];
const now = Date.now();
const allRecords = await this.dataService.getAllRecords();
for (const person of persons) {
const records = allRecords.filter(record => record.personId === person.id);
if (records.length === 0) continue;
// 获取最后往来时间
const sortedRecords = records.sort((a, b) => b.eventTime - a.eventTime);
const lastInteractionTime = sortedRecords[0].eventTime;
const daysSince = Math.floor((now - lastInteractionTime) / (24 * 60 * 60 * 1000));
// 根据关系类型设置不同的提醒阈值
let threshold = 365;
switch (person.relationshipType) {
case RelationshipType.RELATIVE:
threshold = 180;
break;
case RelationshipType.COLLEAGUE:
case RelationshipType.LEADER:
threshold = 180;
break;
case RelationshipType.FRIEND:
case RelationshipType.CLASSMATE:
threshold = 365;
break;
}
// 如果超过阈值的一半,开始提醒
if (daysSince > threshold / 2) {
let suggestionText = '';
let priority = 1;
if (daysSince > threshold) {
suggestionText = `已超过${Math.floor(daysSince / 30)}个月未联系,建议主动问候`;
priority = 5;
} else if (daysSince > threshold * 0.75) {
suggestionText = `已${Math.floor(daysSince / 30)}个月未联系,可以找机会联络`;
priority = 3;
} else {
suggestionText = `已${Math.floor(daysSince / 30)}个月未联系,保持关注`;
priority = 2;
}
suggestions.push({
personId: person.id,
personName: person.name,
relationshipType: person.relationshipType as RelationshipType,
lastInteractionTime,
daysSince,
suggestionType: 'long_time_no_contact',
suggestionText,
priority
});
}
}
// 按优先级排序
suggestions.sort((a, b) => {
if (a.priority !== b.priority) {
return b.priority - a.priority;
}
return b.daysSince - a.daysSince;
});
return suggestions.slice(0, 10); // 最多返回10条建议
} catch (error) {
console.error('获取关系维护建议失败:', JSON.stringify(error));
return [];
}
}
/**
* 获取健康度评级文本
*/
public getHealthScoreLevel(score: number): string {
if (score >= 90) return '优秀';
if (score >= 80) return '良好';
if (score >= 70) return '中等';
if (score >= 60) return '及格';
return '需要改善';
}
/**
* 获取健康度评级颜色
*/
public getHealthScoreColor(score: number): string {
if (score >= 90) return '#4CAF50';
if (score >= 80) return '#8BC34A';
if (score >= 70) return '#FFC107';
if (score >= 60) return '#FF9800';
return '#F44336';
}
}
核心算法解析
1. 月度趋势预测算法
// 基于当前进度预测全月数据
const progressRatio = currentDay / daysInMonth;
const predictedReceived = actualReceived / progressRatio;
2. 健康度评分算法
评分维度(总分 100 分):
- 收支平衡度: 30 分
- 往来频率: 25 分
- 待回礼情况: 25 分
- 关系维护: 20 分
3. 紧急度评分算法
if (daysSince > 180) urgency = 5; // 超过半年
else if (daysSince > 90) urgency = 4; // 超过3个月
else if (daysSince > 30) urgency = 3; // 超过1个月
else if (daysSince > 7) urgency = 2; // 超过1周
else urgency = 1; // 1周内
4. 关系维护阈值
不同关系类型设置不同的联系阈值:
- 亲戚/领导/同事: 6 个月
- 朋友/同学: 1 年
- 邻居: 6 个月
性能优化
1. 并发加载
const results = await Promise.all([
this.getMonthlyTrendInsight(),
this.calculateHealthScore(),
this.getPendingReciprocations(),
this.getRelationshipSuggestions()
]);
2. 数据预加载
一次性加载所有记录,避免重复查询:
const allRecords = await this.dataService.getAllRecords();
3. 内存过滤
使用 JavaScript 数组方法过滤,减少数据库查询。
最佳实践
- 算法可配置化: 将阈值提取为配置项
- 分级评分: 采用多维度评分体系
- 智能建议: 根据业务规则生成建议
- 性能优化: 使用并发加载提升速度
应用场景
- 数据分析报告
- 智能提醒系统
- 用户行为分析
- 个性化推荐
总结
本文实现了一套完整的智能数据洞察系统,包括:
- ✅ 趋势预测算法
- ✅ 健康度评分体系
- ✅ 智能提醒生成
- ✅ 性能优化方案
适用于需要数据分析和智能建议的鸿蒙应用。