基于HarmonyOS Next的体育类应用开发实战:从零构建篮球赛事管理系统

一、项目概述与开发环境准备

随着全民健身热潮的兴起,体育类应用在移动端的需求日益增长。本教程将带领开发者使用HarmonyOS Next的AppGallery Connect服务,开发一个功能完整的篮球赛事管理系统。该系统将包含赛事发布、球队管理、球员数据统计等核心功能,全面展示鸿蒙生态下体育类应用的开发流程。

开发环境要求:

  • 安装最新版DevEco Studio(建议4.0及以上版本)
  • 配置HarmonyOS SDK(API Version 9+)
  • 注册华为开发者账号并开通AppGallery Connect服务

项目技术栈:

  • 前端:ArkTS + 声明式UI
  • 后端:AppGallery Connect的云数据库、云函数、认证服务
  • 数据统计:AppGallery Connect的分析服务

二、项目初始化与基础配置

首先在DevEco Studio中创建新项目,选择"Application"模板,设备类型选择"Phone",语言选择ArkTS:

// 项目入口文件:EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  // 应用启动时创建
  onCreate(want, launchParam) {
    console.info('BasketballApp onCreate');
    // 初始化AppGallery Connect服务
    this.initAGC();
  }

  private async initAGC() {
    try {
      // 引入AGC核心模块
      const agconnect = await import('@hw-agconnect/core-ohos');
      // 使用项目配置初始化AGC
      agconnect.default.instance().configInstance(
        this.context, 
        {
          api_key: "您的API_KEY",
          client_id: "您的CLIENT_ID",
          client_secret: "您的CLIENT_SECRET",
          project_id: "您的PROJECT_ID",
          package_name: "com.example.basketballapp"
        }
      );
      console.info('AGC初始化成功');
    } catch (error) {
      console.error('AGC初始化失败:', error);
    }
  }

  // 窗口创建时调用
  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err) {
        console.error('加载页面失败:', err);
        return;
      }
      console.info('页面加载成功:', data);
    });
  }
}

resources/base/profile/main_pages.json中配置应用路由:

{
  "src": [
    "pages/Index",
    "pages/Login",
    "pages/Home",
    "pages/Matches",
    "pages/Teams",
    "pages/Players",
    "pages/Stats"
  ]
}

三、用户认证模块实现

体育类应用通常需要用户系统来管理不同角色的权限。我们使用AppGallery Connect的认证服务来实现这一功能。

1. 配置认证服务

在AppGallery Connect控制台启用认证服务,选择"手机号码"和"邮箱地址"作为登录方式。

2. 实现登录页面

// pages/Login.ets
import { LoginButton, LoginType } from '@hw-agconnect/auth-ohos';
import router from '@ohos.router';

@Entry
@Component
struct LoginPage {
  @State phoneNumber: string = '';
  @State verifyCode: string = '';
  @State isCounting: boolean = false;
  @State countdown: number = 60;

