资讯动态

Actual Budget 26.5.0 技术解析:Age of Money 与 Sankey 桑基图报告背后的实现原理

发布时间:2026/9/12 2:14:48 来源:尧图企业网站定制
Actual Budget 26.5.0 技术解析Age of Money 与 Sankey 桑基图报告背后的实现原理【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actualActual Budget 26.5.0 是 2026 年 5 月发布的一个以「报告能力」为核心的版本首次引入实验性的 Age of Money货币账龄与 Sankey桑基图两大新报告同时带来拆分交易税务式比例分配、BALANCE_OF规则公式、五个全新社区主题以及认证限流等 70 余项特性、修复与工程改进。本文以 26.5.0 发布说明 为主线结合仓库源码深入剖析这两大新报告的算法与数据流实现并梳理其余值得关注的增强项帮助你在升级到 26.5.0 后快速上手新功能同时理解它们是如何被构建出来的。版本概览与升级方式26.5.0 的发布说明将本次更新概括为「强大的新报告能力以及大量修复」三大核心亮点分别是实验性Age of Money 与 Sankey Diagram 报告实验性自定义主题目录新增五个社区主题Nord、Ilavenil、Gruvbox Light/Dark、You Need A Theme Light/Dark拆分交易中的税务式比例分配。官方推荐的部署方式是使用 Docker 镜像镜像标签为26.5.0docker pull actualbudget/actual:26.5.0 docker run -d -p 5006:5006 -v actual-data:/data actualbudget/actual:26.5.0随后发布的 26.5.1 / 26.5.2 为同内容的补丁版本Docker Tag 26.5.1 / 26.5.2仅针对认证限流计数、自签名证书处理与 UUID 生成兼容性做了修复详见后文「补丁版本说明」一节。需要特别注意的是Age of Money 与 Sankey 报告目前属于实验性功能Sankey 需要在「设置 → 实验性功能」中打开Sankey report开关Experimental.tsx 中的flagsankeyReportAge of Money 同样作为实验性报告进入 Dashboard 组件体系。实验性功能意味着其接口、数据模型与行为仍可能随版本演进发生变化。亮点一Age of Money货币账龄报告Age of Money 是预算社区中非常经典的「财务健康度」指标用于衡量你的钱在进入预算后平均闲置多少天才被花掉。界面中AgeOfMoney.tsx给出的官方定义是Age of Money 显示你的钱平均在预算中停留多少天后才被消费它衡量的是「赚到钱」与「花掉钱」之间的时间差。数值越高说明你在花更早的钱即「靠上个月的收入生活」而不是「月光」。30 天及以上被认为是理想状态。计算原理FIFO 收入桶模型与许多按当前余额 / 日均支出估算的简易算法不同Actual 的实现采用了严格的FIFOFirst In, First Out模拟收入按时间排序后形成一个个收入桶income bucket每次支出按时间顺序从最老的桶里扣减该笔支出的账龄即「支出日期 − 收入桶日期」。最终展示的数值是最近 10 笔支出的平均账龄。核心逻辑位于 age-of-money-spreadsheet.ts主要由四个可独立测试的函数组成函数职责关键实现细节classifyTransactions收入/支出分类按金额符号分类正数含退款进入收入池负数视为支出calculateAgeOfMoneyFIFO 计算收入排序成桶支出从最老桶扣减桶被耗尽则移动到下一个桶若支出无法被完全覆盖则置insufficientData truecalculateAverageAge平均账龄取最近 N 笔默认 10的年龄求平均并四舍五入calculateTrend趋势判定比较最近两个数据点差值超过 ±2 天判定为 ↑/↓否则 → 稳定这套实现有几个值得注意的设计决策基于金额而非类别分类注释明确指出退款正数但无收入类别也应进入资金池因此采用金额符号判定而不是依赖类别标记categoryIsIncome字段被保留备用。全量历史参与 FIFO收入与支出查询都要求「截至结束日期」的完整历史amount: { $gt: 0 }/amount: { $lt: 0 }因为 FIFO 需要完整的收入历史才能正确消耗收入桶——只取报告区间内的数据会算错账龄。转账的特殊处理buildTransferInclusionFilter默认排除「预算内账户 ↔ 预算内账户」的转账它们只是在预算池内移动资金但当用户对报告施加了账户过滤时会通过反转账户条件invertAccountOp把「对端账户被过滤掉」的预算内转账也纳入收支——例如只筛选支票账户时信用卡还款应被视为真实支出报告才能反映现金账龄而非全预算账龄。分桶粒度支持daily/weekly/monthly三种粒度getPeriodKey中周粒度以周一开始月度粒度按YYYY-MM聚合日/周粒度下不生成未来日期段避免滚动均值被拉平成水平线。可视化与状态色报告页顶部实时展示当前账龄天数与趋势箭头状态色由 AgeOfMoneyCard.tsx 的getAgeColor定义账龄 ≥ 30 天绿色理想reportsNumberPositive账龄 ≥ 14 天黄色warningText正在改善账龄 14 天红色reportsNumberNegative需要努力无数据中性色显示N/A。趋势箭头基于calculateTrend±2 天阈值同时当insufficientData为真时会显示「支出超过同期收入部分支出无法匹配到收入」的警告。作为 Dashboard 组件它既可单独进入/reports/age-of-money/:id全屏页面也可作为卡片AgeOfMoneyCard挂载到仪表盘支持条件过滤useRuleConditionFilters、时间范围滑窗/固定区间与粒度切换并可保存为 widget。测试用例见 age-of-money-spreadsheet.test.ts。亮点二Sankey 桑基图报告Sankey 图用「节点 流动宽度」直观展示资金的流向钱从收入端流入经过账户、类别组最终流向具体类别。26.5.0 引入的 Sankey 报告Sankey.tsx、sankey-spreadsheet.ts是本次版本中工程量最大的新功能经过 #7582 的数据模型优化后支持收入来源、图层过滤与更完善的预算处理。两种视图模式Spent 与 Budgeted报告顶部通过GraphModeSelector在两种模式间切换Spent已支出基于真实交易流水展示「收入来源 → 收入类别 → 账户 → 类别组 → 类别」的资金消耗路径默认图层范围为 Payee → CategoryBudgeted已预算基于api/budget-month的预算数据展示「收入类别 → 可用资金 → 已预算 → 类别组 → 类别」的预算分配路径默认图层范围为 Income Category → Category。两种模式的数据来源不同Spent 模式通过 AQL 查询transactions并按账户、类别分组聚合fetchCategoryDataBudgeted 模式则逐月请求预算月数据并累加createBudgetSpreadsheet对跨月区间逐月求和budgeted/spent/balance/received。六层数据模型与图层过滤整张图由 6 个语义图层GraphLayers构成按固定顺序排列GRAPH_LAYER_ORDERPayee(收入收款方) → IncomeCategory(收入类别) → Account(账户) → Budget(预算) → CategoryGroup(类别组) → Category(类别)Budgeted 模式下不包含 Payee 层Spent 模式下不包含 Budget 层。用户可通过工具栏的两个LayerSelector自由选择「From 层」与「To 层」来裁剪视图如只看 账户 → 类别normalizeLayerRange保证 from 严格在 to 之前点击刷新按钮可恢复默认图层范围。数据构建流水线Spent 模式的核心查询在fetchCategoryData中按「类别 × 账户× 收款方」执行 AQLgroupBy聚合收入类别的支出还会拆分为正/负两个方向__NEGATIVE后缀节点处理退款等反向流动。转账数据fetchTransferDataaggregateTransferPairs按transfer_id配对并合并净额。随后buildSankeyData依次执行完整的可视化预处理管线cloneGraph深拷贝基础图避免污染缓存groupOtherCategories将 Top N 之外的类别合并进__OTHER_BUCKET节点sortGraph按三种排序策略重排节点cleanUpNodes清理孤立节点addHiddenNodes补齐隐藏节点addPercentageLabels计算百分比标签addColors基于图表主题着色filterGraphByLayers按用户选择的图层区间裁剪convertToSankeyData转换为 recharts 的nodeslinks数据结构。预算模式还包含几个特殊节点SpecialNodeKeysTo budget待分配资金、Budgeted、Overspent上月超支、For next month预留下月、From previous month上月结转与Available income可用资金完整还原预算工作流的资金语义超额预算toBudget 0时显示为「Overbudgeted」反向流。交互与可配置项Sankey 报告的工具栏提供了相当完整的配置能力配置项选项 / 默认值说明视图模式Spent / Budgeted默认 Spent切换交易流与预算流Top N 类别All(1e5 哨兵值)/10/15/20/25/30默认 15超过上限的类别并入 Other 桶同时受卡片高度限制topNNodes max(2, floor(cardHeight/35px))类别排序Sort per group / Sort all / Sort as budget三种排序策略图层范围From/To 六层自由组合见上文Options 菜单百分比显示 / Spent 视图分组账户 / Spent 视图显示转账均为布尔开关showPercentages、groupAccounts、showTransfersgroupAccounts开启后所有账户聚合为单个「Income」节点SpecialNodeKeys.AllAccountsshowTransfers则会在账户节点之间绘制转账连线。该报告同样支持条件过滤器与时间范围含 1 个月快捷选项并可作为 Dashboard widget 保存meta 中持久化mode、topNcategories、categorySort、layerFrom、layerTo等全部配置。后续修复还保证了 #7619「Sankey 卡片遵循报告设置」与 #7632「收款方为空时收入不被误判为支出」。亮点三拆分交易的税务式比例分配26.5.0 为拆分交易新增了「按比例分配剩余金额」的选项#7257。此前拆分交易只能把差额平均分配给空白的子分录而现在支持按各子分录的既有金额比例分摊差额——这正是税务场景如按比例分摊税费所需要的分配方式。实现位于 TransactionsTable.tsx 的分配处理器中分两条路径路径一存在空白子分录amount 0时等额分配先计算差额remainingAmount parentAmount - Σ(sibling amounts)再Math.floor(remaining / 空子分录数)得到基础份额多余的分remainingCents逐笔 1 分配保证金额总和精确等于父交易金额不产生一分钱误差。路径二无空白子分录时按比例分配按现有子分录的占比计算份额newAmount floor(siblingAmount / ΣsiblingAmounts × remainingAmount) siblingAmount随后用一个循环把所有子分录之和修正到恰好等于父交易金额多则逐笔 -1、少则逐笔 1循环取模分配。这种「先比例、后余数修正」的策略确保了任意金额组合下拆分总和都与父交易严格相等且分配的优先级是等额优先于比例。其他值得关注的增强项规则公式新增BALANCE_OF#7335 为规则与报告公式新增BALANCE_OF(account_id_or_name)函数用于读取其他账户的余额。根据 formulaCatalog.ts 的注册信息支持query与transaction两种模式在规则公式中返回该笔交易发生时刻的运行余额单位分与当前账户的balance变量口径一致在报告/查询公式中返回该账户的当前余额货币显示单位参数为带引号的账户 ID确定性匹配或精确账户名。测试见 useFormulaExecution.test.ts按 ID 解析并查询余额、按精确名称解析、未知账户返回 0 且不发查询。实现层面通过balanceOfNames预取映射useFormulaExecution.ts批量解析公式中的账户引用。自定义主题五个新社区主题与持久化 CSS 覆盖自定义主题目录Custom Themes在 26.5.0 一次性新增五个社区主题#7513 Nord、#7543 Ilavenil、#7571 Gruvbox Light/Dark、#7441/#7447 You Need A Theme Light/Dark后者基于 2026 nYNAB 配色。同时 #7495 修复了一个长期痛点自定义 CSS 覆盖现在可以在主题切换后持久保留且在主题下拉菜单旁边会显示可见的激活指示器#7253 改善了主题目录的响应式表现。工程侧还增加了 #7566「每晚自动扫描主题目录以发现损坏主题」的 CI 保障。认证端点限流防暴力破解#7432 为认证端点引入了速率限制防止暴力破解攻击。值得留意的是补丁版本 #7707 将其修正为「仅统计失败的登录尝试」计入限流避免合法用户因多次输错密码被误锁的同时也避免正常登录流量触发误伤——这正是发布说明中强调的「认证限流」修复项。其他增强速览预算分析报告支持类别组#7116自定义报告 Live 模式新增 Last 30 days 时间范围#7217对应 ReportOptions.ts 中的last30Days选项每笔计划单独覆盖 upcoming 通知窗口#7434允许每个排程schedule拥有自己的提醒时间窗而不必受全局设置约束Dashboard 组件错误边界#7382与规则页 scoped ErrorBoundary#7437组件级渲染崩溃不再拖垮整个应用API 类型导出#7581可通过actual-app/api/models直接导入模型类型#7468 同步为actual-app/core发布.d.ts声明文件修复严格 TypeScript 下游的类型错误双击 Ctrl-f 触发浏览器查找#7605Reimport Deleted 默认开启并在导入间保持状态#7610智利比索 CLP 货币支持#7346对账表单排版重构#7423提升清晰度与易用性CLI 改进#7378账户排序、更完善的 Agent 使用说明与类型修复端到端加密说明澄清#7392E2E 加密仅保护预算数据不包含银行同步令牌同时 #7368 允许桌面端对预算文件启用端到端加密。关键 Bugfix 盘点26.5.0 修复了一批影响日常使用的缺陷按主题可归纳为API 与数据完整性#7242 修复api.updateTransaction()部分更新时损坏拆分父交易的问题#7453 修复子拆分交易的解锁异常。搜索与过滤#7270 修复快捷搜索误将?、%当作通配符导致返回全部交易的问题#7324 修复以$开头的标签查询失败#7304 修复切换过滤操作符时显示 UUID。安全#7428 修复文件上传净化中的路径遍历漏洞#7608 禁止初始化后重新配置 OpenID。报告#7296 修复净资产图时间间隔小于设定值#7356 修复自定义报告编辑器在路由切换间保留未保存设置的问题#7619/#7632 见上文 Sankey 修复。稳定性#7381 修复过期循环排程导致账本页崩溃#7532 修复清空交易收款方报错#7623 修复余额模板金额不可整除时的无限循环#7564 修复 Docker 因未解析的工作区依赖而无法启动#7656 修复标签页挂起后共享 Worker 恢复。一致性#7496 修复 Web 与移动端在未分类交易菜单上「转账」入口不一致的问题#7572 修复交易行拖拽干扰行内文本编辑。维护与工程化改进发布说明中的 Maintenance 条目反映了这个版本在工程质量上的投入值得工程团队借鉴发布自动化升级#7407/#7408/#7418发布说明直接生成文档页面并取消每月合并冻结的流程#7635/#7640 修复冲突与 cherry-pick 提交下的说明生成脚本模块化改造#7429/#7446/#7462loot-core 内部导入迁移至 Node.js subpath imports#server/*、#shared/*所有包统一模块导入规范并新增 #7467 架构边界 ESLint 规则CI 加固#7433/#7448/#7465/#7533修复 GitHub Actions 中的脚本注入模式、引入zizmor检查与自动修复减少 stale 工作流权限发布安全#7556/#7579/#7583nightly 与正式 npm 包启用 Trusted Publishing并统一发布工作流构建效率#7503/#7551e2e 测试改用预构建包、共享 CI 依赖安装步骤以削减yarn install次数工具链升级#7463/#7508oxlint/oxfmt 升级、ESLint v10#7538 关闭产物压缩以保留可读的生产报错堆栈#7547 关闭 Electron 构建矩阵 fail-fast。补丁版本说明26.5.1 / 26.5.2紧随其后的 26.5.1 与 26.5.2 为功能完全一致的补丁版本多出的 26.5.2 仅为解决 Windows Store 发布问题核心修复见 26.5.1 发布说明#7707认证限流仅统计失败登录尝试见上文#7713修复桌面应用自签名证书功能#7734UUID 生成回退到uuid库而非crypto.randomUUID()以兼容 HTTP 非安全上下文环境。如果你的部署环境使用了自签名证书或通过 HTTP 访问非 localhost建议升级到 26.5.1 及以上版本。结语从实现层面看26.5.0 的两个新报告体现了 Actual 报告体系的两个方向Age of Money 用可验证的 FIFO 算法提供「财务健康度」的量化指标Sankey 则以六层数据模型 可配置管线提供资金流向的可视化洞察。二者都深度复用了规则条件过滤useRuleConditionFilters、时间范围与 Dashboard widget 机制使得报告不再是孤立的页面而是可以组合、过滤、保存的一等公民。配合认证限流、E2E 加密澄清、主题持久化等增强26.5.0 是升级价值相当高的一次发布——如果对上述源码细节感兴趣建议继续阅读 age-of-money-spreadsheet.ts 与 sankey-spreadsheet.ts 的完整实现及配套测试。【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价