HarmonyOS开发:家庭共享——多用户管理

举报
Jack20 发表于 2026/06/26 15:59:29 2026/06/26
【摘要】 HarmonyOS开发:家庭共享——多用户管理📌 核心要点:家庭共享让全家人一起用智能家居,创建家庭、邀请成员、角色权限控制,共享设备和场景,成员数据隔离互不干扰。 背景与动机你家有5口人,5部手机,30个智能设备。你配好了所有设备,设好了场景和定时。然后呢?你老婆打开App,一个设备都看不到——因为设备绑在你的账号下,她的账号里是空的。你爸打开App,也是空的。你妈打开App,还是空的...

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

角色权限

权限 管理员 普通成员 受限成员
查看设备 仅指定设备
控制设备 仅指定设备
添加设备
删除设备
创建场景 ✅(个人场景)
管理共享场景
邀请成员
移除成员
修改角色

成员邀请流程

  1. 管理员生成邀请码/链接
  2. 新成员输入邀请码或点击链接
  3. 新成员确认加入家庭
  4. 管理员设置新成员角色
  5. 新成员获得对应权限

数据隔离

家庭成员的数据需要隔离:

  • 共享数据:设备、共享场景、定时任务——所有成员可见
  • 个人数据:个人场景、个人偏好、语音指令历史——仅创建者可见
  • 管理数据:成员列表、角色权限、邀请记录——仅管理员可见

代码实战

基础用法:家庭管理服务

家庭的创建、成员管理、权限控制。

// 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对家庭共享做了几项增强:

  1. 华为账号集成:新增@kit.AccountKit的家庭管理API,直接使用华为账号体系,不需要自己实现用户系统。邀请成员时直接选择华为账号联系人。

  2. 分布式家庭成员同步:家庭成员信息通过分布式数据自动同步到所有设备。在手机上邀请成员,平板上立刻能看到新成员。

  3. 家庭空间:新增家庭空间概念,家庭成员可以共享照片、日历、备忘录等,不仅仅是设备。

  4. 儿童模式:新增儿童角色,自动限制可控设备(如不能控制燃气灶、不能修改空调温度到极端值)。

适配代码示例:

// 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",而是"每个人用自己的方式控制同一个家"。 差别在权限和数据隔离里。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。