HarmonyOS开发:家庭共享——多用户管理
HarmonyOS开发:家庭共享——多用户管理
📌 核心要点:家庭共享让全家人一起用智能家居,创建家庭、邀请成员、角色权限控制,共享设备和场景,成员数据隔离互不干扰。
背景与动机
你家有5口人,5部手机,30个智能设备。你配好了所有设备,设好了场景和定时。然后呢?
你老婆打开App,一个设备都看不到——因为设备绑在你的账号下,她的账号里是空的。你爸打开App,也是空的。你妈打开App,还是空的。每个人都要重新配网、重新设场景?那智能家居也太不智能了。
家庭共享就是解决这个问题的。 创建一个"家庭",把设备放到家庭里,家庭成员都能看到和控制这些设备。你配好设备,邀请家人加入家庭,全家共享。
但家庭共享做起来有几个关键问题:谁有权限添加设备?谁能删除设备?小孩能不能控制空调?家庭成员退出家庭后,他创建的场景归谁?两个家庭的设备能不能互看?
这些问题不解决,家庭共享就是"共享了个寂寞"。
核心原理
家庭模型
家庭是设备、成员、场景的容器。一个用户可以属于多个家庭(自己家、父母家),但一个设备只能属于一个家庭。
flowchart TD
A[家庭 Home] --> B[成员 Members]
A --> C[设备 Devices]
A --> D[场景 Scenes]
A --> E[定时任务 Timers]
B --> F[管理员 Admin]
B --> G[普通成员 Member]
B --> H[受限成员 Restricted]
F --> I[全部权限:添加/删除设备、邀请/移除成员、管理场景]
G --> J[控制权限:控制设备、创建个人场景]
H --> K[受限权限:仅控制指定设备]
C --> L[共享设备:所有成员可见可控制]
D --> M[共享场景:所有成员可见可执行]
D --> N[个人场景:仅创建者可见]
classDef home fill:#1565C0,color:#fff,stroke:#0D47A1
classDef member fill:#2E7D32,color:#fff,stroke:#1B5E20
classDef device fill:#E65100,color:#fff,stroke:#BF360C
classDef perm fill:#6A1B9A,color:#fff,stroke:#4A148C
class A,home
class B,F,G,H,member
class C,D,E,L,M,N,device
class I,J,K,perm
角色权限
| 权限 | 管理员 | 普通成员 | 受限成员 |
|---|---|---|---|
| 查看设备 | ✅ | ✅ | 仅指定设备 |
| 控制设备 | ✅ | ✅ | 仅指定设备 |
| 添加设备 | ✅ | ❌ | ❌ |
| 删除设备 | ✅ | ❌ | ❌ |
| 创建场景 | ✅ | ✅(个人场景) | ❌ |
| 管理共享场景 | ✅ | ❌ | ❌ |
| 邀请成员 | ✅ | ❌ | ❌ |
| 移除成员 | ✅ | ❌ | ❌ |
| 修改角色 | ✅ | ❌ | ❌ |
成员邀请流程
- 管理员生成邀请码/链接
- 新成员输入邀请码或点击链接
- 新成员确认加入家庭
- 管理员设置新成员角色
- 新成员获得对应权限
数据隔离
家庭成员的数据需要隔离:
- 共享数据:设备、共享场景、定时任务——所有成员可见
- 个人数据:个人场景、个人偏好、语音指令历史——仅创建者可见
- 管理数据:成员列表、角色权限、邀请记录——仅管理员可见
代码实战
基础用法:家庭管理服务
家庭的创建、成员管理、权限控制。
// services/FamilyService.ets
// 家庭管理服务 - 创建家庭、邀请成员、权限控制
// 家庭信息
interface Family {
familyId: string
name: string
ownerId: string // 家庭创建者ID
createdAt: number
memberCount: number
deviceCount: number
}
// 家庭成员
interface FamilyMember {
userId: string
userName: string
avatar?: string
role: 'admin' | 'member' | 'restricted'
joinedAt: number
// 受限成员的可控设备列表
allowedDevices?: string[]
}
// 邀请信息
interface FamilyInvitation {
inviteId: string
familyId: string
familyName: string
inviterName: string
inviteCode: string // 6位邀请码
role: 'admin' | 'member' | 'restricted'
expiresAt: number // 过期时间
usedAt?: number // 使用时间
}
// 权限检查结果
interface PermissionCheck {
allowed: boolean
reason?: string
}
class FamilyService {
// 当前家庭
private currentFamily?: Family
// 家庭成员列表
private members: Map<string, FamilyMember> = new Map()
// 当前用户ID(模拟)
private currentUserId: string = 'user_001'
// 创建家庭
async createFamily(name: string): Promise<Family> {
const family: Family = {
familyId: `family_${Date.now()}`,
name: name,
ownerId: this.currentUserId,
createdAt: Date.now(),
memberCount: 1,
deviceCount: 0
}
this.currentFamily = family
// 创建者自动成为管理员
this.members.set(this.currentUserId, {
userId: this.currentUserId,
userName: '我',
role: 'admin',
joinedAt: Date.now()
})
// 实际项目中:上传到服务端
return family
}
// 生成邀请码
async generateInviteCode(role: 'admin' | 'member' | 'restricted'): Promise<FamilyInvitation> {
if (!this.currentFamily) {
throw new Error('请先创建或加入家庭')
}
// 检查权限:只有管理员可以邀请
const myRole = this.members.get(this.currentUserId)?.role
if (myRole !== 'admin') {
throw new Error('只有管理员可以邀请成员')
}
// 生成6位随机邀请码
const inviteCode = Math.random().toString(36).substring(2, 8).toUpperCase()
const invitation: FamilyInvitation = {
inviteId: `invite_${Date.now()}`,
familyId: this.currentFamily.familyId,
familyName: this.currentFamily.name,
inviterName: this.members.get(this.currentUserId)?.userName || '',
inviteCode: inviteCode,
role: role,
expiresAt: Date.now() + 24 * 60 * 60 * 1000 // 24小时有效
}
return invitation
}
// 通过邀请码加入家庭
async joinFamily(inviteCode: string): Promise<Family> {
// 实际项目中:调用服务端验证邀请码
// 这里简化处理
const family: Family = {
familyId: 'family_joined',
name: '我的家',
ownerId: 'user_other',
createdAt: Date.now(),
memberCount: 2,
deviceCount: 5
}
this.currentFamily = family
this.members.set(this.currentUserId, {
userId: this.currentUserId,
userName: '新成员',
role: 'member',
joinedAt: Date.now()
})
return family
}
// 移除成员
async removeMember(userId: string): Promise<boolean> {
const myRole = this.members.get(this.currentUserId)?.role
if (myRole !== 'admin') {
return false
}
// 不能移除自己
if (userId === this.currentUserId) {
return false
}
// 不能移除其他管理员
const targetRole = this.members.get(userId)?.role
if (targetRole === 'admin') {
return false
}
this.members.delete(userId)
return true
}
// 修改成员角色
async updateMemberRole(userId: string, newRole: 'admin' | 'member' | 'restricted'): Promise<boolean> {
const myRole = this.members.get(this.currentUserId)?.role
if (myRole !== 'admin') return false
const member = this.members.get(userId)
if (!member) return false
member.role = newRole
return true
}
// 设置受限成员的可控设备
async setAllowedDevices(userId: string, deviceIds: string[]): Promise<boolean> {
const myRole = this.members.get(this.currentUserId)?.role
if (myRole !== 'admin') return false
const member = this.members.get(userId)
if (!member || member.role !== 'restricted') return false
member.allowedDevices = deviceIds
return true
}
// 检查权限
checkPermission(action: string, deviceId?: string): PermissionCheck {
const myRole = this.members.get(this.currentUserId)?.role
if (myRole === 'admin') {
return { allowed: true }
}
if (myRole === 'member') {
// 普通成员:可以控制设备,不能管理
const memberActions = ['control_device', 'view_device', 'create_personal_scene', 'view_scene']
if (memberActions.includes(action)) {
return { allowed: true }
}
return { allowed: false, reason: '您没有此操作权限' }
}
if (myRole === 'restricted') {
// 受限成员:只能控制指定设备
const member = this.members.get(this.currentUserId)
if (action === 'control_device' || action === 'view_device') {
if (deviceId && member?.allowedDevices?.includes(deviceId)) {
return { allowed: true }
}
return { allowed: false, reason: '您没有该设备的控制权限' }
}
return { allowed: false, reason: '您的权限受限' }
}
return { allowed: false, reason: '未知角色' }
}
// 获取当前家庭
getCurrentFamily(): Family | undefined {
return this.currentFamily
}
// 获取成员列表
getMembers(): FamilyMember[] {
return Array.from(this.members.values())
}
// 退出家庭
async leaveFamily(): Promise<boolean> {
if (!this.currentFamily) return false
// 管理员退出需要先转让管理员角色
const myRole = this.members.get(this.currentUserId)?.role
if (myRole === 'admin' && this.members.size > 1) {
return false // 需要先转让
}
// 如果是最后一个成员,解散家庭
this.members.delete(this.currentUserId)
if (this.members.size === 0) {
this.currentFamily = undefined
}
return true
}
}
export default new FamilyService()
export type { Family, FamilyMember, FamilyInvitation }
进阶用法:家庭共享数据管理
管理家庭内的共享设备、共享场景、个人场景的数据隔离。
// services/FamilyDataManager.ets
// 家庭共享数据管理 - 设备共享、场景共享、数据隔离
import FamilyService from './FamilyService'
// 场景类型
type SceneScope = 'shared' | 'personal'
// 家庭场景
interface FamilyScene {
sceneId: string
name: string
scope: SceneScope // shared=共享场景,personal=个人场景
creatorId: string // 创建者ID
actions: Array<{
deviceId: string
property: string
value: Object
}>
enabled: boolean
}
class FamilyDataManager {
// 家庭共享设备列表
private sharedDevices: Map<string, {
deviceId: string
name: string
typeId: string
addedBy: string
roomName: string
}> = new Map()
// 家庭场景列表
private scenes: Map<string, FamilyScene> = new Map()
// 添加设备到家庭
async addDeviceToFamily(deviceId: string, name: string, typeId: string, roomName: string): Promise<boolean> {
// 检查权限
const perm = FamilyService.checkPermission('add_device')
if (!perm.allowed) return false
this.sharedDevices.set(deviceId, {
deviceId,
name,
typeId,
addedBy: 'user_001',
roomName
})
return true
}
// 从家庭移除设备
async removeDeviceFromFamily(deviceId: string): Promise<boolean> {
const perm = FamilyService.checkPermission('remove_device')
if (!perm.allowed) return false
this.sharedDevices.delete(deviceId)
// 同时移除所有场景中涉及该设备的动作
this.scenes.forEach((scene) => {
scene.actions = scene.actions.filter(a => a.deviceId !== deviceId)
})
return true
}
// 获取用户可见的设备列表
getVisibleDevices(): Array<{ deviceId: string, name: string, typeId: string, roomName: string }> {
const myRole = FamilyService.getMembers().find(
m => m.userId === 'user_001'
)?.role
if (myRole === 'restricted') {
// 受限成员只能看到允许的设备
const member = FamilyService.getMembers().find(m => m.userId === 'user_001')
const allowedIds = member?.allowedDevices || []
return Array.from(this.sharedDevices.values())
.filter(d => allowedIds.includes(d.deviceId))
}
// 管理员和普通成员可以看到所有设备
return Array.from(this.sharedDevices.values())
}
// 创建场景
async createScene(name: string, scope: SceneScope, actions: Array<{
deviceId: string
property: string
value: Object
}>): Promise<FamilyScene> {
// 共享场景需要管理员权限
if (scope === 'shared') {
const perm = FamilyService.checkPermission('create_shared_scene')
if (!perm.allowed) {
throw new Error('只有管理员可以创建共享场景')
}
}
const scene: FamilyScene = {
sceneId: `scene_${Date.now()}`,
name,
scope,
creatorId: 'user_001',
actions,
enabled: true
}
this.scenes.set(scene.sceneId, scene)
return scene
}
// 获取用户可见的场景
getVisibleScenes(): FamilyScene[] {
const currentUserId = 'user_001'
return Array.from(this.scenes.values()).filter(scene => {
// 共享场景所有人可见
if (scene.scope === 'shared') return true
// 个人场景只有创建者可见
if (scene.scope === 'personal') return scene.creatorId === currentUserId
return false
})
}
// 删除场景
async deleteScene(sceneId: string): Promise<boolean> {
const scene = this.scenes.get(sceneId)
if (!scene) return false
const currentUserId = 'user_001'
// 共享场景只有管理员和创建者可以删除
if (scene.scope === 'shared') {
const perm = FamilyService.checkPermission('delete_shared_scene')
if (!perm.allowed && scene.creatorId !== currentUserId) return false
}
// 个人场景只有创建者可以删除
if (scene.scope === 'personal' && scene.creatorId !== currentUserId) return false
this.scenes.delete(sceneId)
return true
}
}
export default new FamilyDataManager()
完整示例:家庭管理页面
家庭创建、成员邀请、角色管理、权限设置的完整UI。
// pages/FamilyManagePage.ets
// 家庭管理页面
import FamilyService, { Family, FamilyMember, FamilyInvitation } from '../services/FamilyService'
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct FamilyManagePage {
@State family: Family | null = null
@State members: FamilyMember[] = []
@State showInviteDialog: boolean = false
@State inviteCode: string = ''
@State inviteRole: string = 'member'
@State showJoinDialog: boolean = false
@State joinCode: string = ''
@State showCreateDialog: boolean = false
@State newFamilyName: string = ''
aboutToAppear() {
this.family = FamilyService.getCurrentFamily() || null
this.members = FamilyService.getMembers()
}
// 创建家庭
async createFamily() {
if (!this.newFamilyName.trim()) {
promptAction.showToast({ message: '请输入家庭名称' })
return
}
this.family = await FamilyService.createFamily(this.newFamilyName)
this.members = FamilyService.getMembers()
this.showCreateDialog = false
this.newFamilyName = ''
promptAction.showToast({ message: '家庭创建成功' })
}
// 生成邀请码
async generateInvite() {
try {
const invitation = await FamilyService.generateInviteCode(
this.inviteRole as 'admin' | 'member' | 'restricted'
)
this.inviteCode = invitation.inviteCode
} catch (err) {
promptAction.showToast({ message: (err as Error).message })
}
}
// 加入家庭
async joinFamily() {
if (!this.joinCode.trim()) {
promptAction.showToast({ message: '请输入邀请码' })
return
}
try {
this.family = await FamilyService.joinFamily(this.joinCode)
this.members = FamilyService.getMembers()
this.showJoinDialog = false
promptAction.showToast({ message: '加入成功' })
} catch (err) {
promptAction.showToast({ message: '邀请码无效或已过期' })
}
}
// 移除成员
async removeMember(userId: string) {
const success = await FamilyService.removeMember(userId)
if (success) {
this.members = FamilyService.getMembers()
promptAction.showToast({ message: '已移除成员' })
} else {
promptAction.showToast({ message: '移除失败' })
}
}
// 修改角色
async changeRole(userId: string, role: string) {
const success = await FamilyService.updateMemberRole(
userId, role as 'admin' | 'member' | 'restricted'
)
if (success) {
this.members = FamilyService.getMembers()
}
}
// 获取角色标签
getRoleLabel(role: string): string {
const labels: Record<string, string> = {
'admin': '管理员',
'member': '成员',
'restricted': '受限成员'
}
return labels[role] || role
}
// 获取角色颜色
getRoleColor(role: string): string {
const colors: Record<string, string> = {
'admin': '#007DFF',
'member': '#4CAF50',
'restricted': '#FF9800'
}
return colors[role] || '#999999'
}
build() {
Navigation() {
Column() {
if (this.family) {
this.FamilyInfoSection()
this.MemberListSection()
} else {
this.NoFamilySection()
}
}
.width('100%')
.height('100%')
.backgroundColor('#F8F8F8')
}
.title('家庭管理')
.titleMode(NavigationTitleMode.Mini)
.bindContentCover(this.showInviteDialog, this.InviteDialog(), {
modalTransition: ModalTransition.DEFAULT
})
.bindContentCover(this.showJoinDialog, this.JoinDialog(), {
modalTransition: ModalTransition.DEFAULT
})
.bindContentCover(this.showCreateDialog, this.CreateDialog(), {
modalTransition: ModalTransition.DEFAULT
})
}
// 家庭信息
@Builder FamilyInfoSection() {
Column() {
Row() {
Column() {
Text(this.family!.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(`${this.members.length}位成员`)
.fontSize(14)
.fontColor('#999999')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
Blank()
// 邀请成员按钮
Button('邀请成员')
.height(36)
.fontSize(13)
.fontColor('#007DFF')
.backgroundColor('#F0F7FF')
.borderRadius(18)
.onClick(() => { this.showInviteDialog = true })
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
.padding({ left: 16, right: 16, top: 16 })
}
// 成员列表
@Builder MemberListSection() {
Column() {
Text('家庭成员')
.fontSize(14)
.fontColor('#999999')
.margin({ bottom: 8 })
List({ space: 8 }) {
ForEach(this.members, (member: FamilyMember) => {
ListItem() {
Row() {
// 头像
Circle({ width: 40, height: 40 })
.fill('#E0E0E0')
// 名称和角色
Column() {
Text(member.userName)
.fontSize(15)
.fontColor('#333333')
Text(this.getRoleLabel(member.role))
.fontSize(12)
.fontColor(this.getRoleColor(member.role))
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
.layoutWeight(1)
// 角色选择(仅管理员可操作)
if (member.userId !== 'user_001') {
Row() {
ForEach(['admin', 'member', 'restricted'], (role: string) => {
Text(this.getRoleLabel(role))
.fontSize(11)
.fontColor(member.role === role ? '#FFFFFF' : '#666666')
.backgroundColor(member.role === role ? this.getRoleColor(role) : '#F5F5F5')
.borderRadius(10)
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.margin({ left: 4 })
.onClick(() => this.changeRole(member.userId, role))
})
}
}
// 移除按钮
if (member.userId !== 'user_001') {
Image($r('sys.media.ohos_ic_public_remove'))
.width(20).height(20).fillColor('#F44336')
.margin({ left: 8 })
.onClick(() => this.removeMember(member.userId))
}
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(10)
}
})
}
.width('100%')
}
.width('100%')
.padding(16)
.margin({ top: 8 })
}
// 无家庭
@Builder NoFamilySection() {
Column() {
Text('您还没有加入任何家庭')
.fontSize(16)
.fontColor('#999999')
.margin({ top: 100 })
Row() {
Button('创建家庭')
.width('40%')
.height(44)
.backgroundColor('#007DFF')
.fontColor('#FFFFFF')
.borderRadius(22)
.onClick(() => { this.showCreateDialog = true })
Button('加入家庭')
.width('40%')
.height(44)
.backgroundColor('#F0F7FF')
.fontColor('#007DFF')
.borderRadius(22)
.onClick(() => { this.showJoinDialog = true })
}
.width('90%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 24 })
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
}
// 邀请对话框
@Builder InviteDialog() {
Column() {
Text('邀请家庭成员')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 选择角色
Text('成员角色').fontSize(14).fontColor('#666666').margin({ bottom: 8 })
Row() {
ForEach([
{ key: 'member', label: '普通成员' },
{ key: 'restricted', label: '受限成员' },
{ key: 'admin', label: '管理员' }
], (item: { key: string, label: string }) => {
Text(item.label)
.fontSize(13)
.fontColor(this.inviteRole === item.key ? '#FFFFFF' : '#333333')
.backgroundColor(this.inviteRole === item.key ? '#007DFF' : '#F5F5F5')
.borderRadius(16)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8 })
.onClick(() => { this.inviteRole = item.key })
})
}
.margin({ bottom: 20 })
// 生成邀请码
Button('生成邀请码')
.width('100%')
.height(44)
.backgroundColor('#007DFF')
.fontColor('#FFFFFF')
.borderRadius(22)
.onClick(() => this.generateInvite())
// 显示邀请码
if (this.inviteCode) {
Column() {
Text('邀请码').fontSize(12).fontColor('#999999').margin({ top: 16 })
Text(this.inviteCode)
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#007DFF')
.letterSpacing(8)
.margin({ top: 8 })
Text('24小时内有效,将此邀请码分享给家人')
.fontSize(12).fontColor('#999999').margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Center)
.padding(16)
.backgroundColor('#F0F7FF')
.borderRadius(12)
.margin({ top: 16 })
}
// 关闭
Button('关闭')
.width('100%')
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#333333')
.borderRadius(22)
.margin({ top: 16 })
.onClick(() => { this.showInviteDialog = false })
}
.padding(24)
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 24, topRight: 24 })
}
// 加入对话框
@Builder JoinDialog() {
Column() {
Text('加入家庭')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Text('请输入邀请码').fontSize(14).fontColor('#666666').margin({ bottom: 8 })
TextInput({ placeholder: '6位邀请码', text: this.joinCode })
.fontSize(20)
.textAlign(TextAlign.Center)
.letterSpacing(4)
.maxLength(6)
.onChange((value: string) => { this.joinCode = value.toUpperCase() })
.margin({ bottom: 20 })
Row() {
Button('取消')
.width('45%').height(44)
.backgroundColor('#F5F5F5').fontColor('#333333').borderRadius(22)
.onClick(() => { this.showJoinDialog = false })
Button('加入')
.width('45%').height(44)
.backgroundColor('#007DFF').fontColor('#FFFFFF').borderRadius(22)
.onClick(() => this.joinFamily())
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(24)
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 24, topRight: 24 })
}
// 创建对话框
@Builder CreateDialog() {
Column() {
Text('创建家庭')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Text('家庭名称').fontSize(14).fontColor('#666666').margin({ bottom: 8 })
TextInput({ placeholder: '如:我的家', text: this.newFamilyName })
.fontSize(16)
.onChange((value: string) => { this.newFamilyName = value })
.margin({ bottom: 20 })
Row() {
Button('取消')
.width('45%').height(44)
.backgroundColor('#F5F5F5').fontColor('#333333').borderRadius(22)
.onClick(() => { this.showCreateDialog = false })
Button('创建')
.width('45%').height(44)
.backgroundColor('#007DFF').fontColor('#FFFFFF').borderRadius(22)
.onClick(() => this.createFamily())
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(24)
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 24, topRight: 24 })
}
}
踩坑与注意事项
1. 管理员退出问题
管理员退出家庭时,如果家庭里还有其他成员,怎么办?直接退出的话,家庭就没有管理员了,没人能邀请新成员、删除设备。
解法: 管理员退出前必须先转让管理员角色给其他成员。如果家庭只剩管理员一个人,退出时自动解散家庭。
2. 设备归属问题
管理员A添加了设备,管理员B删除了设备——A不乐意了。设备到底归谁?
解法: 设备属于家庭,不属于个人。任何管理员都可以添加/删除设备,但删除设备需要二次确认。设备添加时记录添加者,删除时通知添加者。
3. 受限成员的体验
受限成员(如小孩)只能控制指定设备。但App里所有设备都显示着,小孩点一个没权限的设备,弹一个"没权限"的提示——体验很差。
解法: 受限成员只显示有权限的设备,不显示没权限的设备。这样就不需要频繁弹"没权限"的提示了。
4. 多家庭切换
用户可能属于多个家庭(自己家、父母家)。切换家庭时,设备列表、场景、定时任务都要切换。如果切换不及时,用户在自己家控制了父母家的设备——那就尴尬了。
解法: 当前家庭ID作为全局状态,所有数据查询都带家庭ID。切换家庭时清空缓存,重新加载数据。UI上明确显示当前所在家庭。
5. 邀请码安全
邀请码是6位随机字符串,暴力破解理论上可行。虽然24小时过期,但如果被猜到,陌生人可以加入你的家庭,控制你的设备。
解法: 邀请码加上使用次数限制(最多3次)。或者不用邀请码,用二维码+深度链接,更安全也更方便。
HarmonyOS 6适配说明
HarmonyOS 6对家庭共享做了几项增强:
-
华为账号集成:新增
@kit.AccountKit的家庭管理API,直接使用华为账号体系,不需要自己实现用户系统。邀请成员时直接选择华为账号联系人。 -
分布式家庭成员同步:家庭成员信息通过分布式数据自动同步到所有设备。在手机上邀请成员,平板上立刻能看到新成员。
-
家庭空间:新增家庭空间概念,家庭成员可以共享照片、日历、备忘录等,不仅仅是设备。
-
儿童模式:新增儿童角色,自动限制可控设备(如不能控制燃气灶、不能修改空调温度到极端值)。
适配代码示例:
// HarmonyOS 6 华为账号集成邀请
import { accountManager } from '@kit.AccountKit'
// 通过华为账号邀请
const contactPicker = accountManager.createContactPicker()
const selectedContacts = await contactPicker.pick({
mode: accountManager.PickMode.MULTI,
filter: accountManager.ContactFilter.HAS_HUAWEI_ACCOUNT
})
// 发送邀请到选中的联系人
for (const contact of selectedContacts) {
await FamilyService.sendInvitation(contact.accountId, 'member')
}
总结
家庭共享让智能家居从"一个人的玩具"变成"全家人的工具"。创建家庭、邀请成员、角色权限控制,三个核心功能缺一不可。
角色权限是关键——管理员、普通成员、受限成员,权限分明,互不干扰。数据隔离保证个人隐私,共享数据让全家受益。
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐ 权限模型不难,但多角色、数据隔离、多家庭切换组合起来有一定复杂度 |
| 使用频率 | ⭐⭐⭐⭐ 家庭共享是多人使用智能家居的前提,使用频率高 |
| 重要程度 | ⭐⭐⭐⭐ 没有家庭共享,智能家居就是"个人遥控器",不是"家庭管家" |
一句话:家庭共享不是"多个人用同一个App",而是"每个人用自己的方式控制同一个家"。 差别在权限和数据隔离里。
- 点赞
- 收藏
- 关注作者
评论(0)