diff --git a/app/Admin/Controllers/CockpitController.php b/app/Admin/Controllers/CockpitController.php new file mode 100644 index 0000000..2f6afa9 --- /dev/null +++ b/app/Admin/Controllers/CockpitController.php @@ -0,0 +1,157 @@ +count(); + // 员工总数 + $employeesCount = Employee::onlyOnline()->count(); + + return [ + 'stores_count' => $storesCount, + 'employees_count' => $employeesCount, + ]; + } + + /** + * 销售趋势 + */ + public function salesTrend(Request $request): array + { + $request->validate( + rules: [ + 'last' => ['bail', 'required', Rule::in(['7days', '30days', '180days', '365days'])], + ], + ); + + $data = collect(); + + $last = $request->input('last'); + + if (in_array($last, ['7days', '30days'])) { + // 按天 + $days = match ($last) { + '7days' => 7, + '30days' => 30, + }; + + // 今天 + $today = Carbon::today(); + // 开始时间 + $startAt = $today->copy()->subDays($days); + // 结束时间 + $endAt = $today->copy()->subDay(); + + $ledgers = Ledger::select(['date', DB::raw('SUM(sales) as sales')]) + ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) + ->groupBy('date') + ->get() + ->keyBy('date'); + + while ($startAt->lte($endAt)) { + $date = $startAt->toDateString(); + + $ledger = $ledgers->get($date); + + $data->push([ + 'date' => $date, + 'sales' => trim_zeros($ledger->sales ?? 0), + ]); + + $startAt->addDay(); + } + } + elseif (in_array($last, ['180days', '365days'])) { + // 按月 + $months = match ($last) { + '180days' => 6, // 6个月 + '365days' => 12, // 12个月 + }; + + // 今天 + $today = Carbon::today(); + // 开始时间 + $startAt = $today->copy()->startOfMonth()->subMonths($months); + // 结束时间 + $endAt = $today->copy()->startOfMonth()->subMonth()->endOfMonth(); + + $ledgers = Ledger::select([DB::raw("DATE_FORMAT(`date`, '%Y-%m') as month"), DB::raw('SUM(sales) as sales')]) + ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) + ->groupBy('month') + ->get() + ->keyBy('month'); + + for ($i=0; $i < $months; $i++) { + $month = $startAt->format('Y-m'); + + $ledger = $ledgers->get($month); + + $data->push([ + 'date' => $month, + 'sales' => trim_zeros($ledger->sales ?? 0), + ]); + + $startAt->addMonth(); + } + } + + return $data->all(); + } + + /** + * 彩种销售趋势 + */ + public function lotterySalesTrend(Request $request): array + { + return []; + } + + /** + * 门店销量排名 + */ + public function storeSalesRanking(Request $request): array + { + return []; + } + + /** + * 年度目标 + */ + public function yearlyGoals(Request $request): array + { + $request->validate( + rules: [ + 'year' => ['bail', 'required', 'int'], + ], + ); + + $aggregates = TaskPerformance::select([ + DB::raw('SUM(`expected_performance`) as expected_performance'), + DB::raw('SUM(`actual_performance`) as actual_performance'), + ])->whereYear(DB::raw("STR_TO_DATE(CONCAT(`month`, '-01'), '%Y-%m-%d')"), $request->input('year'))->first(); + + return [ + // 目标业绩 + 'expected_performance' => trim_zeros($aggregates['expected_performance'] ?? 0), + // 实际业绩 + 'actual_performance' => trim_zeros($aggregates['actual_performance'] ?? 0), + ]; + } +} diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 21a5356..316f880 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -2,6 +2,7 @@ use App\Admin\Controllers\AgreementController; use App\Admin\Controllers\BaseKeywordController; +use App\Admin\Controllers\CockpitController; use App\Admin\Controllers\Complaint\ComplaintController; use App\Admin\Controllers\Complaint\FeedbackController; use App\Admin\Controllers\Finance\CommissionIncomeController; @@ -237,5 +238,19 @@ Route::group([ $router->get('workflow/logs', [WorkflowController::class, 'logs']); $router->get('tasks', [TaskController::class, 'shareList']); + + //---------------------------------- + // 驾驶舱数据统计 + //---------------------------------- + // 基础统计 + $router->get('cockpit/basic', [CockpitController::class, 'basic']); + // 销售走势 + $router->get('cockpit/sales-trend', [CockpitController::class, 'salesTrend']); + // 彩种销售走势 + $router->get('cockpit/lottery-sales-trend', [CockpitController::class, 'lotterySalesTrend']); + // 门店销量排名 + $router->get('cockpit/store-sales-ranking', [CockpitController::class, 'storeSalesRanking']); + // 年度目标 + $router->get('cockpit/yearly-goals', [CockpitController::class, 'yearlyGoals']); }); }); diff --git a/app/Models/Employee.php b/app/Models/Employee.php index fe02d8e..b69db3f 100644 --- a/app/Models/Employee.php +++ b/app/Models/Employee.php @@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Model; use Laravel\Sanctum\HasApiTokens; use Slowlyo\OwlAdmin\Models\AdminUser; use App\Enums\UserRole; +use Illuminate\Database\Eloquent\Builder; /** * 员工 @@ -44,6 +45,11 @@ class Employee extends Model implements AuthenticatableContract return EmployeeFilter::class; } + public function scopeOnlyOnline(Builder $query) + { + $query->where('employee_status', EmployeeStatus::Online); + } + public function avatar(): Attribute { return Attribute::make( diff --git a/app/Models/Store.php b/app/Models/Store.php index c7c2178..01a0115 100644 --- a/app/Models/Store.php +++ b/app/Models/Store.php @@ -5,6 +5,7 @@ namespace App\Models; use App\Enums\BusinessStatus; use App\Traits\HasDateTimeFormatter; use EloquentFilter\Filterable; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -31,6 +32,11 @@ class Store extends Model return \App\Admin\Filters\StoreFilter::class; } + public function scopeOnlyOpen(Builder $query) + { + $query->where('business_status', BusinessStatus::Open); + } + // 店长 public function master() { diff --git a/config/admin.php b/config/admin.php index f99d292..ba0bc5d 100644 --- a/config/admin.php +++ b/config/admin.php @@ -57,6 +57,7 @@ return [ ], ], 'except' => [ + '/api/cockpit/*', ], ],