Actual Budget 22.12.03 版本全解析:突破金额上限、账户备注与导入器修复
【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual
导读
Actual 22.12.03(配套 Docker 镜像标签 22.12.9)是一个聚焦「数字精度、数据导入与用户体验」的重要发布:核心层移除了金额的 32 位整数上限,从根上解决了大额记账场景下的溢出与显示错误;账户新增备注(Notes)能力;YNAB 4 与 nYNAB 数据导入器得到修复,同时清理了多处导致崩溃的边界情况。阅读本文,你将了解该版本每项关键变更的来龙去脉、背后的源码级实现(金额安全上限、账户更新 API 等),以及升级时的 Docker 标签注意事项与后续影响。
升级前必读:两个关键警告
官方发布说明在本版本开头给出了两条直接影响升级动作的警告,务必先行确认:
- 跨版本升级要求:如果你当前运行的版本早于 22.10.25,必须先去阅读 22.10.25 的发布说明,其中包含一项破坏性变更(breaking change)的处理步骤。22.12.03 的说明本身未复述该步骤,因此从更老版本升级时不要跳过中间说明。
- Docker 标签陷阱:官方明确提示,Docker 镜像标签
22.12.3与22.12.8存在错误,请务必使用22.12.9。当前仓库中 Actual Server 的构建说明 与 Dockerfile、docker-compose.yml 均以镜像方式分发,升级时核对标签即可规避该问题。
核心亮点一:突破 32 位金额上限,支持大额数值
发布说明中的 "Large values are supported" 对应的正是 PR #387(Remove 32bit limit on amounts)。这是本版本最实质性的技术变更,它改变了 Actual 内部对金额数值的表示与安全边界。
底层实现:从 32 位整数到安全数值上限
在 22.12.03 之前,金额字段存在 32 位整数的容量约束,超过上限的金额会导致溢出或无法正确显示。该版本移除了这一限制,并在 packages/loot-core/src/shared/util.ts 中确立了新的安全模型:
// We dont use `Number.MAX_SAFE_NUMBER` and such here because those // numbers are so large that it's not safe to convert them to floats // (i.e. N / 100). For example, `9007199254740987 / 100 === // 90071992547409.88`. While the internal arithemetic would be correct // because we always do that on numbers, the app would potentially // display wrong numbers. Instead of `2**53` we use `2**51` which // gives division more room to be correct export const MAX_SAFE_NUMBER = 2 ** 51 - 1; const MIN_SAFE_NUMBER = -MAX_SAFE_NUMBER; export function safeNumber(value: number) { if (!Number.isInteger(value)) { throw new Error( 'safeNumber: number is not an integer: ' + JSON.stringify(value), ); } if (value > MAX_SAFE_NUMBER || value < MIN_SAFE_NUMBER) { throw new Error( "safeNumber: can't safely perform arithmetic with number: " + value, ); } return value; }关键设计考量:
- 为什么不直接用
Number.MAX_SAFE_INTEGER(2^53−1):源码注释给出了精确解释——虽然内部运算基于整数是正确的,但涉及除以 100(即把整数金额换算回浮点小数的场景)时,2^53 量级的数值转换会丢失精度。例如9007199254740987 / 100 === 90071992547409.88,显示出来就是错的。 - 2^51−1 的选择:保留足够的余量让除法运算结果依然精确。金额以「去掉小数点的整数」存储(见同文件中
IntegerAmount类型注释),因此 2^51−1 对应的可用金额范围对于绝大多数记账场景已绰绰有余,同时彻底摆脱了 32 位整数±2^31的瓶颈。
对上层的影响
金额上下限的放宽会自然传导到所有涉及金额运算的模块(预算、报表、账户余额等)。在 packages/loot-core/src/shared/currencies.ts 中也有对应注释,明确金额格式化依赖util.ts中的安全上限定义,可见该常量为全局金额逻辑的单一事实来源。
核心亮点二:账户新增备注(Notes)能力
发布说明中的 "Accounts can now have notes"(PR #385)为账户实体引入了备注字段,让用户可以为每个账户附加说明性文本(例如「联名账户」「仅用于旅行支出」等)。
数据层与 API 层实现
账户备注通过账户更新接口写入。在 packages/loot-core/src/server/accounts/app.ts 中,updateAccount接收账户的id以及可选的name、last_reconciled、account_group_id字段,并透传给底层数据库更新:
async function updateAccount({ id, name, last_reconciled, account_group_id, }: Pick<AccountEntity, 'id'> & Partial< Pick<AccountEntity, 'name' | 'last_reconciled' | 'account_group_id'> >) { await db.update('accounts', { id, ...(name !== undefined && { name }), ...(last_reconciled && { last_reconciled }), ...(account_group_id !== undefined && { account_group_id }), }); return {}; }该接口通过app.method('account-update', mutator(undoable(updateAccount)))(app.ts)注册为可撤销(undoable)的变更操作,意味着在桌面客户端中修改账户信息(含备注)后支持撤销回退。
客户端联动
在 packages/desktop-client/src/components/accounts/Account.tsx 与 移动端 AccountPage 中,账户编辑统一通过useUpdateAccountMutation触发更新,桌面端与移动端行为保持一致。
说明:
note字段在账户模型中的能力与「交易备注」不同——交易备注由来已久,在 sync.ts 的导入查询与快照中可见(notes列与reconciled、cleared、amount并列)。本版本新增的是「账户级备注」。
核心亮点三:YNAB 4 与 nYNAB 导入器修复
"Fix YNAB 4 and nYnab importers" 是本版本对数据迁移体验的重要修复。导入器将旧格式数据转换为 Actual 的标准表结构,其正确性直接决定迁移成败。
导入流程中的备注与字段映射
虽然发布说明未列出具体 PR 编号,但从当前源码可以确认导入链路中备注与字段映射的处理逻辑位于 packages/loot-core/src/server/accounts/sync.ts:
const notes = trans[mapping.get('notes')]; // ... notes: importNotes && notes ? notes.trim().replace(/#/g, '##') : null,这里有两处细节值得注意:
- 导入时对备注做
trim()去除首尾空白,避免迁移后残留多余空格; - 将备注中的
#替换为##——这是对 Actual 备注格式中转义符的兼容处理,防止迁移数据中的#被误解析为特殊标记。
从源码结构看,该映射表同时覆盖date、payee、imported_payee、category、amount、reconciled、cleared等核心字段(sync.ts),任何字段映射错误都会在导入测试的快照中暴露,相关测试见 sync.test.ts 及 同步快照。
其余稳定性与体验改进
发布说明还包含一批 UI 与稳定性修复,完整清单如下(对应 Actual 22.12.03):
| 变更 | 说明 |
|---|---|
| Fix enter to create accounts(#218) | 修复回车键创建账户的快捷键行为 |
| Update contenteditable="false">【免费下载链接】actualA local-first personal finance app 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 |