> **技术栈**:HarmonyOS 5.0 + ArkTS + Preferences

>

> **适用场景**:用户行为分析、成就系统、数据可视化

---

## 前言

了解用户如何使用应用对产品迭代至关重要。本文将介绍如何在HarmonyOS应用中实现一个完整的使用统计系统,包括数据记录、统计分析和成就系统。

## 一、数据结构设计

### 1.1 使用记录

```typescript

export interface UsageRecord {

  id: string;

  appId: string;      // 功能模块ID

  appName: string;    // 功能名称

  duration: number;   // 使用时长(秒)

  timestamp: number;  // 时间戳

  date: string;       // 日期 YYYY-MM-DD

}

```

### 1.2 统计数据

```typescript

export interface StatisticsData {

  totalUseCount: number;    // 总使用次数

  totalDuration: number;    // 总使用时长

  todayUseCount: number;    // 今日使用次数

  todayDuration: number;    // 今日使用时长

  weeklyData: DailyData[];  // 周数据

  monthlyData: DailyData[]; // 月数据

  appUsage: AppUsageData[]; // 各功能使用统计

  achievements: Achievement[];

}

export interface DailyData {

  date: string;

  useCount: number;

  duration: number;

}

export interface AppUsageData {

  appId: string;

  appName: string;

  useCount: number;

  duration: number;

}

```

### 1.3 成就系统

```typescript

export interface Achievement {

  id: string;

  name: string;

  description: string;

  icon: string;

  unlocked: boolean;

  unlockedAt?: number;

  requirement: number;  // 达成条件

  current: number;      // 当前进度

}

```

## 二、统计工具类实现

```typescript

export class StatisticsUtil {

  private static readonly RECORDS_KEY = 'usage_records';

  private static readonly ACHIEVEMENTS_KEY = 'achievements';

  /**

   * 记录一次使用

   */

  static async recordUsage(appId: string, appName: string, duration: number): Promise<void> {

    const now = Date.now();

    const date = new Date(now).toISOString().split('T')[0];

   

    const record: UsageRecord = {

      id: `${now}_${Math.random().toString(36).substr(2, 9)}`,

      appId,

      appName,

      duration,

      timestamp: now,

      date

    };

    const records = await StatisticsUtil.getRecords();

    records.push(record);

    // 只保留最近30天的记录

    const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1000;

    const filteredRecords = records.filter(r => r.timestamp > thirtyDaysAgo);

    await PreferencesUtil.putString(StatisticsUtil.RECORDS_KEY, JSON.stringify(filteredRecords));

    await StatisticsUtil.checkAchievements();

  }

  /**

   * 获取统计数据

   */

  static async getStatistics(): Promise<StatisticsData> {

    const records = await StatisticsUtil.getRecords();

    const now = Date.now();

    const today = new Date(now).toISOString().split('T')[0];

    const weekAgo = now - 7 * 24 * 60 * 60 * 1000;

    // 总计

    const totalUseCount = records.length;

    const totalDuration = records.reduce((sum, r) => sum + r.duration, 0);

    // 今日

    const todayRecords = records.filter(r => r.date === today);

    const todayUseCount = todayRecords.length;

    const todayDuration = todayRecords.reduce((sum, r) => sum + r.duration, 0);

    // 周数据

    const weeklyData = StatisticsUtil.aggregateByDate(

      records.filter(r => r.timestamp > weekAgo)

    );

    // 应用使用统计

    const appUsage = StatisticsUtil.aggregateByApp(records);

    // 成就

    const achievements = await StatisticsUtil.getAchievements(totalUseCount, totalDuration);

    return {

      totalUseCount,

      totalDuration,

      todayUseCount,

      todayDuration,

      weeklyData,

      monthlyData: [],

      appUsage,

      achievements

    };

  }

  /**

   * 按日期聚合

   */

  private static aggregateByDate(records: UsageRecord[]): DailyData[] {

    const map = new Map<string, DailyData>();

   

    for (const record of records) {

      const existing = map.get(record.date);

      if (existing) {

        existing.useCount++;

        existing.duration += record.duration;

      } else {

        map.set(record.date, {

          date: record.date,

          useCount: 1,

          duration: record.duration

        });

      }

    }

   

    return Array.from(map.values()).sort((a, b) => a.date.localeCompare(b.date));

  }

  /**

   * 按应用聚合

   */

  private static aggregateByApp(records: UsageRecord[]): AppUsageData[] {

    const map = new Map<string, AppUsageData>();

   

    for (const record of records) {

      const existing = map.get(record.appId);

      if (existing) {

        existing.useCount++;

        existing.duration += record.duration;

      } else {

        map.set(record.appId, {

          appId: record.appId,

          appName: record.appName,

          useCount: 1,

          duration: record.duration

        });

      }

    }

   

    return Array.from(map.values()).sort((a, b) => b.useCount - a.useCount);

  }

}

```

