You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面向建筑所有者的Firestore数据结构设计咨询(Android项目)

Firestore数据结构设计与功能实现方案

核心数据结构设计

Firestore是文档型数据库,建议分三个核心集合(避免超过2层嵌套,保证查询效率):

1. users 集合(存储所有用户)

文档ID直接用Firebase Auth生成的uid(和你已有的身份验证流程对齐),字段包括:

  • email: string(用户注册邮箱)
  • role: string(可选值:building_owner / region_admin)
  • regionId: string(仅区域管理员需要,关联所属区域ID)
  • displayName: string(可选,用户昵称)

2. regions 集合(存储区域信息)

文档ID用自定义标识(比如region_001),字段包括:

  • name: string(区域名称,如“XX区”)
  • adminId: string(关联users集合的管理员uid)

3. buildings 集合(存储建筑数据)

文档ID用Firestore自动生成ID或自定义ID,字段包括:

  • ownerId: string(关联users集合的建筑所有者uid)
  • regionId: string(关联regions集合的区域ID)
  • basicInfo: map(存储建筑详情:address、area、constructionYear等)
  • status: string(可选值:pending_review / approved,用于管理员审核)
  • createTime: timestamp(用服务器时间生成,记录上传时间)

用户与自身建筑的关联实现

1. 上传建筑时关联用户

用户上传建筑时,直接获取当前登录用户的uid,写入buildings文档的ownerId字段:

// 获取当前登录用户
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
if (currentUser == null) {
    // 未登录处理逻辑
    return;
}

// 构建建筑数据
Map<String, Object> buildingData = new HashMap<>();
buildingData.put("ownerId", currentUser.getUid());
buildingData.put("regionId", "region_001"); // 可让用户选择区域或自动定位填充
buildingData.put("basicInfo", new HashMap<String, Object>() {{
    put("address", "XX街道XX号");
    put("area", 120.5);
}});
buildingData.put("status", "pending_review");
buildingData.put("createTime", FieldValue.serverTimestamp());

// 写入Firestore
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("buildings")
        .add(buildingData)
        .addOnSuccessListener(documentReference -> {
            // 上传成功提示逻辑
        })
        .addOnFailureListener(e -> {
            // 上传失败处理逻辑
        });

2. 查询用户自己的建筑

通过whereEqualTo过滤ownerId等于当前用户uid的文档:

FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
if (currentUser == null) return;

FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("buildings")
        .whereEqualTo("ownerId", currentUser.getUid())
        .get()
        .addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                List<Building> userBuildings = new ArrayList<>();
                for (QueryDocumentSnapshot document : task.getResult()) {
                    // 解析文档为自定义Building实体类
                    Building building = document.toObject(Building.class);
                    userBuildings.add(building);
                }
                // 渲染用户建筑列表
            }
        });

3. 修改自身建筑的权限控制

修改前先验证当前用户uid是否等于建筑的ownerId,防止越权操作:

String buildingDocId = "xxx"; // 要修改的建筑文档ID
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
if (currentUser == null) return;

FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("buildings").document(buildingDocId)
        .get()
        .addOnSuccessListener(documentSnapshot -> {
            if (documentSnapshot.exists()) {
                String ownerId = documentSnapshot.getString("ownerId");
                if (currentUser.getUid().equals(ownerId)) {
                    // 权限验证通过,执行修改
                    Map<String, Object> updates = new HashMap<>();
                    updates.put("basicInfo.address", "新地址");
                    db.collection("buildings").document(buildingDocId)
                            .update(updates)
                            .addOnSuccessListener(aVoid -> {
                                // 修改成功提示
                            });
                } else {
                    // 无权限修改提示
                }
            }
        });

区域管理员接收数据的实现

1. 管理员查询所属区域的待审核建筑

管理员登录后,先从users文档获取自己的regionId,再过滤该区域下status为pending_review的建筑:

FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
if (currentUser == null) return;

FirebaseFirestore db = FirebaseFirestore.getInstance();
// 先获取管理员所属区域ID
db.collection("users").document(currentUser.getUid())
        .get()
        .addOnSuccessListener(userDoc -> {
            String regionId = userDoc.getString("regionId");
            // 实时监听该区域的待审核建筑
            db.collection("buildings")
                    .whereEqualTo("regionId", regionId)
                    .whereEqualTo("status", "pending_review")
                    .addSnapshotListener((querySnapshot, e) -> {
                        if (e != null) return;
                        for (DocumentChange dc : querySnapshot.getDocumentChanges()) {
                            if (dc.getType() == DocumentChange.Type.ADDED) {
                                Building newBuilding = dc.getDocument().toObject(Building.class);
                                // 通知管理员有新建筑待审核
                            }
                        }
                    });
        });

2. 管理员更新建筑审核状态

审核通过后,修改建筑的status字段:

String buildingDocId = "xxx";
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("buildings").document(buildingDocId)
        .update("status", "approved")
        .addOnSuccessListener(aVoid -> {
            // 可推送通知给建筑所有者
        });

额外实用建议

  • 配置安全规则:必须设置Firestore安全规则,禁止未授权访问:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // 建筑所有者仅能读写自己的建筑
        match /buildings/{buildingId} {
          allow read, update, delete: if request.auth != null && resource.data.ownerId == request.auth.uid;
          allow create: if request.auth != null;
        }
        // 区域管理员仅能查看所属区域的建筑
        match /buildings/{buildingId} {
          allow read: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.regionId == resource.data.regionId;
        }
        // 用户仅能读写自己的文档
        match /users/{userId} {
          allow read, write: if request.auth != null && request.auth.uid == userId;
        }
      }
    }
    
  • 避免嵌套集合:不要把建筑放在用户文档的子集合里,否则跨区域查询会异常低效。
  • 统一时间格式:用FieldValue.serverTimestamp()记录时间,避免客户端时间不一致问题。

内容的提问来源于stack exchange,提问作者Tudor M.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:42:39