feat: 添加预算服务,包含预算的增删改查、进度获取及相关工具函数
This commit is contained in:
@@ -5,6 +5,47 @@
|
||||
import api from './api';
|
||||
import type { Budget, ApiResponse, BudgetPeriodType } from '../types';
|
||||
|
||||
/**
|
||||
* Backend Budget API response (snake_case)
|
||||
*/
|
||||
interface ApiBudget {
|
||||
id: number;
|
||||
name: string;
|
||||
amount: number;
|
||||
period_type: BudgetPeriodType;
|
||||
category_id?: number;
|
||||
account_id?: number;
|
||||
is_rolling: boolean;
|
||||
start_date: string;
|
||||
end_date?: string;
|
||||
spent?: number;
|
||||
progress?: number;
|
||||
created_at: string;
|
||||
// Nested objects from Preload
|
||||
category?: { id: number; name: string; icon?: string };
|
||||
account?: { id: number; name: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert backend API response to frontend Budget model
|
||||
*/
|
||||
function mapApiBudgetToBudget(apiBudget: ApiBudget): Budget {
|
||||
return {
|
||||
id: apiBudget.id,
|
||||
name: apiBudget.name,
|
||||
amount: apiBudget.amount,
|
||||
periodType: apiBudget.period_type,
|
||||
categoryId: apiBudget.category_id,
|
||||
accountId: apiBudget.account_id,
|
||||
isRolling: apiBudget.is_rolling,
|
||||
startDate: apiBudget.start_date,
|
||||
endDate: apiBudget.end_date,
|
||||
spent: apiBudget.spent ?? 0,
|
||||
progress: apiBudget.progress ?? 0,
|
||||
createdAt: apiBudget.created_at,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Budget form input interface
|
||||
*/
|
||||
@@ -35,19 +76,20 @@ export interface BudgetProgress {
|
||||
* Get all budgets
|
||||
*/
|
||||
export async function getBudgets(): Promise<Budget[]> {
|
||||
const response = await api.get<ApiResponse<Budget[]>>('/budgets');
|
||||
return response.data || [];
|
||||
const response = await api.get<ApiResponse<ApiBudget[]>>('/budgets');
|
||||
const apiBudgets = response.data || [];
|
||||
return apiBudgets.map(mapApiBudgetToBudget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single budget by ID
|
||||
*/
|
||||
export async function getBudget(id: number): Promise<Budget> {
|
||||
const response = await api.get<ApiResponse<Budget>>(`/budgets/${id}`);
|
||||
const response = await api.get<ApiResponse<ApiBudget>>(`/budgets/${id}`);
|
||||
if (!response.data) {
|
||||
throw new Error('Budget not found');
|
||||
}
|
||||
return response.data;
|
||||
return mapApiBudgetToBudget(response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,11 +119,11 @@ export async function createBudget(data: BudgetFormInput): Promise<Budget> {
|
||||
start_date: data.startDate ? `${data.startDate}T00:00:00Z` : undefined,
|
||||
end_date: data.endDate ? `${data.endDate}T23:59:59Z` : undefined,
|
||||
};
|
||||
const response = await api.post<ApiResponse<Budget>>('/budgets', payload);
|
||||
const response = await api.post<ApiResponse<ApiBudget>>('/budgets', payload);
|
||||
if (!response.data) {
|
||||
throw new Error(response.error || 'Failed to create budget');
|
||||
}
|
||||
return response.data;
|
||||
return mapApiBudgetToBudget(response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,11 +142,11 @@ export async function updateBudget(id: number, data: Partial<BudgetFormInput>):
|
||||
if (data.startDate !== undefined) payload.start_date = `${data.startDate}T00:00:00Z`;
|
||||
if (data.endDate !== undefined) payload.end_date = data.endDate ? `${data.endDate}T23:59:59Z` : null;
|
||||
|
||||
const response = await api.put<ApiResponse<Budget>>(`/budgets/${id}`, payload);
|
||||
const response = await api.put<ApiResponse<ApiBudget>>(`/budgets/${id}`, payload);
|
||||
if (!response.data) {
|
||||
throw new Error(response.error || 'Failed to update budget');
|
||||
}
|
||||
return response.data;
|
||||
return mapApiBudgetToBudget(response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user