## 三、成就系统实现

```typescript

private static readonly ACHIEVEMENT_DEFINITIONS: Achievement[] = [

  { id: 'first_use', name: '初次体验', description: '完成第一次使用', icon: '🎉', requirement: 1, unlocked: false, current: 0 },

  { id: 'use_10', name: '常客', description: '累计使用10次', icon: '⭐', requirement: 10, unlocked: false, current: 0 },

  { id: 'use_50', name: '忠实用户', description: '累计使用50次', icon: '🏆', requirement: 50, unlocked: false, current: 0 },

  { id: 'duration_1h', name: '时间投入', description: '累计使用1小时', icon: '⏰', requirement: 3600, unlocked: false, current: 0 },

  { id: 'duration_10h', name: '深度用户', description: '累计使用10小时', icon: '💎', requirement: 36000, unlocked: false, current: 0 },

];

static async checkAchievements(): Promise<void> {

  const records = await StatisticsUtil.getRecords();

  const totalCount = records.length;

  const totalDuration = records.reduce((sum, r) => sum + r.duration, 0);

  const achievements = await StatisticsUtil.loadAchievements();

  for (const achievement of achievements) {

    if (achievement.unlocked) continue;

   

    let current = 0;

    if (achievement.id.startsWith('use_') || achievement.id === 'first_use') {

      current = totalCount;

    } else if (achievement.id.startsWith('duration_')) {

      current = totalDuration;

    }

   

    achievement.current = current;

    if (current >= achievement.requirement) {

      achievement.unlocked = true;

      achievement.unlockedAt = Date.now();

    }

  }

  await PreferencesUtil.putString(StatisticsUtil.ACHIEVEMENTS_KEY, JSON.stringify(achievements));

}

```

## 四、页面展示

```typescript

@Entry

@Component

struct StatisticsPage {

  @State statistics: StatisticsData | null = null;

  aboutToAppear(): void {

    this.loadStatistics();

  }

  async loadStatistics(): Promise<void> {

    this.statistics = await StatisticsUtil.getStatistics();

  }

  build() {

    Column() {

      // 总览卡片

      Row() {

        Column() {

          Text(`${this.statistics?.totalUseCount || 0}`)

            .fontSize(32).fontWeight(FontWeight.Bold)

          Text('总使用次数').fontSize(12)

        }

        Column() {

          Text(this.formatDuration(this.statistics?.totalDuration || 0))

            .fontSize(32).fontWeight(FontWeight.Bold)

          Text('总使用时长').fontSize(12)

        }

      }

      // 成就列表

      Text('成就').fontSize(20).margin({ top: 20 })

      ForEach(this.statistics?.achievements || [], (item: Achievement) => {

        Row() {

          Text(item.icon).fontSize(24)

          Column() {

            Text(item.name).fontWeight(FontWeight.Bold)

            Text(item.description).fontSize(12)

          }

          Text(item.unlocked ? '✓' : `${item.current}/${item.requirement}`)

        }

      })

    }

  }

  formatDuration(seconds: number): string {

    if (seconds < 60) return `${seconds}秒`;

    if (seconds < 3600) return `${Math.floor(seconds / 60)}分`;

    return `${Math.floor(seconds / 3600)}时`;

  }

}

```

## 五、避坑指南

1. **数据清理**:定期清理过期数据,避免存储膨胀

2. **异步处理**:统计操作不要阻塞主线程

3. **隐私保护**:所有数据仅存储在本地

4. **性能优化**:大量数据时考虑分页加载

## 总结

本文实现了一个完整的应用使用统计系统,包括数据记录、统计分析和成就系统。这些数据可以帮助开发者了解用户行为,同时成就系统也能提升用户粘性。

---

**🎓 我的HarmonyOS开发课堂**:[点击进入课堂学习](https://developer.huawei.com/consumer/cn/training/classDetail/03d6a547f8124d35aab33fc40840288f?type=1?ha_source=hmosclass&ha_sourceId=89000248)

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