feat: 前端分離+功能api化

1. 新增資產管理CRUD、googleOAuth登入
2. 更改原記帳controller
This commit is contained in:
2026-06-25 14:19:07 +08:00
parent 7bb931c97b
commit 85972f950c
70 changed files with 4754 additions and 1520 deletions

View File

@@ -0,0 +1,114 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Expense;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
public function index()
{
$userId = Auth::id();
$now = Carbon::now();
// 1. 本月基礎統計 (用一個查詢同時撈出「總金額」與「總筆數」)
$monthStats = Expense::where('user_id', $userId)
->whereYear('date', $now->year)
->whereMonth('date', $now->month)
->selectRaw('SUM(amount) as total_amount, COUNT(id) as total_count')
->first();
$monthlyTotal = (float) ($monthStats->total_amount ?? 0);
$monthlyCount = (int) ($monthStats->total_count ?? 0);
// 2. 本月分類統計(圓餅圖)- 保持集合操作即可
$categoryStats = Expense::with('category')
->where('user_id', $userId)
->whereYear('date', $now->year)
->whereMonth('date', $now->month)
->get()
->groupBy(fn($e) => $e->category?->name ?? '未分類')
->map(fn($group, $name) => [
'name' => $name,
'total' => (float) $group->sum('amount'),
'color' => $group->first()->category?->color ?? '#6B7280',
])
->values();
// 3. 近 6 個月趨勢優化:一次性撈出半年內的所有資料,避免 6 次 SQL 查詢
$sixMonthsAgo = $now->copy()->subMonths(5)->startOfMonth();
$endOfCurrentMonth = $now->copy()->endOfMonth();
// 建立近 6 個月的初始月份清單(例如 ['2026/01' => 0.0, '2026/02' => 0.0 ... ]
$monthlyTrendData = collect(range(5, 0))->mapWithKeys(function ($i) use ($now) {
$monthStr = $now->copy()->subMonths($i)->format('Y/m');
return [$monthStr => 0.0];
})->toArray();
// ➔ (A) 撈取一般消費 (未開啟分攤)
$isPostgres = DB::connection()->getDriverName() === 'pgsql';
$dateGroupRaw = $isPostgres ? "to_char(date, 'YYYY/MM')" : "DATE_FORMAT(date, '%Y/%m')";
$normalExpenses = Expense::where('user_id', $userId)
->where('is_amortized', false)
->where('date', '>=', $sixMonthsAgo)
->selectRaw("{$dateGroupRaw} as month, SUM(amount) as total")
->groupBy('month')
->get();
foreach ($normalExpenses as $expense) {
if (array_key_exists($expense->month, $monthlyTrendData)) {
$monthlyTrendData[$expense->month] += (float) $expense->total;
}
}
// ➔ (B) 智慧分攤消費 (處理跨月、跨年、半年繳大魔王 🎯)
// 撈出所有歸屬期間有交集到這 6 個月範圍內的分攤費用
$amortizedExpenses = Expense::where('user_id', $userId)
->where('is_amortized', true)
->where(function ($query) use ($sixMonthsAgo, $endOfCurrentMonth) {
$query->where('period_start', '<=', $endOfCurrentMonth)
->where('period_end', '>=', $sixMonthsAgo);
})
->get();
foreach ($amortizedExpenses as $expense) {
$start = Carbon::parse($expense->period_start)->startOfMonth();
$end = Carbon::parse($expense->period_end)->endOfMonth();
// 計算這筆費用的總跨月份數 (至少為 1 個月)
$totalMonths = max(1, $start->diffInMonths($end) + 1);
// 算出每月平均攤提金額
$monthlyShare = (float) $expense->amount / $totalMonths;
// 跑這 6 個月的迴圈,看有沒有落在這筆費用的歸屬區間內
foreach ($monthlyTrendData as $monthStr => $currentTotal) {
$currentMonthObj = Carbon::createFromFormat('Y/m', $monthStr)->startOfMonth();
// 如果圖表上的這個月份,剛好在費用的歸屬起訖之內,就把每月平均金額灌進去!
if ($currentMonthObj->betweenIncluded($start, $end)) {
$monthlyTrendData[$monthStr] += $monthlyShare;
}
}
}
// 轉回前端圖表要的格式
$monthlyTrend = collect($monthlyTrendData)->map(function ($total, $monthStr) {
return [
'month' => $monthStr,
'total' => round($total, 2), // 四捨五入到小數點兩位
];
})->values();
return response()->json([
'monthlyTotal' => $monthlyTotal,
'monthlyCount' => $monthlyCount,
'categoryStats' => $categoryStats,
'monthlyTrend' => $monthlyTrend,
]);
}
}