鸿蒙掌上驾考宝典应用开发48:会员中心——应用内支付会员开通
2026/8/30 16:02:25 网站建设 项目流程

# 第48篇:会员中心——应用内支付会员开通

## 一、引言

会员功能是应用实现商业变现的重要方式。DriverLicenseExam 项目的 `membership` 组件实现了会员中心功能,包括会员权益展示、开通会员、会员专属内容控制等。本文将深入解析会员功能的设计与实现。

## 二、会员组件架构

### 2.1 组件结构

```
membership/
├── src/main/ets/
│ ├── components/ ← 会员 UI 组件
│ ├── model/ ← 会员数据模型
│ └── pages/ ← 会员页面
├── Index.ets ← 模块导出
└── oh-package.json5 ← 模块配置
```

### 2.2 会员功能流程

```
用户点击"会员中心"

├── 未登录 → 跳转到登录页面

└── 已登录 → 进入会员页面

├── 展示会员权益
├── 展示会员价格
└── 点击开通

├── 调用数字收银台支付

└── 支付成功 → 更新会员状态
```

## 三、会员入口实现

### 3.1 我的页面中的会员入口

会员入口位于 MineView 的用户信息模块下方:

```ets
// MineView.ets - 会员中心入口
Row() {
Text('会员中心')
.width(72).height(28)
.fontSize(14)
.borderRadius(14)
.fontColor('#fff')
.backgroundColor('rgba(100, 187, 92, 1)')
.textAlign(TextAlign.Center)
.maxFontScale(1)
.onClick(() => {
if (AccountUtil.getAccountInfo().idToken === '') {
// 未登录 → 跳转登录页
this.jumpLoginPage('a000******00');
} else {
// 已登录 → 进入会员中心
this.vm.navStack.pushPathByName('membership', true);
}
});
}
.justifyContent(FlexAlign.End)
.width('100%');
```

这个入口的设计体现了登录拦截逻辑:
- 未登录用户点击会员中心,先跳转到登录页面
- 已登录用户直接进入会员中心

### 3.2 登录拦截

```ets
private jumpLoginPage(quickLoginAnonymousPhone: string | undefined) {
if (quickLoginAnonymousPhone) {
const params: Record<string, Object> = { 'anonymousPhone': quickLoginAnonymousPhone };
this.vm.navStack.pushPathByName('QuickLoginPage', params, true);
} else {
const hint = (this.getUIContext().getHostContext() as Context)
.resourceManager.getStringSync($r('app.string.get_mobile_number_error').id);
showToast(hint, this.getUIContext());
}
}
```

## 四、会员权益控制

### 4.1 会员专属内容标识

在数据模型中,通过 `needVip` 字段标识会员专属内容:

```ets
// 视频数据模型中的会员标识
export interface Video {
poster: Resource;
url: string;
name: string;
simpleDescription: string;
description: string;
copyrightNotice: string;
time: string;
needVip?: boolean; // true = 会员专属,false/undefined = 公开
}
```

### 4.2 会员内容访问控制

```ets
// 视频列表中的会员控制
if (item.needVip && !this.isLoggedIn) {
showToast('开通会员后可观看', this.getUIContext());
return;
}

// 或者跳转到会员页面
if (item.needVip && !this.isLoggedIn) {
this.vm.navStack.pushPathByName('membership', true);
return;
}
```

### 4.3 会员状态管理

```ets
// 检查用户是否为会员
get isLoggedIn(): boolean {
return AccountUtil.getAccountInfo().idToken !== '';
}

// 会员状态可以通过服务端验证
async checkMembershipStatus(): Promise<boolean> {
try {
const response = await https.getUserInfo();
return response.data.isVIP;
} catch (error) {
return false;
}
}
```

## 五、会员数据模型

### 5.1 会员状态模型

```ets
// 会员数据模型
export interface MembershipInfo {
isVIP: boolean; // 是否为会员
expireDate: string; // 会员到期日期
plan: string; // 当前套餐:monthly/quarterly/yearly
purchaseDate: string; // 购买日期
autoRenew: boolean; // 是否自动续费
}

// 会员状态管理
export class MembershipManager {
private static _instance: MembershipManager;
@Trace membershipInfo: MembershipInfo = {
isVIP: false,
expireDate: '',
plan: '',
purchaseDate: '',
autoRenew: false,
};

public static get instance(): MembershipManager {
if (!MembershipManager._instance) {
MembershipManager._instance = new MembershipManager();
}
return MembershipManager._instance;
}

// 检查会员是否有效
isMembershipValid(): boolean {
if (!this.membershipInfo.isVIP) return false;
if (!this.membershipInfo.expireDate) return false;
const expire = new Date(this.membershipInfo.expireDate);
return expire > new Date();
}

// 获取剩余天数
getRemainingDays(): number {
if (!this.isMembershipValid()) return 0;
const expire = new Date(this.membershipInfo.expireDate);
const now = new Date();
return Math.ceil((expire.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
}
}
```

