feat: 初始化财务管理应用前端项目,包含账户、预算、交易、报表、设置等核心功能模块。
This commit is contained in:
158
copy/src/services/accountService.ts
Normal file
158
copy/src/services/accountService.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Account Service - API calls for account management
|
||||
*/
|
||||
|
||||
import api from './api';
|
||||
import type { Account, AccountFormInput, TransferFormInput, ApiResponse } from '../types';
|
||||
|
||||
/**
|
||||
* Get all accounts
|
||||
*/
|
||||
export async function getAccounts(): Promise<Account[]> {
|
||||
const response = await api.get<ApiResponse<Account[]>>('/accounts');
|
||||
return response.data || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single account by ID
|
||||
*/
|
||||
export async function getAccount(id: number): Promise<Account> {
|
||||
const response = await api.get<ApiResponse<Account>>(`/accounts/${id}`);
|
||||
if (!response.data) {
|
||||
throw new Error('Account not found');
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new account
|
||||
*/
|
||||
export async function createAccount(data: AccountFormInput): Promise<Account> {
|
||||
// Convert camelCase to snake_case for backend
|
||||
const payload = {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
balance: data.balance,
|
||||
currency: data.currency,
|
||||
icon: data.icon,
|
||||
billing_date: data.billingDate,
|
||||
payment_date: data.paymentDate,
|
||||
};
|
||||
const response = await api.post<ApiResponse<Account>>('/accounts', payload);
|
||||
if (!response.data) {
|
||||
throw new Error(response.error || 'Failed to create account');
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing account
|
||||
*/
|
||||
export async function updateAccount(id: number, data: Partial<AccountFormInput>): Promise<Account> {
|
||||
// Convert camelCase to snake_case for backend
|
||||
const payload: Record<string, unknown> = {};
|
||||
if (data.name !== undefined) payload.name = data.name;
|
||||
if (data.type !== undefined) payload.type = data.type;
|
||||
if (data.balance !== undefined) payload.balance = data.balance;
|
||||
if (data.currency !== undefined) payload.currency = data.currency;
|
||||
if (data.icon !== undefined) payload.icon = data.icon;
|
||||
if (data.billingDate !== undefined) payload.billing_date = data.billingDate;
|
||||
if (data.paymentDate !== undefined) payload.payment_date = data.paymentDate;
|
||||
|
||||
const response = await api.put<ApiResponse<Account>>(`/accounts/${id}`, payload);
|
||||
if (!response.data) {
|
||||
throw new Error(response.error || 'Failed to update account');
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an account
|
||||
*/
|
||||
export async function deleteAccount(id: number): Promise<void> {
|
||||
await api.delete<ApiResponse<void>>(`/accounts/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer between accounts
|
||||
*/
|
||||
export async function transferBetweenAccounts(data: TransferFormInput): Promise<void> {
|
||||
// Convert camelCase to snake_case for backend
|
||||
const payload = {
|
||||
from_account_id: data.fromAccountId,
|
||||
to_account_id: data.toAccountId,
|
||||
amount: data.amount,
|
||||
note: data.note,
|
||||
};
|
||||
const response = await api.post<ApiResponse<void>>('/accounts/transfer', payload);
|
||||
if (!response.success && response.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accounts grouped by type
|
||||
*/
|
||||
export function groupAccountsByType(accounts: Account[] | undefined): Record<string, Account[]> {
|
||||
if (!accounts || !Array.isArray(accounts)) {
|
||||
return {};
|
||||
}
|
||||
return accounts.reduce(
|
||||
(groups, account) => {
|
||||
const type = account.type;
|
||||
if (!groups[type]) {
|
||||
groups[type] = [];
|
||||
}
|
||||
groups[type].push(account);
|
||||
return groups;
|
||||
},
|
||||
{} as Record<string, Account[]>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total balance for accounts
|
||||
*/
|
||||
export function calculateTotalBalance(accounts: Account[] | undefined): number {
|
||||
if (!accounts || !Array.isArray(accounts)) {
|
||||
return 0;
|
||||
}
|
||||
return accounts.reduce((total, account) => total + account.balance, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total assets (positive balances)
|
||||
*/
|
||||
export function calculateTotalAssets(accounts: Account[] | undefined): number {
|
||||
if (!accounts || !Array.isArray(accounts)) {
|
||||
return 0;
|
||||
}
|
||||
return accounts
|
||||
.filter((account) => account.balance > 0)
|
||||
.reduce((total, account) => total + account.balance, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total liabilities (negative balances)
|
||||
*/
|
||||
export function calculateTotalLiabilities(accounts: Account[] | undefined): number {
|
||||
if (!accounts || !Array.isArray(accounts)) {
|
||||
return 0;
|
||||
}
|
||||
return accounts
|
||||
.filter((account) => account.balance < 0)
|
||||
.reduce((total, account) => total + Math.abs(account.balance), 0);
|
||||
}
|
||||
|
||||
export default {
|
||||
getAccounts,
|
||||
getAccount,
|
||||
createAccount,
|
||||
updateAccount,
|
||||
deleteAccount,
|
||||
transferBetweenAccounts,
|
||||
groupAccountsByType,
|
||||
calculateTotalBalance,
|
||||
calculateTotalAssets,
|
||||
calculateTotalLiabilities,
|
||||
};
|
||||
Reference in New Issue
Block a user