本地优先架构实践:SQLite + Repository 模式 + 自动备份
文章目录本地优先架构实践SQLite Repository 模式 自动备份为什么选择本地优先数据库设计Schema7 张表索引策略Repository 模式自动备份5 行代码的数据安全数据导出/导入JSON 导出JSON 导入最复杂的部分CSV 导出局限性总结本地优先架构实践SQLite Repository 模式 自动备份不联网、不注册、不上云。你的财务数据只存在你手机上。为什么选择本地优先做本地优先Local-First不是因为技术限制而是产品定位的刻意选择1. 隐私财务数据是最敏感的个人信息之一。你有多少存款、你的房子值多少、你的信用卡欠多少——这些信息不应该存在于任何第三方服务器上。WorthBase 不主动发起任何网络请求。2. 离线可用地铁里、飞机上、山区旅行——任何地方都能打开 App 查看和更新数据。不依赖网络状态。3. 数据所有权你的数据是你的。不存在服务商停止运营后数据丢失的问题。数据就是一个.db文件你可以随时复制、备份、迁移。数据库设计Schema7 张表-- 账户表8 种类型CREATETABLEaccounts(idTEXTPRIMARYKEY,nameTEXTNOTNULL,typeTEXTNOTNULL,-- wechat/alipay/bank_card/cash/fund/credit_card/loan/othericonTEXT,sort_orderINTEGERDEFAULT0,deleted_atTEXT,-- 软删除不物理删除created_atTEXT,updated_atTEXT);-- 余额快照表追加式不修改历史CREATETABLEbalance_snapshots(idTEXTPRIMARYKEY,account_idTEXTNOTNULLREFERENCESaccounts(id),balanceREALNOTNULL,snapshot_dateTEXTNOTNULL);-- 实物资产表8 种分类4 种分摊方式CREATETABLEassets(idTEXTPRIMARYKEY,nameTEXTNOTNULL,categoryTEXTNOTNULL,-- vehicle/real_estate/electronics/digital/home/luxury/precious_metal/otherpurchase_dateTEXTNOTNULL,purchase_priceREALNOTNULL,amortization_typeTEXTNOTNULL,expected_lifespan_monthsINTEGER,residual_valueREAL,valuation_trackingINTEGERDEFAULT0,current_valuationREAL,statusTEXTDEFAULTactive,-- active/retired/soldsell_dateTEXT,sell_priceREAL,weight_gramsREAL,image_pathTEXT,created_atTEXT,updated_atTEXT);-- 经常性支出保险、停车费等按月生效CREATETABLErecurring_expenses(idTEXTPRIMARYKEY,asset_idTEXTREFERENCESassets(id),nameTEXTNOTNULL,amountREALNOTNULL,effective_fromTEXTNOTNULL,effective_toTEXT,-- null 持续生效ended_reasonTEXT);-- 一次性维护记录维修、保养CREATETABLEmaintenance_records(idTEXTPRIMARYKEY,asset_idTEXTREFERENCESassets(id),nameTEXTNOTNULL,amountREALNOTNULL,dateTEXTNOTNULL,amortizeINTEGERDEFAULT0-- 是否纳入月均分摊);-- 估值历史可选追踪CREATETABLEvaluation_history(idTEXTPRIMARYKEY,asset_idTEXTREFERENCESassets(id),valuationREALNOTNULL,recorded_dateTEXTNOTNULL);-- 应用设置KV 存储CREATETABLEsettings(keyTEXTPRIMARYKEY,valueTEXTNOTNULL);索引策略针对高频查询场景建索引-- 余额快照按账户日期查询最新余额CREATEINDEXidx_snapshots_account_dateONbalance_snapshots(account_id,snapshot_date);-- 余额快照按日期查询趋势图数据CREATEINDEXidx_snapshots_dateONbalance_snapshots(snapshot_date);-- 经常性支出按生效区间查询当月有哪些支出生效CREATEINDEXidx_recurring_effectiveONrecurring_expenses(effective_from,effective_to);Repository 模式每个表对应一个 Repository 对象封装所有 SQL 操作// src/db/account-repository.ts (简化)exportconstAccountRepository{asyncgetAll():PromiseAccount[]{...},asynccreate(account:Account):Promisevoid{...},asyncupdate(id:string,updates:PartialAccount):Promisevoid{...},asyncsoftDelete(id:string):Promisevoid{...},// SET deleted_at now// 聚合查询获取所有账户的最新余额asyncgetAllLatestBalances():PromiseMapstring,number{constresultawaitdb.getAllAsync(SELECT bs.account_id, bs.balance FROM balance_snapshots bs INNER JOIN ( SELECT account_id, MAX(snapshot_date) as max_date FROM balance_snapshots GROUP BY account_id ) latest ON bs.account_id latest.account_id AND bs.snapshot_date latest.max_date INNER JOIN accounts a ON bs.account_id a.id WHERE a.deleted_at IS NULL);returnnewMap(result.map(r[r.account_id,r.balance]));},};没有 ORM没有 query builder纯 SQL。对于 7 张表的项目来说ORM 引入的抽象层比它解决的问题更多。自动备份5 行代码的数据安全本地优先 App 最大的风险是数据丢失——手机丢了、App 被卸载、数据库文件损坏。我的解决方案极其简单// app/_layout.tsx:58-66useEffect((){constsubscriptionAppState.addEventListener(change,(nextAppState){constprevappStateRef.current;appStateRef.currentnextAppState;// App 退到后台 → 自动备份if(prevactive(nextAppStatebackground||nextAppStateinactive)){BackupService.createBackup();}});return()subscription.remove();},[]);BackupService.createBackup()做的事情就是把 SQLite.db文件原封不动地复制到backups/目录文件名带时间戳。最多保留 3 份旧的自动删除。没有后台任务、没有定时器、没有推送通知。App 退后台就备份就这么朴素。数据导出/导入JSON 导出用户手动触发生成结构化 JSON 文件{version:1,exportDate:2026-07-12T08:00:00.000Z,accounts:[...],balanceSnapshots:[...],assets:[...],recurringExpenses:[...],maintenanceRecords:[...],valuationHistory:[...],settings:{...}}从 7 个 Repository 各取全量数据打包成一个 JSON。通过expo-sharing调起系统分享面板用户可以发到微信、邮件、云盘。JSON 导入最复杂的部分导入不是简单的把 JSON 写进数据库。核心挑战是ID 重映射清空所有 7 张表逐条插入 accounts记录oldId → newId映射逐条插入 assets同样记录映射插入子表balance_snapshots、recurring_expenses 等用映射表替换外键如果父记录不存在孤儿记录静默跳过整个过程在一个数据库事务内执行。任何一步失败全部回滚。// src/services/import-service.ts (简化)awaitdb.withTransactionAsync(async(){// 清空所有表for(consttableofTABLES){awaitdb.execAsync(DELETE FROM${table});}// 重建账户记录 ID 映射constaccountIdMapnewMapstring,string();for(constaccountofdata.accounts){constnewIdgenerateId();accountIdMap.set(account.id,newId);awaitAccountRepository.create({...account,id:newId});}// 重建子表替换外键for(constsnapshotofdata.balanceSnapshots){constnewAccountIdaccountIdMap.get(snapshot.accountId);if(!newAccountId)continue;// 孤儿记录跳过awaitBalanceSnapshotRepository.create({...snapshot,accountId:newAccountId,});}// ... 其他表类似});CSV 导出给人看的格式方便在 Excel 里查看日期, 微信零钱, 支付宝余额, 招商储蓄卡, ..., 总计 2025-01-15, 3500, 12000, 45000, ..., 60500 2025-02-15, 4000, 11500, 46000, ..., 61500局限性本地优先不是万能的局限说明缓解方案无多端同步换设备需要手动导出/导入JSON 文件 系统分享设备丢失 数据丢失如果没备份自动备份到本地退后台触发图片未备份资产照片存在文件系统中.db备份不包含图片已知问题计划中无加密SQLite 文件可以被直接读取应用锁保护 App 访问总结本地优先架构的核心原则简单胜过完备。不用云同步用文件导出/导入不用后台任务用 AppState 监听不用 ORM用纯 SQL不用复杂加密用应用锁对于一个个人财务 App 来说这些简单方案已经够用了。源码: github.com/callmebg/worthbase | db/ | services/CSDN 标签:SQLiteReact Native数据安全本地存储架构掘金话题:前端数据库React Native架构

