端云一体的定位与适用场景

端云一体(Serverless 化的 BaaS)把服务器、数据库、文件存储与鉴权打包成云端服务,应用端通过 SDK 直接调用。开发者不需要买服务器、配域名、写部署脚本,就能拥有后端能力。

它适合三类场景:需要账号与多端同步的应用、数据以文档形式组织的轻业务系统、以及快速验证的产品原型。不适合的场景也很明确:需要复杂事务与强一致性、超低延迟、或需要深度定制运行环境的后端服务。

端云一体不是“没有后端”,而是“后端被抽象成了配置与函数”。你依然要设计数据模型、权限与错误处理,只是不用维护服务器。

判断标准可以简化为一句话:如果你的后端逻辑主要是增删改查加少量校验,端云一体能显著缩短交付周期;如果需要复杂的计算与调度,还是老老实实写服务端。

云函数:把后端逻辑写成函数

云函数是运行在云端的代码片段,按调用次数与运行时长计费。它承担三类职责:数据校验与写入、需要保密的逻辑(如调用第三方服务的密钥)、以及多端共用的业务规则。

// cloudfunctions/addTodo/index.ts —— 云函数入口
import { cloud } from '@hw-agconnect/cloud';

export async function handler(event: { title: string; uid: string }) {
  const title = (event.title ?? '').trim();
  if (title.length === 0 || title.length > 200) {
    return { code: 400, msg: '标题不合法' };
  }
  const db = cloud.database();
  const res = await db.collection('todo').insert({
    title,
    uid: event.uid,
    done: false,
    createdAt: new Date().toISOString(),
  });
  return { code: 0, data: { id: res.id } };
}

三个注意点:云函数里要再次做参数校验(不能只信任客户端);单次执行有时长上限,长任务要拆分或改为异步触发;把密钥放在云函数的环境变量里,绝不要打包进客户端。

云数据库:文档模型与查询

云数据库通常是文档型(类 JSON),没有固定表结构,写入时自动创建集合。灵活性的代价是需要你自己约束数据形状。

数据建模的三个建议
  • 按查询设计先想清楚列表页要按什么排序、筛选,再决定字段与索引。
  • 控制文档大小单个文档别塞数组大对象,超过几十 KB 就该拆子集合。
  • 索引与配额常用查询条件要建索引,同时留意单次查询返回条数上限,用分页拉取。
端云一体示意:左下手机轮廓与右上方云端立方体集群之间以两条反向发光箭头连接,中间穿插数据库与数据粒子
端云数据交换:上行写入、下行拉取,中间隔着一条需要认真设计的数据通道。

安全规则是文档数据库的重中之重:必须按用户维度限制读写范围(例如只允许读写 uid 等于当前用户 ID 的记录),否则任何人都能拉走全库数据。这条规则要在控制台显式配置,并在上线前用另一个账号实测越权访问。

云存储与文件上传

云存储用于保存头像、图片、附件等非结构化文件。标准流程是:客户端申请上传凭证 → 直接上传到云存储 → 拿到文件 URL → 把 URL 写入数据库记录。

几点实践经验:上传前压缩图片(端侧压缩比云端处理更省流量);文件名用随机串避免重名与覆盖;大文件用分片上传并显示进度;删除记录时记得同时清理文件,否则会持续占用存储配额产生费用。

权限方面,尽量不要把存储桶设为公开可读。如果必须公开(如商品图),则要对上传做严格校验:文件类型白名单、大小上限、以及内容审核。上传接口是最容易被滥用的入口之一,一定要加频率限制。

端侧 SDK 接入与鉴权

端侧接入的第一步是初始化与鉴权。通常支持匿名登录、账号密码、第三方账号等多种方式,匿名登录适合先体验后注册的产品。

import { cloud } from '@hw-agconnect/cloud';
import { hilog } from '@kit.PerformanceAnalysisKit';

export async function initCloud(): Promise<void> {
  await cloud.init({
    context: getContext(),      // 从 UIAbility 上下文中获取
  });
  const auth = cloud.auth();
  if (!auth.getCurrentUser()) {
    await auth.signInAnonymously();     // 匿名登录,后续可绑定正式账号
  }
  hilog.info(0x0000, 'Cloud', '当前用户: %{public}s', auth.getCurrentUser()?.getUid() ?? 'none');
}

export async function listTodo() {
  const db = cloud.database();
  const uid = cloud.auth().getCurrentUser()?.getUid();
  const res = await db.collection('todo')
    .where({ uid })                     // 关键:始终带上用户维度条件
    .orderBy('createdAt', 'desc')
    .limit(20)
    .get();
  return res.data ?? [];
}

把 where({ uid }) 写在每个查询里是刻意为之的习惯:即使云端有安全规则,客户端也应当显式限定范围。双重保障,出错概率会低很多。

数据同步与离线优先设计

移动端网络不可靠,好的同步设计必须支持离线。核心思路是“本地先写、后台再同步”,让用户的操作立刻有反馈。

  • 本地缓存:把数据存到端侧数据库或 Preferences,打开应用先渲染缓存,再拉取云端增量。
  • 操作队列:离线时的增删改先入本地队列,联网后按顺序重放。
  • 冲突处理:多端同时修改同一条记录时,用“最后写入胜出”或版本号比较;重要数据建议保留修改历史。
  • 同步状态可视化:给用户明确的“已同步 / 同步中 / 待同步”提示,避免误以为数据丢失。
离线优先不是加个缓存,而是把“数据先落地、后同步”作为默认设计。用户不关心一致性协议,只关心点下去有没有反应。

实现时建议为每条记录加 updatedAt 与 version 字段,用它们做增量拉取与冲突判断,比全量拉取更省流量也更快。