Files
accounting/server/routes/balance.js
2026-03-26 01:23:19 +08:00

115 lines
3.5 KiB
JavaScript
Executable File

const express = require('express');
const { getDb } = require('../db');
const {
createUserNameResolver,
normalizeAmount,
roundCurrency,
} = require('../utils/accounting');
const router = express.Router();
function getUsers(db) {
return db.all('SELECT name FROM users ORDER BY sort_order, id');
}
function buildBalancePayload(db) {
const totalIncome = db.get("SELECT COALESCE(SUM(amount), 0) AS total FROM records WHERE type = 'income'");
const totalExpense = db.get("SELECT COALESCE(SUM(amount), 0) AS total FROM records WHERE type = 'expense'");
const income = roundCurrency(totalIncome?.total);
const expense = roundCurrency(totalExpense?.total);
const userBalances = getUsers(db).map((user) => {
const balanceSetting = db.get(
'SELECT initial_balance FROM user_balance_settings WHERE user_name = ?',
user.name
);
const userIncome = db.get(
"SELECT COALESCE(SUM(amount), 0) AS total FROM records WHERE type = 'income' AND user = ?",
user.name
);
const userExpense = db.get(
"SELECT COALESCE(SUM(amount), 0) AS total FROM records WHERE type = 'expense' AND user = ?",
user.name
);
const initialBalance = roundCurrency(balanceSetting?.initial_balance);
const resolvedIncome = roundCurrency(userIncome?.total);
const resolvedExpense = roundCurrency(userExpense?.total);
return {
user: user.name,
initial_balance: initialBalance,
income: resolvedIncome,
expense: resolvedExpense,
balance: roundCurrency(initialBalance + resolvedIncome - resolvedExpense),
};
});
const totalInitialBalance = roundCurrency(
userBalances.reduce((sum, item) => sum + item.initial_balance, 0)
);
return {
initial_balance: totalInitialBalance,
total_income: income,
total_expense: expense,
current_balance: roundCurrency(totalInitialBalance + income - expense),
userBalances,
};
}
router.get('/', (req, res) => {
res.json(buildBalancePayload(getDb()));
});
router.put('/', (req, res) => {
const db = getDb();
const initialBalance = normalizeAmount(req.body.initial_balance);
if (initialBalance === null) {
return res.status(400).json({ error: '请提供有效的初始余额' });
}
db.run(
'UPDATE balance_settings SET initial_balance = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1',
initialBalance
);
res.json(buildBalancePayload(db));
});
router.put('/user/:userName', (req, res) => {
const db = getDb();
const users = getUsers(db);
const resolveUserName = createUserNameResolver(users);
const userName = resolveUserName(req.params.userName, '');
const initialBalance = normalizeAmount(req.body.initial_balance);
if (!userName || !users.some((user) => user.name === userName)) {
return res.status(404).json({ error: '用户不存在' });
}
if (initialBalance === null) {
return res.status(400).json({ error: '请提供有效的初始余额' });
}
const existing = db.get('SELECT * FROM user_balance_settings WHERE user_name = ?', userName);
if (existing) {
db.run(
'UPDATE user_balance_settings SET initial_balance = ?, updated_at = CURRENT_TIMESTAMP WHERE user_name = ?',
initialBalance,
userName
);
} else {
db.run(
'INSERT INTO user_balance_settings (user_name, initial_balance) VALUES (?, ?)',
userName,
initialBalance
);
}
const payload = buildBalancePayload(db).userBalances.find((item) => item.user === userName);
res.json(payload);
});
module.exports = router;