相关新闻

软件授权方式怎么选?

软件授权方式怎么选?

如果你第一次给软件做授权保护,最容易纠结:用硬件指纹、浮动授权、加密狗,还是在线心跳?我的建议很直接:大多数软件团队,优先从硬件指纹绑定授权开始。它简单、省事,不需要一开始就维护复杂的授…

2026/7/22 21:36:40阅读更多 →
Token管理优化实战:从JWT痛点解决到零Token架构

Token管理优化实战:从JWT痛点解决到零Token架构

1. 项目背景与核心痛点最近在开发者社区里,"Token税"成了高频讨论词。这个术语形象地描述了我们在日常开发中面临的一种困境:随着业务复杂度提升,系统间交互越来越依赖Token机制,导致授权管理、令牌刷新、权限校验等环节…

2026/7/22 6:52:03阅读更多 →
微信支付安全:3种高危提示与应对措施

微信支付安全:3种高危提示与应对措施

1. 警惕微信安全提示:这3种情况必须立即修改密码上周帮朋友处理微信零钱被盗刷的问题时,发现大多数受害者都忽略了微信的安全警告提示。作为每天都要使用微信支付的用户,我们必须清楚哪些安全提示是绝对不能忽视的红色警报。微信支付的安全机…

2026/7/21 21:54:37阅读更多 →
Windows 11版本26100.8968,版本为24H2(构建版26100)和25H2(构建版26200)

