问题描述
在 BeeCount Cloud 共享账本中,Editor 从手机 App 新建标签后:
- App 提示标签已创建、同步状态也显示已上传;
- 该标签随后不会出现在当前共享账本的标签选择器中;
- Owner 及其他成员的 App/Web 也看不到该标签;
- 当 Editor 在 Web 端把这个标签名称关联到一笔共享交易后,Owner 设备会在拉取交易时创建一条新的同名标签;
- 新标签再次上传后,所有成员才开始看到它。
最终服务端会出现两条名称相同但 syncId 不同的标签,分别属于 Editor 和 Owner。
复现步骤
- 创建一个共享账本,账户 A 为 Owner,账户 B 为 Editor;
- 账户 B 在手机 App 的共享账本交易标签选择器中选择“新建标签”;
- 等待 BeeCount Cloud 自动同步完成;
- 检查账户 B 的共享账本标签选择器,以及账户 A 的 App/Web 标签列表;
- 使用账户 B 在 Web 端编辑一笔共享交易,填入刚才创建的标签名称;
- 等待账户 A 的手机 App 拉取并再次同步;
- 检查服务端和各端标签列表。
实际行为
- Editor 新建的标签被保存为 Editor 自己的 user-global 标签;
- 共享账本的 Editor 标签选择器只展示 Owner 的镜像标签,因此刚创建的标签会“消失”;
- Owner 和其他成员无法读取 Editor 的 user-global 标签;
- Web 更新交易时可能只提交
tags 名称而没有合法的 tagIds;
- Owner 手机拉取这笔交易后,旧协议 fallback 会按名称创建新的本地标签并生成新的 UUID;
- 同步兜底逻辑随后将该标签作为 Owner 的 user-global 标签上传,造成同名重复标签。
“标签已同步”的提示并非网络层误报:服务端确实接受了变更,但它进入了 Editor 的个人用户作用域,而不是共享账本可见的标签集合。
期望行为
共享账本内创建、选择和同步标签的权限及作用域必须一致,不能出现以下情况:
- Editor 创建成功但当前共享账本不可见;
- 通过关联交易间接为 Owner 创建标签;
- 同一个逻辑标签在不同用户作用域下生成多个
syncId;
- 未知的标签名称被客户端 fallback 静默升级为 Owner 的全局标签。
根因
当前实现同时存在三处不一致:
- 标签数据模型是 user-global,创建时归当前登录用户所有;
- 共享账本 Editor 的选择器会完全替换为 Owner 的共享资源镜像;
- 交易同步仍允许只有标签名称、没有
tagIds,移动端对此执行“按名称创建本地标签”的兼容逻辑。
另外,标签选择器期望 TagEditPage 返回新建的 Tag 以自动选中,但创建页目前丢弃了 createTag() 返回值并以空结果退出,因此自动选中逻辑不会执行。
方案一:维持现有 Owner 标签模型(短期止血)
共享账本标签继续采用“Owner 的 user-global 标签作为唯一权威集合”。
App
- 当前账本为共享账本且角色为 Editor 时,隐藏或禁用“新建标签”;
- 提示“共享账本标签由 Owner 管理”;
- Editor 仍可在自己的个人账本中创建个人标签;
- Owner/单人账本创建标签后,
TagEditPage 应返回新建标签,使选择器可以立即选中。
Web/服务端
- Editor 编辑共享交易时,只能选择 Owner 已有标签;
- 请求必须携带属于 Owner 的合法
tagIds;
- 对只有未知标签名称、没有合法
tagIds 的共享交易写入返回校验错误;
- 移动端应用共享交易时,不得因为缺少
tagIds 而自动创建 Owner 标签;
- 对旧协议名称 fallback:共享账本中只允许按名称匹配现有 Owner/镜像标签,匹配不到则跳过并记录同步警告。
优点
- 改动和迁移范围较小;
- 与当前 Owner 资源镜像模型一致;
- 可以快速阻止重复标签继续产生。
缺点
- Editor 无法在共享账本中创建新标签;
- 协作体验受到限制,需要联系 Owner 先创建标签。
方案二:新增账本级共享标签(完整协作方案)
为共享账本增加独立于任何用户个人标签库的 ledger-scoped 标签模型。
数据模型
- 标签以
ledger_id + sync_id 为稳定身份;
- Owner 和 Editor 均可在当前共享账本中创建标签;
- Editor 创建的标签不会进入 Editor 或 Owner 的个人 user-global 标签库;
- 共享标签只在对应共享账本中可见;
- 所有成员及交易引用同一个
syncId。
权限建议
- Owner、Editor:创建和使用共享标签;
- Owner:可管理、重命名和删除全部共享标签;
- Editor 是否允许重命名/删除,可单独定义,或仅允许管理自己创建的标签。
同步和兼容
- App 和 Web 创建共享标签时调用明确的 ledger-scoped 写入接口;
- 服务端生成或确认唯一
syncId,提交后广播给所有成员;
- 交易必须引用共享标签
syncId;
- 需要设计现有 Owner 标签在共享账本中的迁移/映射策略;
- 需要同时修改 BeeCount App 和 BeeCount-Cloud。
优点
- 符合共享记账的协作预期;
- Editor 可以正常创建标签;
- 不会修改 Owner 在其他私人账本使用的全局标签库;
- 权限和数据所有权边界清晰。
缺点
- 涉及数据库、同步协议、App/Web UI 和迁移逻辑,改动较大;
- 需要处理旧交易及现有 Owner 标签的兼容映射。
建议实施顺序
- 先采用方案一阻止继续生成重复标签,并修复创建页不返回新标签的问题;
- 如果产品目标允许 Editor 管理共享账本标签,再以方案二作为后续完整改造;
- 修复上线后,提供检查或清理工具识别同名不同
syncId 的历史重复标签,不建议仅按名称自动跨用户合并。
验收测试
环境
- BeeCount Cloud:1.6.2
- 同步方式:BeeCount Cloud 自建服务
- 影响范围:Android/iOS App、Web,以及 BeeCount-Cloud 同步协议
问题描述
在 BeeCount Cloud 共享账本中,Editor 从手机 App 新建标签后:
最终服务端会出现两条名称相同但
syncId不同的标签,分别属于 Editor 和 Owner。复现步骤
实际行为
tags名称而没有合法的tagIds;“标签已同步”的提示并非网络层误报:服务端确实接受了变更,但它进入了 Editor 的个人用户作用域,而不是共享账本可见的标签集合。
期望行为
共享账本内创建、选择和同步标签的权限及作用域必须一致,不能出现以下情况:
syncId;根因
当前实现同时存在三处不一致:
tagIds,移动端对此执行“按名称创建本地标签”的兼容逻辑。另外,标签选择器期望
TagEditPage返回新建的Tag以自动选中,但创建页目前丢弃了createTag()返回值并以空结果退出,因此自动选中逻辑不会执行。方案一:维持现有 Owner 标签模型(短期止血)
共享账本标签继续采用“Owner 的 user-global 标签作为唯一权威集合”。
App
TagEditPage应返回新建标签,使选择器可以立即选中。Web/服务端
tagIds;tagIds的共享交易写入返回校验错误;tagIds而自动创建 Owner 标签;优点
缺点
方案二:新增账本级共享标签(完整协作方案)
为共享账本增加独立于任何用户个人标签库的 ledger-scoped 标签模型。
数据模型
ledger_id + sync_id为稳定身份;syncId。权限建议
同步和兼容
syncId,提交后广播给所有成员;syncId;优点
缺点
建议实施顺序
syncId的历史重复标签,不建议仅按名称自动跨用户合并。验收测试
syncId;环境