Actual Budget - 开源个人财务管理工具

Actual Budget

Actual Budget是一款本地优先的个人财务管理工具,采用NodeJS开发,100%免费且开源。它具有同步功能,可以在设备间无缝同步所有更改,无需复杂的配置。

功能特性

  • 信封预算方法:采用经典的信封预算系统,帮助您有效管理资金
  • 跨设备同步:所有更改可以在多个设备间自动同步
  • 交易管理:完整的交易记录和管理功能
  • 预算分析:提供详细的预算执行情况分析报表
  • 多账户支持:支持管理多个银行账户和信用卡账户
  • API接口:提供完整的JavaScript API用于扩展开发
  • 本地优先:数据首先存储在本地,确保隐私和安全
  • 多平台支持:支持Windows、Mac和Linux桌面应用

安装指南

系统要求

  • Node.js版本需满足package.json中指定的引擎要求
  • 支持Windows、macOS和Linux操作系统

安装方式

npm安装(API使用)

npm install @actual-app/api

桌面应用
官方网站下载对应平台的桌面应用

Docker部署

# 使用官方Docker镜像
docker pull actualbudget/actual

托管部署

使用说明

基本API使用

import { init, loadBudget, getBudgetMonths } from '@actual-app/api';

// 初始化应用
await init({
  dataDir: '/path/to/budgets'
});

// 加载预算文件
await loadBudget('my-budget');

// 获取预算月份数据
const months = await getBudgetMonths();
console.log(months);

查询功能

import { q, aqlQuery } from '@actual-app/api';

// 构建查询
const query = q('transactions')
  .select(['id', 'amount', 'date'])
  .filter({ date: { $gte: '2023-01-01' } })
  .orderBy([{ date: 'desc' }]);

// 执行查询
const results = await aqlQuery(query);

金额处理工具

import { amountToInteger, integerToAmount } from '@actual-app/api';

// 金额与整数的转换
const amount = 123.45;
const integer = amountToInteger(amount); // 12345
const convertedBack = integerToAmount(integer); // 123.45

核心代码

API初始化模块

// index.ts - 核心初始化逻辑
import { validateNodeVersion } from './validateNodeVersion';

let actualApp: null | typeof bundle.lib;

export async function init(config: InitConfig = {}) {
  if (actualApp) {
    return;
  }

  validateNodeVersion();

  if (!globalThis.fetch) {
    globalThis.fetch = (url: URL | RequestInfo, init?: RequestInit) => {
      return import('node-fetch').then(({ default: fetch }) =>
        fetch(url as unknown as FetchInfo, init as unknown as FetchInit),
      ) as unknown as Promise<Response>;
    };
  }

  await bundle.init(config);
  actualApp = bundle.lib;

  injected.override(bundle.lib.send);
  return bundle.lib;
}

查询构建器

// query.js - 强大的查询构建器
class Query {
  constructor(state) {
    this.state = {
      filterExpressions: state.filterExpressions || [],
      selectExpressions: state.selectExpressions || [],
      groupExpressions: state.groupExpressions || [],
      orderExpressions: state.orderExpressions || [],
      calculation: false,
      rawMode: false,
      withDead: false,
      validateRefs: true,
      limit: null,
      offset: null,
      ...state,
    };
  }

  filter(expr) {
    return new Query({
      ...this.state,
      filterExpressions: [...this.state.filterExpressions, expr],
    });
  }

  select(exprs = []) {
    if (!Array.isArray(exprs)) {
      exprs = [exprs];
    }

    const query = new Query({ ...this.state, selectExpressions: exprs });
    query.state.calculation = false;
    return query;
  }
}

export function q(table) {
  return new Query({ table });
}

版本管理工具

// get-next-package-version.js - 智能版本管理
export function getNextVersion({
  currentVersion,
  type,
  currentDate = new Date(),
}) {
  const { versionYear, versionMonth, versionHotfix } =
    parseVersion(currentVersion);
  const { nextVersionYear, nextVersionMonth } = computeNextMonth(
    versionYear,
    versionMonth,
  );
  const resolvedType = resolveType(
    type,
    currentDate,
    versionYear,
    versionMonth,
  );

  const currentDateString = currentDate
    .toISOString()
    .split('T')[0]
    .replaceAll('-', '');

  switch (resolvedType) {
    case 'nightly':
      return `${nextVersionYear}.${nextVersionMonth}.0-nightly.${currentDateString}`;
    case 'hotfix':
      return `${versionYear}.${versionMonth}.${versionHotfix + 1}`;
    case 'monthly':
      return `${nextVersionYear}.${nextVersionMonth}.0`;
    default:
      throw new Error(
        'Invalid type specified. Use "auto", "nightly", "hotfix", or "monthly".',
      );
  }
}

UI组件库

// Button组件 - 统一的按钮设计
export function Button({
  variant = 'normal',
  isDisabled = false,
  children,
  ...props
}) {
  const variantStyle = {
    normal: {
      backgroundColor: theme.buttonNormalBackground,
      color: theme.buttonNormalText,
      borderColor: theme.buttonNormalBorder,
    },
    primary: {
      backgroundColor: theme.buttonPrimaryBackground,
      color: theme.buttonPrimaryText,
      borderColor: theme.buttonPrimaryBorder,
    },
    bare: {
      backgroundColor: 'transparent',
      color: theme.buttonBareText,
      border: 'none',
    }
  };

  return (
    <button
      disabled={isDisabled}
      style={variantStyle[variant]}
      {...props}
    >
      {children}
    </button>
  );
}

Actual Budget提供了完整的个人财务管理解决方案,从核心的预算引擎到用户友好的界面组件,都体现了其专业性和易用性。无论是个人使用还是开发扩展,都能满足各种需求。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

posted @ 2025-09-24 11:00  qife  阅读(10)  评论(0)    收藏  举报