我花 600 元用 AI 做了一个本地优先的个人财务管理 App——技术选型与核心实现

我花 600 元用 AI 做了一个本地优先的个人财务管理 App——技术选型与核心实现 文章目录我花 600 元用 AI 做了一个本地优先的个人财务管理 App——技术选型与核心实现背景为什么做这个 App产品长这样花了多少钱技术选型Vibe Coding我的角色从写代码变成了指挥 AI 审核核心功能实现1. 持有成本计算4 种分摊策略2. 净资产趋势降采样 线性回归预测3. 自动备份App 退到后台就存一份遇到的技术难点图表手势冲突数据导入的 ID 重映射测试开源地址我花 600 元用 AI 做了一个本地优先的个人财务管理 App——技术选型与核心实现不联网、不注册、不上云。你的财务数据只存在你手机上。背景为什么做这个 App市面上的记账 App 都有一个共同点要求你注册账号数据上传到云端。但我不想把自己的财务状况交给任何第三方服务器。我要的是一个能回答三个问题的工具我有多少钱— 多账户余额汇总我的东西值多少— 实物资产估值 持有成本计算净资产趋势如何— 历史折线图 目标预测于是我做了家底 WorthBase一款隐私优先、本地存储的个人财务状态管理 App。产品长这样花了多少钱整个项目我只为 AI token 付了费。成本分解项目费用Claude Qwen API编程辅助~¥600EAS Build 构建免费额度内Apple/Google 开发者账号未购买开源分发总计~¥600没有上架应用商店。因为还不确定是否真的有人用先放GitHub 下载。技术选型┌─────────────────────────────────────────────────┐ │ UI Layer │ │ Expo Router (Tab Navigator) │ │ react-native-paper (Material Design 3) │ │ react-native-svg (图表渲染) │ │ react-native-gesture-handler (缩放/拖拽) │ ├─────────────────────────────────────────────────┤ │ State Layer │ │ Zustand (轻量状态管理) │ ├─────────────────────────────────────────────────┤ │ Engine Layer │ │ HoldingCostCalculator (持有成本4种策略) │ │ NetWorthCalculator (净资产 余额 估值) │ │ ProjectionCalculator (线性回归预测目标达成) │ │ SettlementCalculator (卖出结算) │ ├─────────────────────────────────────────────────┤ │ Data Layer │ │ SQLite (expo-sqlite) │ │ Repository 模式 (7 张表, 无 ORM) │ └─────────────────────────────────────────────────┘选择Expo SDK 55而不是裸 React Native是因为 Expo 的开发体验热更新、EAS 构建对独立开发者来说效率更高。选择SQLite而不是 AsyncStorage是因为财务数据需要事务支持和复杂查询。选择Zustand而不是 Redux是因为 75 个文件的项目不需要那么重的状态管理。Vibe Coding我的角色从写代码变成了指挥 AI 审核这个项目 99% 的代码是 AI 写的。我的工作方式变成了描述需求用自然语言告诉 Claude 我要什么功能AI 生成代码Claude 产出完整实现跑起来验证在设备上运行看效果描述 bug 让 AI 改发现问题后用自然语言描述AI 修复我的核心价值从编码能力变成了需求定义能力 代码审核能力。AI 不懂持有成本应该怎么算才符合直觉但我懂。我负责做产品决策AI 负责把它们变成代码。这个流程让整个项目在5 天内从零到可用。核心功能实现1. 持有成本计算4 种分摊策略这是整个 App 最核心的功能。你买一台 8000 块的 MacBook用了 3 年它每个月花你多少钱答案取决于你怎么算。我设计了 4 种分摊方式用Strategy 模式实现// src/engine/strategies/index.tsconststrategyMap:RecordAmortizationType,AmortizationStrategy{[AmortizationType.SIMPLE_LINEAR]:SimpleLinearStrategy,[AmortizationType.EXPECTED_LIFESPAN]:ExpectedLifespanStrategy,[AmortizationType.RESIDUAL_VALUE]:ResidualValueStrategy,[AmortizationType.NO_AMORTIZATION]:NoAmortizationStrategy,};exportfunctiongetStrategy(asset:Asset):AmortizationStrategy{returnstrategyMap[asset.amortizationType]??NoAmortizationStrategy;}每种策略实现同一个接口的三个方法// src/engine/strategies/SimpleLinearStrategy.tsexportconstSimpleLinearStrategy:AmortizationStrategy{calculateMonthlyCost(asset,currentDate){constmonthsHeldmonthsBetween(asset.purchaseDate,currentDate);returnasset.purchasePrice/monthsHeld;// 持有越久月均越低},calculateAccumulated(asset){returnasset.purchasePrice;},calculateRemaining(){return0;},};四种方式的对比方式公式月均变化适用场景简单线性购入价 ÷ 持有月数递减电子产品、数码预期寿命购入价 ÷ 预期月数固定房产(360月)、家居(108月)残值分摊(购入价-残值) ÷ 预期月数固定车辆(60月)不分摊月摊销 0零奢侈品、贵金属为了让用户不需要理解这些算法我做了一个智能推荐器根据资产类型自动选择最合适的方式// src/engine/AmortizationRecommender.tsconstRECOMMENDATION_TABLE{[AssetCategory.ELECTRONICS]:{type:SIMPLE_LINEAR,hint:电子产品贬值快按已持有时间递减},[AssetCategory.REAL_ESTATE]:{type:EXPECTED_LIFESPAN,defaultLifespanMonths:360,...},[AssetCategory.VEHICLE]:{type:RESIDUAL_VALUE,defaultLifespanMonths:60,...},[AssetCategory.LUXURY]:{type:NO_AMORTIZATION,hint:奢侈品可能增值},};用户添加一台 MacBook系统自动选简单线性添加一辆车自动选残值分摊并默认 60 个月。技术为产品体验服务而不是让用户做技术决策。2. 净资产趋势降采样 线性回归预测净资产 账户余额 资产估值。公式只有 68 行代码// src/engine/NetWorthCalculator.tsconstnetWorthliquidAssetsassetValuations;但趋势图的数据处理更有意思。当数据点超过 24 个时需要降采样。我没有用简单的等距取样而是写了一个保留极值的降采样算法——确保趋势图上的峰值和谷值不会在降采样中丢失// app/index.tsx:644functiondownsamplePreservingExtrema(points,maxPoints){if(points.lengthmaxPoints)returnpoints;constresult[points[0]];// 始终保留第一个点constbucketCountmaxPoints-2;constinteriorPointspoints.slice(1,-1);constbucketSizeinteriorPoints.length/bucketCount;for(letb0;bbucketCount;b){constbucketinteriorPoints.slice(Math.floor(b*bucketSize),Math.floor((b1)*bucketSize));// 在每个桶中找偏离趋势线最远的点constavg(result[result.length-1].valuepoints[points.length-1].value)/2;letbestIdx0,bestDist-1;for(letj0;jbucket.length;j){constdistMath.abs(bucket[j].value-avg);if(distbestDist){bestDistdist;bestIdxj;}}result.push(bucket[bestIdx]);}result.push(points[points.length-1]);// 始终保留最后一个点returnresult;}另外如果你设了净资产目标系统会用最近 6 个数据点做线性回归预测你什么时候能达成目标// src/engine/ProjectionCalculator.tsconstpointshistoricalPoints.slice(-6);// 最小二乘法求斜率月均增长额constslope(n*sumXY-sumX*sumY)/(n*sumX2-sumX*sumX);// 预计达成日期 最后数据点 (目标差距 / 月增长)constmonthsNeeded(goal-currentValue)/slope;3. 自动备份App 退到后台就存一份本地优先 App 最大的风险是数据丢失。我的解决方案极其简单——App 退到后台时自动备份 SQLite 文件// app/_layout.tsx:64if(prevactive(nextAppStatebackground||nextAppStateinactive)){BackupService.createBackup();}就这 5 行代码。没有后台任务、没有定时器、没有推送。AppState监听状态变化退后台就复制.db文件到备份目录最多保留 3 份。此外还支持 JSON 格式的手动导出/导入带 ID 重映射和事务安全以及 CSV 格式的数据导出方便在 Excel 里查看。遇到的技术难点图表手势冲突净资产趋势图支持缩放和拖拽但它嵌套在 ScrollView 里。三个手势图表缩放、图表拖拽、页面滚动互相冲突。最终通过 Gesture Handler 的状态管理解决了图表区域内优先响应缩放/拖拽手势只有手势结束后才允许外层 ScrollView 滚动。InteractiveTrendChart组件因此有 540 行是整个项目最复杂的单个文件。数据导入的 ID 重映射从 JSON 文件导入数据时不能直接用原来的 ID可能和目标设备的已有数据冲突。解决方案是在一个事务内清空所有表 → 重建账户和资产记录 old ID → new ID 的映射→ 用新 ID 重建子记录余额快照、经常性支出等。整个流程在一个数据库事务里任何一步失败都会回滚。测试作为独立开发者我用三层策略保证质量Jest 单元测试2,800 行测试覆盖核心引擎、Repository、Store、UI 组件和边界场景设备手动测试Expo Go 扫码即测热更新快速迭代真实用户反馈小红书发了一篇帖子18 条评论帮我发现了 2 个 bug开源地址GitHub: github.com/callmebg/worthbase如果你觉得这个项目有意思给个 Star 就是最大的支持。欢迎提 Issue 和 PR。下一篇一个人从 0 到 1 做一个 App我是怎么用 vibe coding 在 5 天内管理这个项目的项目管理角度复盘技术深度系列想深入了解某个模块的实现1 WorthBase家底技术架构全解析一款本地优先的个人财务 App2 持有成本计算的 4 种分摊策略详解3 本地优先架构SQLite Repository 模式 自动备份4 净资产趋势可视化SVG 图表 手势交互 降采样算法5 资产生命周期管理状态机 卖出结算6 多账户余额管理8 种账户类型 负债处理7 React Native / Expo 开发体验与可复用模式8 UI 组件库与设计系统