  build() {
    Column() {
      Image($r('app.media.logo'))
        .width(120)
        .height(120)
        .margin({ top: 40, bottom: 30 })

      TextInput({ placeholder: '请输入手机号码' })
        .type(InputType.Number)
        .maxLength(11)
        .height(50)
        .width('80%')
        .onChange((value: string) => {
          this.phoneNumber = value;
        })

      Row() {
        TextInput({ placeholder: '验证码' })
          .type(InputType.Number)
          .maxLength(6)
          .height(50)
          .width('60%')
          .onChange((value: string) => {
            this.verifyCode = value;
          })

        Button(this.isCounting ? `${this.countdown}s后重试` : '获取验证码')
          .width('35%')
          .height(50)
          .margin({ left: 10 })
          .enabled(!this.isCounting && this.phoneNumber.length === 11)
          .onClick(() => {
            this.sendVerifyCode();
          })
      }
      .width('80%')
      .margin({ top: 20 })

      Button('登录')
        .type(ButtonType.Capsule)
        .width('80%')
        .height(50)
        .margin({ top: 40 })
        .enabled(this.phoneNumber.length === 11 && this.verifyCode.length === 6)
        .onClick(() => {
          this.loginWithPhone();
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
  }

  // 发送验证码
  private sendVerifyCode() {
    const auth = require('@hw-agconnect/auth-ohos').default;
    const agconnect = require('@hw-agconnect/core-ohos').default;

    auth.getInstance(agconnect.instance())
      .requestPhoneVerifyCode(this.phoneNumber)
      .then(() => {
        console.info('验证码发送成功');
        this.startCountdown();
      })
      .catch((err: Error) => {
        console.error('验证码发送失败:', err);
      });
  }

  // 倒计时逻辑
  private startCountdown() {
    this.isCounting = true;
    const timer = setInterval(() => {
      if (this.countdown <= 0) {
        clearInterval(timer);
        this.isCounting = false;
        this.countdown = 60;
        return;
      }
      this.countdown--;
    }, 1000);
  }

  // 手机号+验证码登录
  private loginWithPhone() {
    const auth = require('@hw-agconnect/auth-ohos').default;
    const agconnect = require('@hw-agconnect/core-ohos').default;

    const credential = auth.PhoneAuthProvider.credentialWithVerifyCode(
      this.phoneNumber,
      '',
      this.verifyCode
    );

    auth.getInstance(agconnect.instance())
      .signIn(credential)
      .then((user: any) => {
        console.info('登录成功:', user);
        router.replace({ url: 'pages/Home' });
      })
      .catch((err: Error) => {
        console.error('登录失败:', err);
      });
  }
}

四、赛事管理模块开发

赛事管理是体育类应用的核心功能,我们将使用AppGallery Connect的云数据库来存储赛事数据。

1. 创建数据模型

在AppGallery Connect控制台创建matches集合,包含以下字段:

  • matchId: 字符串,主键
  • homeTeam: 字符串,主队名称
  • awayTeam: 字符串,客队名称
  • startTime: 时间戳,比赛开始时间
  • location: 字符串,比赛地点
  • status: 数字,比赛状态(0未开始,1进行中,2已结束)

2. 实现赛事列表页面

// pages/Matches.ets
import { CloudDBZoneWrapper } from '../model/CloudDBZoneWrapper';
import router from '@ohos.router';

@Entry
@Component
struct MatchesPage {
  private cloudDBZoneWrapper: CloudDBZoneWrapper = new CloudDBZoneWrapper();
  @State matches: Array<any> = [];
  @State isLoading: boolean = true;

  aboutToAppear() {
    this.queryMatches();
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('赛事管理')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        
        Blank()
        
        Button('+')
          .type(ButtonType.Circle)
          .width(40)
          .height(40)
          .onClick(() => {
            router.push({ url: 'pages/AddMatch' });
          })
      }
      .width('90%')
      .margin({ top: 15, bottom: 15 })

      // 加载状态
      if (this.isLoading) {
        LoadingProgress()
          .color(Color.Blue)
          .margin({ top: 50 })
      }

      // 赛事列表
      List({ space: 10 }) {
        ForEach(this.matches, (match: any) => {
          ListItem() {
            MatchItem({ match: match })
          }
        }, (match: any) => match.matchId)
      }
      .width('100%')
      .layoutWeight(1)
      .divider({ strokeWidth: 1, color: '#F1F1F1' })
    }
    .width('100%')
    .height('100%')
  }

  // 查询赛事数据
  private queryMatches() {
    this.cloudDBZoneWrapper.queryMatches()
      .then((matches: Array<any>) => {
        this.matches = matches;
        this.isLoading = false;
      })
      .catch((err: Error) => {
        console.error('查询赛事失败:', err);
        this.isLoading = false;
      });
  }
}

// 赛事项组件
@Component
struct MatchItem {
  private match: any;

  build() {
    Row() {
      Column() {
        Text(`${this.match.homeTeam} vs ${this.match.awayTeam}`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
        
        Text(`时间: ${new Date(this.match.startTime).toLocaleString()}`)
          .fontSize(14)
          .margin({ top: 5 })
        
        Text(`地点: ${this.match.location}`)
          .fontSize(14)
          .margin({ top: 2 })
      }
      .layoutWeight(1)

      // 根据状态显示不同标签
      if (this.match.status === 0) {
        Text('未开始')
          .fontColor('#666')
      } else if (this.match.status === 1) {
        Text('进行中')
          .fontColor('#FF9900')
      } else {
        Text('已结束')
          .fontColor('#999')
      }
    }
    .padding(15)
    .width('100%')
    .onClick(() => {
      router.push({ url: 'pages/MatchDetail', params: { matchId: this.match.matchId } });
    })
  }
}

3. 实现云数据库操作类

// model/CloudDBZoneWrapper.ts
import { cloud } from '@hw-agconnect/database-ohos';
import agconnect from '@hw-agconnect/core-ohos';

export class CloudDBZoneWrapper {
  private cloudDBZone: cloud.CloudDBZone;

  constructor() {
    // 初始化云数据库区域
    const config = {
      name: 'BasketballDBZone',  // 自定义区域名称
      persistenceEnabled: true,  // 启用持久化
      encryptionKey: ''          // 加密密钥(可选)
    };
    
    const agcCloudDB = cloud.CloudDBZoneManager.getInstance(agconnect.instance());
    this.cloudDBZone = agcCloudDB.openCloudDBZone(config);
  }

  // 查询所有赛事
  queryMatches(): Promise<Array<any>> {
    return new Promise((resolve, reject) => {
      const query = cloud.CloudDBZoneQuery.where('matches')
        .orderByDesc('startTime')
        .limit(100);

      this.cloudDBZone.executeQuery(query)
        .then((snapshot: cloud.CloudDBZoneSnapshot) => {
          const matches: Array<any> = [];
          while (snapshot.hasNext()) {
            matches.push(snapshot.next());
          }
          resolve(matches);
        })
        .catch((err: Error) => {
          reject(err);
        });
    });
  }