### 5.2 会员与本地存储

会员状态需要持久化存储,避免每次打开应用都重新查询:

```ets
// 保存会员信息到 Preferences
saveMembershipInfo(info: MembershipInfo) {
const preferencesUtil = PreferencesUtil.getInstance();
const preference = preferencesUtil.getPreferences(context);
preferencesUtil.preferencesPut(preference, 'membershipInfo', JSON.stringify(info));
}

// 从 Preferences 读取会员信息
loadMembershipInfo(): MembershipInfo | null {
const preferencesUtil = PreferencesUtil.getInstance();
const preference = preferencesUtil.getPreferences(context);
const value = preferencesUtil.getPreferencesValue(preference, 'membershipInfo');
if (value) {
return JSON.parse(value as string) as MembershipInfo;
}
return null;
}
```

## 六、会员页面设计

### 5.1 会员权益展示

```ets
// MemberShip.ets
@ComponentV2
export struct MemberShip {
@Local isVIP: boolean = false;

aboutToAppear() {
this.checkVIPStatus();
}

build() {
NavDestination() {
Column() {
// 会员标题
Column() {
Image($r('app.media.vip_icon')).width(80).height(80);
Text('驾考会员').fontSize(24).fontWeight(FontWeight.Bold);
Text('无限次模拟考试 + 全部视频课程')
.fontSize(14).fontColor('#666');
}
.padding({ top: 40, bottom: 24 });

// 会员权益列表
Column({ space: 12 }) {
this.benefitItem('全部视频课程免费观看', $r('app.media.ic_video'));
this.benefitItem('无限次模拟考试', $r('app.media.ic_exam'));
this.benefitItem('错题本智能分析', $r('app.media.ic_analysis'));
this.benefitItem('去广告纯净体验', $r('app.media.ic_noad'));
}
.padding(24);

// 开通按钮
Button('立即开通 ¥29.9/月')
.width('80%')
.height(48)
.backgroundColor('#64BB5C')
.borderRadius(24)
.fontColor('#fff')
.fontSize(16)
.margin({ top: 24 })
.onClick(() => {
this.purchaseMembership();
});
}
.width('100%')
.height('100%');
}
.title('会员中心');
}

@Builder
benefitItem(label: string, icon: Resource) {
Row() {
Image(icon).width(24).height(24);
Text(label).fontSize(16).margin({ left: 12 });
}
.width('100%');
}
}
```

### 5.2 支付流程

```ets
// 开通会员 - 调用数字收银台
purchaseMembership() {
// 1. 创建支付订单
// 2. 调用数字收银台支付
// 3. 支付成功回调
// 4. 更新会员状态
showToast('开通成功,欢迎成为会员!', this.getUIContext());
this.isVIP = true;
this.vm.navStack.pop();
}
```

## 六、会员功能的扩展性设计

### 6.1 会员权益配置化

会员权益可以通过配置方式管理,便于后续调整:

```ets
// 会员权益配置
const MEMBERSHIP_BENEFITS = [
{ label: '全部视频课程免费观看', icon: 'ic_video', needVIP: true },
{ label: '无限次模拟考试', icon: 'ic_exam', needVIP: true },
{ label: '错题本智能分析', icon: 'ic_analysis', needVIP: true },
{ label: '去广告纯净体验', icon: 'ic_noad', needVIP: true },
];

// 判断用户是否有权限访问某个功能
function hasAccess(benefit: string): boolean {
const config = MEMBERSHIP_BENEFITS.find(b => b.label === benefit);
if (!config) return true;
if (!config.needVIP) return true;
return isVIP;
}
```

### 6.2 多套餐支持

会员功能可以扩展为多套餐模式:

```ets
interface MembershipPlan {
name: string; // 套餐名称
price: number; // 价格
duration: number; // 有效期(天)
benefits: string[]; // 权益列表
}

const PLANS: MembershipPlan[] = [
{ name: '月度会员', price: 29.9, duration: 30, benefits: ['全部视频', '模拟考试', '去广告'] },
{ name: '季度会员', price: 69.9, duration: 90, benefits: ['全部视频', '模拟考试', '去广告', '智能分析'] },
{ name: '年度会员', price: 199.9, duration: 365, benefits: ['全部权益'] },
];
```

## 七、总结

会员中心组件实现了完整的会员功能闭环:

1. **入口控制**:登录拦截,确保只有登录用户可访问
2. **权益展示**:会员权益列表和价格展示
3. **支付集成**:调用数字收银台完成支付
4. **内容控制**:通过 needVip 字段控制会员专属内容
5. **扩展性**:支持多套餐、权益配置化

---

**关键源码文件:**
- `components/membership/src/main/ets/` — 会员组件
- `products/entry/src/main/ets/pages/mine/MemberShip.ets` — 会员页面
- `products/entry/src/main/ets/pages/mine/MineView.ets` — 会员入口
- `commons/datasource/src/main/ets/Model.ets` — Video 模型(needVip 字段)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询