diff --git a/app/Admin/Controllers/CockpitController.php b/app/Admin/Controllers/CockpitController.php index 2f6afa9..b2177d6 100644 --- a/app/Admin/Controllers/CockpitController.php +++ b/app/Admin/Controllers/CockpitController.php @@ -4,7 +4,9 @@ namespace App\Admin\Controllers; use App\Http\Controllers\Controller; use App\Models\Employee; +use App\Models\Keyword; use App\Models\Ledger; +use App\Models\LedgerItem; use App\Models\Store; use App\Models\TaskPerformance; use Illuminate\Http\Request; @@ -41,10 +43,10 @@ class CockpitController extends Controller ], ); - $data = collect(); - $last = $request->input('last'); + $data = collect(); + if (in_array($last, ['7days', '30days'])) { // 按天 $days = match ($last) { @@ -104,7 +106,7 @@ class CockpitController extends Controller $ledger = $ledgers->get($month); $data->push([ - 'date' => $month, + 'month' => $month, 'sales' => trim_zeros($ledger->sales ?? 0), ]); @@ -120,7 +122,119 @@ class CockpitController extends Controller */ public function lotterySalesTrend(Request $request): array { - return []; + $request->validate( + rules: [ + 'last' => ['bail', 'required', Rule::in(['7days', '30days', '180days', '365days'])], + ], + ); + + $last = $request->input('last'); + + $data = collect(); + + /** @var \Illuminate\Database\Eloquent\Collection */ + $lotteryTypes = Keyword::where('parent_key', 'lottery_type')->get(); + + 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(); + + /** @var \Illuminate\Support\Collection */ + $lotteryTypeLedgers = LedgerItem::select([ + 'date', + 'ledger_item_type_id', + DB::raw('SUM(sales) as sales'), + ]) + ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) + ->groupBy(['date', 'ledger_item_type_id']) + ->get() + ->groupBy('date'); + + while ($startAt->lte($endAt)) { + $date = $startAt->toDateString(); + + $lotteryTypeLedgerItems = $lotteryTypeLedgers->get($date, collect())->keyBy('ledger_item_type_id'); + + $data->push([ + 'date' => $date, + 'data' => $lotteryTypes->map(function ($lotteryType) use ($lotteryTypeLedgerItems) { + $lotteryTypeLedgerItem = $lotteryTypeLedgerItems->get($lotteryType->key); + return [ + 'id' => $lotteryType->key, + 'name' => $lotteryType->name, + 'sales' => trim_zeros($lotteryTypeLedgerItem->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(); + + /** @var \Illuminate\Support\Collection */ + $lotteryTypeLedgers = LedgerItem::select([ + DB::raw("DATE_FORMAT(`date`, '%Y-%m') as month"), + 'ledger_item_type_id', + DB::raw('SUM(sales) as sales'), + ]) + ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) + ->groupBy(['month', 'ledger_item_type_id']) + ->get() + ->keyBy('month'); + + for ($i=0; $i < $months; $i++) { + $month = $startAt->format('Y-m'); + + $lotteryTypeLedgerItems = $lotteryTypeLedgers->get($month, collect())->keyBy('ledger_item_type_id'); + + $data->push([ + 'month' => $month, + 'data' => $lotteryTypes->map(function ($lotteryType) use ($lotteryTypeLedgerItems) { + $lotteryTypeLedgerItem = $lotteryTypeLedgerItems->get($lotteryType->key); + return [ + 'id' => $lotteryType->key, + 'name' => $lotteryType->name, + 'sales' => trim_zeros($lotteryTypeLedgerItem->sales ?? 0), + ]; + }), + ]); + + $startAt->addMonth(); + } + } + + return [ + 'lottery_types' => $lotteryTypes->map(function ($lotteryType) { + return [ + 'id' => $lotteryType->id, + 'name' => $lotteryType->name, + ]; + }), + 'data' => $data->all(), + ]; } /** @@ -128,7 +242,56 @@ class CockpitController extends Controller */ public function storeSalesRanking(Request $request): array { - return []; + $request->validate( + rules: [ + 'last' => ['bail', 'required', Rule::in(['7days', '30days', '180days', '365days'])], + ], + ); + + $last = $request->input('last'); + + $storeSales = Ledger::select(['store_id', DB::raw('SUM(sales) as sales')]) + ->when($last, function ($query, $last) { + $today = Carbon::today(); + + if (in_array($last, ['7days', '30days'])) { + $days = match ($last) { + '7days' => 7, + '30days' => 30, + }; + + $query->whereBetween('date', [ + $today->copy()->subDays($days)->toDateString(), + $today->copy()->subDay()->toDateString(), + ]); + } elseif (in_array($last, ['180days', '365days'])) { + $months = match ($last) { + '180days' => 6, // 6个月 + '365days' => 12, // 12个月 + }; + + $query->whereBetween('date', [ + $today->copy()->startOfMonth()->subMonths($months)->toDateString(), + $today->copy()->startOfMonth()->subMonth()->endOfMonth()->toDateString(), + ]); + } + }) + ->groupBy('store_id'); + + $stores = Store::leftJoinSub($storeSales, 'store_sales', fn ($join) => $join->on('stores.id', '=', 'store_sales.store_id')) + ->orderBy('store_sales.sales', 'DESC') + ->limit(30) + ->get(); + + return $stores->map(function (Store $store) { + return [ + 'store' => [ + 'id' => $store->id, + 'title' => $store->title, + ], + 'sales' => trim_zeros($store->sales ?: 0), + ]; + })->all(); } /** diff --git a/app/Http/Controllers/Api/LedgerController.php b/app/Http/Controllers/Api/LedgerController.php index 7e6a726..aba1220 100644 --- a/app/Http/Controllers/Api/LedgerController.php +++ b/app/Http/Controllers/Api/LedgerController.php @@ -183,7 +183,7 @@ class LedgerController extends Controller throw tap($e, fn ($e) => report($e)); } - return $this->prepareLedger($ledger); + return response()->noContent(); } public function show(string $date, Request $request) @@ -197,25 +197,39 @@ class LedgerController extends Controller ->where('date', $date) ->first(); - return [ - 'data' => $ledger ? $this->prepareLedger($ledger) : null, - ]; - } + $items = []; + + if ($user->store->isLotteryStore()) { + /** @var \Illuminate\Database\Eloquent\Collection */ + $lotteryTypes = Keyword::filter(['parent_key' => 'lottery_type']) + ->oldest('sort') + ->get(); + + $ledgerItems = collect(); + if ($ledger) { + $ledgerItems = $ledger->items->keyBy('ledger_item_type_id'); + } + + $items = $lotteryTypes->map(function ($lotteryType) use ($ledgerItems) { + $ledgerItem = $ledgerItems->get($lotteryType->key); + return [ + 'id' => $lotteryType->key, + 'name' => $lotteryType->name, + 'sales' => $ledgerItem?->sales, + 'expenditure' => $ledgerItem?->expenditure, + ]; + })->all(); + } - protected function prepareLedger(Ledger $ledger) - { return [ - 'date' => $ledger->date, - 'items' => $ledger->items->map(fn ($item) => [ - 'id' => $item->ledger_item_type_id, - 'sales' => $item->sales, - 'expenditure' => $item->expenditure, - ]), - 'new_customers' => $ledger->new_customers, - 'sales' => $ledger->sales, - 'expenditure' => $ledger->expenditure, - 'handover_amount' => $ledger->handover_amount, - 'photos' => $ledger->photos, + 'date' => $date, + 'items' => $items, + 'new_customers' => $ledger?->new_customers, + 'sales' => $ledger?->sales, + 'expenditure' => $ledger?->expenditure, + 'handover_amount' => $ledger?->handover_amount, + 'photos' => $ledger?->photos ?: [], + 'allow_rereport' => is_null($ledger) ? true : $ledger->allowReReport(), ]; } } diff --git a/app/Traits/UploadTrait.php b/app/Traits/UploadTrait.php index c94856b..f60d603 100644 --- a/app/Traits/UploadTrait.php +++ b/app/Traits/UploadTrait.php @@ -2,10 +2,10 @@ namespace App\Traits; -use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; use Slowlyo\OwlAdmin\Admin; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; +use Illuminate\Support\Facades\Storage; trait UploadTrait { @@ -25,17 +25,17 @@ trait UploadTrait public function uploadRich() { $fromWangEditor = false; - $file = request()->file('file'); + $file = request()->file('file'); - if (! $file) { + if (!$file) { $fromWangEditor = true; - $file = request()->file('wangeditor-uploaded-image'); - if (! $file) { + $file = request()->file('wangeditor-uploaded-image'); + if (!$file) { $file = request()->file('wangeditor-uploaded-video'); } } - if (! $file) { + if (!$file) { return $this->response()->additional(['errno' => 1])->fail(__('admin.upload_file_error')); } @@ -57,7 +57,7 @@ trait UploadTrait { $file = request()->file('file'); - if (! $file) { + if (!$file) { return $this->response()->fail(__('admin.upload_file_error')); } @@ -69,63 +69,71 @@ trait UploadTrait return $this->response()->success(['value' => $path]); } - public function saveImage($field, $path) + public function saveImage($field = 'image', $path) { - return $this->saveFile($field, $path); + return $this->saveFile($field, $path); } /** * 表单提交时,转存实际目录,并保留上传时文件名称;文件保存全路径 */ - public function saveFile($field, $path) + public function saveFile($field = 'file', $path) { $file = request()->file($field); - - if (! $file) { - $file = request()->get($field); + + if (!$file) { + $file = request()->get($field); } $fileArr = []; //判断是否多个文件; - if (is_string($file) || isset($file['id'])) { + if(is_string($file) || isset($file['id'])){ $files = [$file]; - } else { + }else{ $files = $file; } - if ($files) { - foreach ($files as $file) { - if (is_array($file) && isset($file['state'])) { - switch ($file['state']) { + if($files){ + foreach($files as $file){ + if(is_array($file) && isset($file['state'])){ + switch($file['state']){ case 'init': - if (strpos($file['value'], 'temporary') !== false) { + if(strpos($file['value'], 'temporary') !== false){ $filePath = $path.'/'.$file['name']; - Storage::disk(Admin::config('admin.upload.disk'))->move($file['value'], $filePath); - $fileArr[] = Storage::disk(Admin::config('admin.upload.disk'))->url($filePath); - } else { + $res = Storage::disk(Admin::config('admin.upload.disk'))->move($file['value'], $filePath); + if($res){ + $fileArr[] = Storage::disk(Admin::config('admin.upload.disk'))->url($filePath); + }else{ + return response()->fail(__('admin.upload_file_error')); + } + }else{ $fileArr[] = $file['value']; } break; case 'uploaded': - if (isset($file['name'])) { + if(isset($file['name'])){ $filePath = $path.'/'.$file['name']; - if (Str::startsWith($file['value'], ['http://', 'https://'])) { + if(Str::startsWith($file['value'], ['http://', 'https://'])){ $fileUrl = parse_url($file['value']); - $fileValue = ltrim($fileUrl['path'], '/'); - } else { + $fileValue = ltrim($fileUrl['path'], '/storage'); + }else{ $fileValue = $file['value']; } - Storage::disk(Admin::config('admin.upload.disk'))->move($file['value'], $filePath); - $fileArr[] = Storage::disk(Admin::config('admin.upload.disk'))->url($filePath); + $res = Storage::disk(Admin::config('admin.upload.disk'))->move($fileValue, $filePath); + + if($res){ + $fileArr[] = Storage::disk(Admin::config('admin.upload.disk'))->url($filePath); + }else{ + return $this->response()->fail(__('admin.upload_file_error')); + } } break; } - } else { + }else{ $fileArr[] = $file; } } } - return $fileArr; } @@ -139,8 +147,8 @@ trait UploadTrait $uploadId = md5(time().$fileName); //创建临时文件夹 - if (Storage::disk(Admin::config('admin.upload.disk'))->exists(Admin::config('admin.upload.tem_directory.'.$type).'/'.$uploadId) === false) { - if (Storage::disk(Admin::config('admin.upload.disk'))->makeDirectory(Admin::config('admin.upload.tem_directory.'.$type).'/'.$uploadId) === false) { + if ( Storage::disk(Admin::config('admin.upload.disk'))->exists(Admin::config('admin.upload.tem_directory.' . $type).'/'.$uploadId) === false ) { + if ( Storage::disk(Admin::config('admin.upload.disk'))->makeDirectory(Admin::config('admin.upload.tem_directory.' . $type).'/'.$uploadId) === false ) { return false; } } @@ -159,19 +167,18 @@ trait UploadTrait $uploadId = request()->get('uploadId'); $partNumber = request()->get('partNumber'); $partSize = request()->get('partSize'); - // dd($file); $fileName = $file->getClientOriginalName(); + $fileName = str_replace(' ', '', $fileName);//去掉空格 //判断该分片是否已存在, - $dirPath = Admin::config('admin.upload.tem_directory.'.$type).'/'.$uploadId; - if (Storage::disk(Admin::config('admin.upload.disk'))->exists($dirPath.'/'.$fileName.'_'.$partNumber)) { + $dirPath = Admin::config('admin.upload.tem_directory.' . $type).'/'.$uploadId; + if(Storage::disk(Admin::config('admin.upload.disk'))->exists($dirPath . '/'.$fileName.'_'.$partNumber)){ return $this->response()->fail(__('admin.upload_file_error')); - } else { + }else{ //验证分片大小-todo $path = $file->storeAs($dirPath, $fileName.'_'.$partNumber, Admin::config('admin.upload.disk')); $realPath = Storage::disk(Admin::config('admin.upload.disk'))->url($path); $eTag = md5_file($realPath); - return $this->response()->success(['eTag' => $eTag]); } } @@ -184,32 +191,33 @@ trait UploadTrait $type = 'file'; //合并文件 $fileName = request()->get('filename', ''); + $fileName = str_replace(' ', '', $fileName); $uploadId = request()->get('uploadId', ''); $partList = request()->get('partList', []); - $basePath = Admin::config('admin.upload.tem_directory.'.$type).'/'.$uploadId; + $basePath = Admin::config('admin.upload.tem_directory.' . $type).'/'.$uploadId; $realPath = 'chunk/'.$fileName; //获取分片列表中序号,查看分片是否都完成上传 $partNumberList = Arr::pluck($partList, 'partNumber'); - if (max($partNumberList) === count($partNumberList)) { - //判断是否已存在同名文件,进行删除 - if (Storage::disk(Admin::config('admin.upload.disk'))->exists($realPath)) { + if(max($partNumberList) === count($partNumberList)){ + //判断是否已存在同名文件,进行重命名 + if(Storage::disk(Admin::config('admin.upload.disk'))->exists($realPath)){ $realPath = 'chunk/(1)'.$fileName; } - for ($i = 1; $i <= count($partNumberList); $i++) { + for($i = 1; $i<= count($partNumberList); $i++){ $_file = Storage::disk(Admin::config('admin.upload.disk'))->get($basePath.'/'.$fileName.'_'.$i); - Storage::disk(Admin::config('admin.upload.disk'))->append($realPath, $_file, null); + $res = Storage::disk(Admin::config('admin.upload.disk'))->append($realPath, $_file, null); } //删除分片文件夹 Storage::disk(Admin::config('admin.upload.disk'))->deleteDirectory($basePath); $value = Storage::disk(Admin::config('admin.upload.disk'))->url($realPath); - return $this->response()->success(['value' => $value]); - } else { + return $this->response()->success(['value'=>$value]); + }else{ return $this->response()->fail(__('admin.upload_file_error')); } } -} +} \ No newline at end of file