Windows 11版本26100.8968,版本为24H2(构建版26100)和25H2(构建版26200)

Windows 11版本26100.8968和26200.8968,适用于Windows 11的版本26100.8968,版本为24H2(构建版26100)和25H2(构建版26200)。 本次更新通过两个发布阶段开放:渐进式和普通版。渐进式推送会分阶段…

2026/7/22 23:40:25阅读更多 →
Jellium Desktop播放历史统计:了解你的媒体消费习惯

Jellium Desktop播放历史统计:了解你的媒体消费习惯

Jellium Desktop播放历史统计:了解你的媒体消费习惯 【免费下载链接】jellium-desktop An unofficial desktop client for Jellyfin 项目地址: https://gitcode.com/GitHub_Trending/je/jellium-desktop Jellium Desktop是一款非官方的Jellyfin桌面客户端&am…

2026/7/22 23:40:25阅读更多 →
国产化边缘视觉方案对比:RK3588 vs X86工控 vs Jetson,工业视觉成本算力全分析

国产化边缘视觉方案对比:RK3588 vs X86工控 vs Jetson,工业视觉成本算力全分析

国产化边缘视觉方案对比:RK3588 vs X86工控 vs Jetson,工业视觉成本算力全分析 关键词:RK3588、边缘计算、机器视觉、国产化替代、Jetson、X86工控机、AI质检、嵌入式NPU0. 前言 在工业机器视觉、智能质检、安防识别、AGV 视觉导航等场景中&a…

2026/7/22 23:40:25阅读更多 →
解决微信支付回调验签难题:wechatpay-apache-httpclient NotificationHandler详解

解决微信支付回调验签难题:wechatpay-apache-httpclient NotificationHandler详解

解决微信支付回调验签难题:wechatpay-apache-httpclient NotificationHandler详解 【免费下载链接】wechatpay-apache-httpclient 微信支付 APIv3 Apache HttpClient装饰器(decorator) 项目地址: https://gitcode.com/gh_mirrors/we/wechat…

2026/7/22 23:40:25阅读更多 →
ApiPost 中配置自动获取 Token 并调用业务接口完整指南

ApiPost 中配置自动获取 Token 并调用业务接口完整指南

ApiPost 中配置自动获取 Token 并调用业务接口完整指南 一、目标 在 ApiPost 中实现:每次发送业务请求时,自动判断 Token 是否过期,过期则自动调用认证接口获取新 Token,无需手动操作。 二、整体流程 点击"发送"业务…

2026/7/22 23:40:25阅读更多 →
大型C++项目维护必备:cppclean检测未声明函数与不一致头文件引用

大型C++项目维护必备:cppclean检测未声明函数与不一致头文件引用

大型C项目维护必备:cppclean检测未声明函数与不一致头文件引用 【免费下载链接】cppclean Finds problems in C source that slow development of large code bases 项目地址: https://gitcode.com/gh_mirrors/cp/cppclean 在大型C项目开发中,代码…

2026/7/22 23:38:25阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/22 18:55:50阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/22 18:55:50阅读更多 →