  // 添加新赛事
  addMatch(match: any): Promise<void> {
    return new Promise((resolve, reject) => {
      this.cloudDBZone.executeUpsert([match])
        .then(() => {
          resolve();
        })
        .catch((err: Error) => {
          reject(err);
        });
    });
  }

  // 更新赛事状态
  updateMatchStatus(matchId: string, status: number): Promise<void> {
    return new Promise((resolve, reject) => {
      const query = cloud.CloudDBZoneQuery.where('matches')
        .equalTo('matchId', matchId);

      this.cloudDBZone.executeQuery(query)
        .then((snapshot: cloud.CloudDBZoneSnapshot) => {
          if (snapshot.hasNext()) {
            const match = snapshot.next();
            match.status = status;
            return this.cloudDBZone.executeUpsert([match]);
          } else {
            throw new Error('未找到对应赛事');
          }
        })
        .then(() => {
          resolve();
        })
        .catch((err: Error) => {
          reject(err);
        });
    });
  }
}

五、数据统计与分析

体育类应用需要对比赛数据进行统计分析。我们将使用AppGallery Connect的分析服务来实现这一功能。

1. 配置分析服务

在AppGallery Connect控制台启用分析服务,创建自定义事件:

  • match_view: 赛事查看
  • stat_record: 数据记录
  • player_performance: 球员表现

2. 实现数据统计功能

// utils/AnalyticsUtil.ts
import agconnect from '@hw-agconnect/core-ohos';

export class AnalyticsUtil {
  private static instance: AnalyticsUtil;
  private analytics: any;

  private constructor() {
    this.analytics = require('@hw-agconnect/analytics-ohos').default;
  }

  public static getInstance(): AnalyticsUtil {
    if (!AnalyticsUtil.instance) {
      AnalyticsUtil.instance = new AnalyticsUtil();
    }
    return AnalyticsUtil.instance;
  }

  // 记录赛事查看事件
  public logMatchView(matchId: string) {
    const eventData = {
      match_id: matchId,
      timestamp: new Date().getTime()
    };
    this.analytics.instance(agconnect.instance())
      .onEvent('match_view', eventData);
  }

  // 记录比赛数据
  public logMatchStats(matchId: string, stats: any) {
    const eventData = {
      match_id: matchId,
      ...stats
    };
    this.analytics.instance(agconnect.instance())
      .onEvent('stat_record', eventData);
  }

  // 记录球员表现
  public logPlayerPerformance(playerId: string, performance: any) {
    const eventData = {
      player_id: playerId,
      ...performance
    };
    this.analytics.instance(agconnect.instance())
      .onEvent('player_performance', eventData);
  }

  // 获取热门赛事数据
  public getPopularMatches(): Promise<Array<any>> {
    return new Promise((resolve, reject) => {
      // 这里可以调用分析服务的API获取数据
      // 实际开发中需要根据分析服务的具体API实现
      resolve([]);
    });
  }
}

3. 在赛事详情页使用分析服务

// pages/MatchDetail.ets
import { AnalyticsUtil } from '../utils/AnalyticsUtil';
import router from '@ohos.router';

@Entry
@Component
struct MatchDetailPage {
  @State match: any = {};
  @State isLoading: boolean = true;

  aboutToAppear() {
    const params = router.getParams() as Record<string, string>;
    const matchId = params['matchId'];
    
    if (matchId) {
      this.fetchMatchDetail(matchId);
      // 记录赛事查看事件
      AnalyticsUtil.getInstance().logMatchView(matchId);
    }
  }

  build() {
    Column() {
      // 页面内容...
    }
  }

  private fetchMatchDetail(matchId: string) {
    // 获取赛事详情逻辑...
  }
}

六、应用打包与发布

完成开发后,我们需要将应用打包并发布到AppGallery。

1. 配置应用签名

在DevEco Studio中:

  1. 选择"File" > "Project Structure" > "Project" > "Signing Configs"
  2. 配置签名证书信息
  3. 勾选"Automatically generate signing"

2. 构建发布版本

  1. 选择"Build" > "Build Haps"
  2. 选择"Release"模式
  3. 等待构建完成

3. 上传到AppGallery Connect

  1. 登录AppGallery Connect控制台
  2. 选择"我的应用" > "添加应用"
  3. 填写应用信息并上传构建好的HAP文件
  4. 提交审核

七、总结与扩展

通过本教程,我们完成了一个基于HarmonyOS Next的篮球赛事管理系统的核心功能开发。系统包含了用户认证、赛事管理、数据统计等体育类应用的典型功能模块。

扩展建议:

  1. 可以集成华为地图服务,实现比赛地点的精确定位和导航
  2. 添加直播功能,使用华为的视频服务实现比赛直播
  3. 开发穿戴设备版本,实时监测运动员的运动数据
  4. 使用华为的机器学习服务,对比赛数据进行分析预测

林钟雪
4 声望0 粉丝