// +---------------------------------------------------------------------- namespace Modules\Account\Services; use Illuminate\Http\Request; use Modules\Account\Models\Bill; use Modules\Account\Models\Family; use Modules\Account\Models\FamilyMember; use Illuminate\Support\Facades\DB; class AdminStatisticsService { /** * @title 获取概览统计 * * @param Request $request * @return array */ public function getOverview($request) { $year = $request->input('year', date('Y')); $month = $request->input('month', date('m')); $startDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'; $endDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-31'; // 总账单数 $totalBills = Bill::count(); // 当月账单数 $monthBills = Bill::whereBetween('bill_date', [$startDate, $endDate])->count(); // 总收入 $totalIncome = (float)Bill::where('type', 'income')->sum('amount'); // 总支出 $totalExpense = (float)Bill::where('type', 'expense')->sum('amount'); // 当月收入 $monthIncome = (float)Bill::where('type', 'income') ->whereBetween('bill_date', [$startDate, $endDate]) ->sum('amount'); // 当月支出 $monthExpense = (float)Bill::where('type', 'expense') ->whereBetween('bill_date', [$startDate, $endDate]) ->sum('amount'); // 总用户数 $totalUsers = DB::table('auth_users')->count(); // 总家庭数 $totalFamilies = Family::count(); // 活跃用户数(本月有记账的用户) $activeUsers = Bill::whereBetween('bill_date', [$startDate, $endDate]) ->distinct('user_id') ->count('user_id'); // 活跃家庭数(本月有记账的家庭) $activeFamilies = Bill::whereBetween('bill_date', [$startDate, $endDate]) ->whereNotNull('family_id') ->distinct('family_id') ->count('family_id'); return [ 'total_bills' => $totalBills, 'month_bills' => $monthBills, 'total_income' => $totalIncome, 'total_expense' => $totalExpense, 'total_balance' => $totalIncome - $totalExpense, 'month_income' => $monthIncome, 'month_expense' => $monthExpense, 'month_balance' => $monthIncome - $monthExpense, 'total_users' => $totalUsers, 'total_families' => $totalFamilies, 'active_users' => $activeUsers, 'active_families' => $activeFamilies ]; } /** * @title 获取趋势统计 * * @param Request $request * @return array */ public function getTrend($request) { $type = $request->input('type', 'month'); // month 或 year $year = $request->input('year', date('Y')); $months = $request->input('months', 12); $years = $request->input('years', 5); if ($type === 'month') { // 月度趋势 $data = []; for ($i = $months - 1; $i >= 0; $i--) { $date = date('Y-m', strtotime("-$i months")); $startDate = $date . '-01'; $endDate = date('Y-m-t', strtotime($date)); $income = (float)Bill::where('type', 'income') ->whereBetween('bill_date', [$startDate, $endDate]) ->sum('amount'); $expense = (float)Bill::where('type', 'expense') ->whereBetween('bill_date', [$startDate, $endDate]) ->sum('amount'); $count = Bill::whereBetween('bill_date', [$startDate, $endDate])->count(); $data[] = [ 'date' => $date, 'income' => $income, 'expense' => $expense, 'balance' => $income - $expense, 'count' => $count ]; } } else { // 年度趋势 $data = []; for ($i = $years - 1; $i >= 0; $i--) { $currentYear = date('Y') - $i; $income = (float)Bill::where('type', 'income') ->whereYear('bill_date', $currentYear) ->sum('amount'); $expense = (float)Bill::where('type', 'expense') ->whereYear('bill_date', $currentYear) ->sum('amount'); $count = Bill::whereYear('bill_date', $currentYear)->count(); $data[] = [ 'year' => $currentYear, 'income' => $income, 'expense' => $expense, 'balance' => $income - $expense, 'count' => $count ]; } } return $data; } /** * @title 获取分类统计 * * @param Request $request * @return array */ public function getCategoryStatistics($request) { $type = $request->input('type', 'expense'); // income 或 expense $year = $request->input('year', date('Y')); $month = $request->input('month'); $query = Bill::where('type', $type); if ($year && $month) { $startDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'; $endDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-31'; $query->whereBetween('bill_date', [$startDate, $endDate]); } else { $query->whereYear('bill_date', $year); } $bills = $query->get(); // 按分类汇总 $data = $bills->groupBy('category')->map(function ($items) { return [ 'category' => $items->first()->category, 'count' => $items->count(), 'amount' => (float)$items->sum('amount') ]; })->sortByDesc('amount')->values(); // 计算总金额和占比 $totalAmount = $data->sum('amount'); $data = $data->map(function ($item) use ($totalAmount) { $item['percentage'] = $totalAmount > 0 ? round($item['amount'] / $totalAmount * 100, 2) : 0; return $item; }); return [ 'type' => $type, 'total_amount' => (float)$totalAmount, 'total_count' => $data->sum('count'), 'categories' => $data ]; } /** * @title 获取用户统计 * * @param Request $request * @return array */ public function getUserStatistics($request) { $limit = $request->input('limit', 20); $type = $request->input('type', 'expense'); // income 或 expense $year = $request->input('year', date('Y')); $month = $request->input('month'); $query = Bill::select('user_id') ->selectRaw('COUNT(*) as bill_count') ->selectRaw('SUM(CASE WHEN type = "income" THEN amount ELSE 0 END) as total_income') ->selectRaw('SUM(CASE WHEN type = "expense" THEN amount ELSE 0 END) as total_expense') ->selectRaw('SUM(CASE WHEN type = "income" THEN amount ELSE 0 END) - SUM(CASE WHEN type = "expense" THEN amount ELSE 0 END) as balance') ->with('user:uid,nickname,username'); if ($year && $month) { $startDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'; $endDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-31'; $query->whereBetween('bill_date', [$startDate, $endDate]); } else { $query->whereYear('bill_date', $year); } $users = $query->groupBy('user_id') ->orderBy($type === 'income' ? 'total_income' : 'total_expense', 'desc') ->limit($limit) ->get(); return [ 'type' => $type, 'users' => $users->map(function ($user) { return [ 'user_id' => $user->user_id, 'name' => $user->user->nickname ?? $user->user->username, 'username' => $user->user->username, 'bill_count' => $user->bill_count, 'total_income' => (float)$user->total_income, 'total_expense' => (float)$user->total_expense, 'balance' => (float)$user->balance ]; }) ]; } /** * @title 获取家庭统计 * * @param Request $request * @return array */ public function getFamilyStatistics($request) { $limit = $request->input('limit', 20); $type = $request->input('type', 'expense'); // income 或 expense $year = $request->input('year', date('Y')); $month = $request->input('month'); $query = Bill::select('family_id') ->selectRaw('COUNT(*) as bill_count') ->selectRaw('SUM(CASE WHEN type = "income" THEN amount ELSE 0 END) as total_income') ->selectRaw('SUM(CASE WHEN type = "expense" THEN amount ELSE 0 END) as total_expense') ->selectRaw('SUM(CASE WHEN type = "income" THEN amount ELSE 0 END) - SUM(CASE WHEN type = "expense" THEN amount ELSE 0 END) as balance') ->whereNotNull('family_id') ->with('family:id,name,owner_id'); if ($year && $month) { $startDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'; $endDate = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-31'; $query->whereBetween('bill_date', [$startDate, $endDate]); } else { $query->whereYear('bill_date', $year); } $families = $query->groupBy('family_id') ->orderBy($type === 'income' ? 'total_income' : 'total_expense', 'desc') ->limit($limit) ->get(); // 获取成员数量 $familyIds = $families->pluck('family_id')->toArray(); $memberCounts = FamilyMember::whereIn('family_id', $familyIds) ->selectRaw('family_id, COUNT(*) as member_count') ->groupBy('family_id') ->pluck('member_count', 'family_id'); return [ 'type' => $type, 'families' => $families->map(function ($family) use ($memberCounts) { return [ 'family_id' => $family->family_id, 'name' => $family->family->name, 'member_count' => $memberCounts[$family->family_id] ?? 0, 'bill_count' => $family->bill_count, 'total_income' => (float)$family->total_income, 'total_expense' => (float)$family->total_expense, 'balance' => (float)$family->balance ]; }) ]; } }