feat: 添加预算服务,包含预算的增删改查、进度获取及相关工具函数
This commit is contained in:
@@ -5,6 +5,47 @@
|
|||||||
import api from './api';
|
import api from './api';
|
||||||
import type { Budget, ApiResponse, BudgetPeriodType } from '../types';
|
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
|
* Budget form input interface
|
||||||
*/
|
*/
|
||||||
@@ -35,19 +76,20 @@ export interface BudgetProgress {
|
|||||||
* Get all budgets
|
* Get all budgets
|
||||||
*/
|
*/
|
||||||
export async function getBudgets(): Promise<Budget[]> {
|
export async function getBudgets(): Promise<Budget[]> {
|
||||||
const response = await api.get<ApiResponse<Budget[]>>('/budgets');
|
const response = await api.get<ApiResponse<ApiBudget[]>>('/budgets');
|
||||||
return response.data || [];
|
const apiBudgets = response.data || [];
|
||||||
|
return apiBudgets.map(mapApiBudgetToBudget);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a single budget by ID
|
* Get a single budget by ID
|
||||||
*/
|
*/
|
||||||
export async function getBudget(id: number): Promise<Budget> {
|
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) {
|
if (!response.data) {
|
||||||
throw new Error('Budget not found');
|
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,
|
start_date: data.startDate ? `${data.startDate}T00:00:00Z` : undefined,
|
||||||
end_date: data.endDate ? `${data.endDate}T23:59:59Z` : 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) {
|
if (!response.data) {
|
||||||
throw new Error(response.error || 'Failed to create budget');
|
throw new Error(response.error || 'Failed to create budget');
|
||||||
}
|
}
|
||||||
return response.data;
|
return mapApiBudgetToBudget(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,12 +141,12 @@ export async function updateBudget(id: number, data: Partial<BudgetFormInput>):
|
|||||||
if (data.isRolling !== undefined) payload.is_rolling = data.isRolling;
|
if (data.isRolling !== undefined) payload.is_rolling = data.isRolling;
|
||||||
if (data.startDate !== undefined) payload.start_date = `${data.startDate}T00:00:00Z`;
|
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;
|
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) {
|
if (!response.data) {
|
||||||
throw new Error(response.error || 'Failed to update budget');
|
throw new Error(response.error || 'Failed to update budget');
|
||||||
}
|
}
|
||||||
return response.data;
|
return mapApiBudgetToBudget(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user