diff --git a/app/Console/Commands/DailyStockRecordReport.php b/app/Console/Commands/DailyStockRecordReport.php index 8550978..9c27e44 100644 --- a/app/Console/Commands/DailyStockRecordReport.php +++ b/app/Console/Commands/DailyStockRecordReport.php @@ -53,7 +53,8 @@ class DailyStockRecordReport extends Command $orderItems = BusinessOrderItem::query() ->leftJoin("business_orders as b", "business_order_id", "=", "b.id") ->select("business_order_items.external_sku_id" - , DB::raw("sum(goods_number-already_cancel_number) as goods_total")) + , DB::raw("sum(goods_number-already_cancel_number) as goods_total") + , DB::raw("ROUND(sum(goods_amount) / 100,2) as order_total_amount")) ->whereBetween("business_order_items.created_at", [$startDateTime, $endDateTime]) ->where("business_order_items.cancel_status", "=", 0) ->groupBy('external_sku_id')->get()->pluck(null, "external_sku_id")->toArray(); @@ -85,6 +86,8 @@ class DailyStockRecordReport extends Command ? $lossRecords[$externalSkuId]["loss_num"] : 0; $record->order_goods_num = !empty($orderItems[$externalSkuId]["goods_total"]) ? $orderItems[$externalSkuId]["goods_total"] : 0; + $record->order_total_amount = !empty($orderItems[$externalSkuId]["order_total_amount"]) + ? $orderItems[$externalSkuId]["order_total_amount"] : 0; $record->save(); }); } diff --git a/app/Http/Controllers/DataCenter/DataCenterController.php b/app/Http/Controllers/DataCenter/DataCenterController.php index 9a0936b..2d0f07d 100644 --- a/app/Http/Controllers/DataCenter/DataCenterController.php +++ b/app/Http/Controllers/DataCenter/DataCenterController.php @@ -8,6 +8,7 @@ use App\Http\Resources\DailySalesReportResource; use App\Models\BusinessOrderItem; use App\Models\DailySalesReport; use App\Models\GoodsSku; +use App\Services\Service\SaleDataService; use App\Utils\FormatUtils; use Carbon\Carbon; use Illuminate\Http\Request; @@ -49,7 +50,7 @@ class DataCenterController extends Controller * @param Request $request * @return void */ - public function skuSalesReport(Request $request) + public function saleStatistics(Request $request) { //获取所有参数 $allParams = $request->all(); @@ -57,8 +58,7 @@ class DataCenterController extends Controller $validator = Validator::make($allParams, [ 'type' => 'required|integer', //1表示今日 'start_day' => 'sometimes|string', - 'end_day' => 'sometimes|string', - 'title' => 'sometimes|string' + 'end_day' => 'sometimes|string' ]); if ($validator->fails()) { @@ -66,25 +66,41 @@ class DataCenterController extends Controller $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); return response($this->res, $this->res['httpCode']); } - if (StaticTypeEnum::TODAY == $allParams->type) { - //实时统计 - $orderItems = BusinessOrderItem::query() - ->leftJoin("business_orders as b", "business_order_id", "=", "b.id") - ->select("business_order_items.external_sku_id" - , DB::raw("sum(CASE WHEN b.shipping_status>0 THEN goods_number-already_cancel_number ELSE 0 END) as shipping_num") - , DB::raw("sum(CASE WHEN b.shipping_status=0 THEN goods_number-already_cancel_number ELSE 0 END) as unshipping_num") - , DB::raw("sum(goods_number-already_cancel_number) as goods_total")) - ->where("business_order_items.created_at", ">",Carbon::now()->startOfDay()->toDateTimeString()) - ->where("business_order_items.cancel_status", "=", 0) - ->groupBy('external_sku_id') - ->orderBy("goods_total","DESC") - ->paginate($request->get('per_page')); - return $orderItems; - } else { - $startTime = Carbon::parse($request->input("start_day"))->toDateTimeString(); - $endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString(); + return SaleDataService::saleStatistics($request); + } + + /** + * spu 维度的数据统计 + * @param Request $request + * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|mixed + */ + public function spuSaleStatistics(Request $request){ + //进行校验验证 + $validator = Validator::make($request->all(), [ + 'type' => 'required|integer', //1表示今日 + 'start_day' => 'sometimes|date_format:Y-m-d', + 'end_day' => 'sometimes|date_format:Y-m-d' + ]); + if ($validator->fails()) { + //校验失败返回异常 + $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); + return response($this->res, $this->res['httpCode']); } + return SaleDataService::spuSaleStatistics($request); + } - + public function gmvStatistics(Request $request){ + //进行校验验证 + $validator = Validator::make($request->all(), [ + 'type' => 'required|integer', //1表示今日 + 'start_day' => 'sometimes|date_format:Y-m-d', + 'end_day' => 'sometimes|date_format:Y-m-d' + ]); + if ($validator->fails()) { + //校验失败返回异常 + $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); + return response($this->res, $this->res['httpCode']); + } + return SaleDataService::gmvStatistics($request); } } \ No newline at end of file diff --git a/app/Http/Enum/CacheKeyEnum.php b/app/Http/Enum/CacheKeyEnum.php index 588631b..4f9ba60 100644 --- a/app/Http/Enum/CacheKeyEnum.php +++ b/app/Http/Enum/CacheKeyEnum.php @@ -9,4 +9,6 @@ class CacheKeyEnum const STOCK_RULE_PROPORTION = "stock_rule_proportion"; const DEFAULT_EXPIRE_DAY = "default_expire_day"; + + const SPU_STATISTIC_BY_DATE = "spu_statistic_by_date"; } \ No newline at end of file diff --git a/app/Services/Service/SaleDataService.php b/app/Services/Service/SaleDataService.php new file mode 100644 index 0000000..587cf83 --- /dev/null +++ b/app/Services/Service/SaleDataService.php @@ -0,0 +1,243 @@ +type) { + //实时统计 sku维度 + return static::skuSaleStatisticsByToday($request); + } else { + //历史数据查询 + return static::skuSaleStatisticsByHistory($request); + } + } + + public static function skuSaleStatisticsByToday(Request $request) + { + $orderItems = BusinessOrderItem::query() + ->leftJoin("business_orders as b", "business_order_id", "=", "b.id") + ->select("business_order_items.external_sku_id" + , DB::raw("sum(CASE WHEN b.shipping_status>0 THEN goods_number-already_cancel_number ELSE 0 END) as shipping_num") + , DB::raw("sum(CASE WHEN b.shipping_status=0 THEN goods_number-already_cancel_number ELSE 0 END) as unshipping_num") + , DB::raw("sum(goods_number-already_cancel_number) as goods_total") + , DB::raw("ROUND(sum(goods_amount) / 100,2) as goods_total_amount")) + ->where("business_order_items.created_at", ">", Carbon::now()->subDays(2)->startOfDay()->toDateTimeString()) + ->where("business_order_items.cancel_status", "=", 0) + ->groupBy('external_sku_id') + ->orderBy("goods_total", "DESC") + ->paginate($request->get('per_page')); + + if (!empty($orderItems->items())) { + $externalSkuIds = collect($orderItems->items())->pluck("external_sku_id")->toArray(); + $goodsSkus = GoodsSku::query()->whereIn("external_sku_id", $externalSkuIds) + ->select("id", "title", "name", "stock", "sale_stock", "status", "external_sku_id")->get() + ->pluck(null, "id")->toArray(); + return static::addSaleDataToGoodsSku($goodsSkus, $orderItems); + } + return $orderItems; + } + + public static function skuSaleStatisticsByHistory(Request $request) + { + $startTime = Carbon::parse($request->input("start_day"))->toDateTimeString(); + $endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString(); + $dailyRecord = DailyStockRecord::query() + ->select("sku_id", DB::raw("sum(order_goods_num) as goods_total") + , DB::raw("ROUND(sum(order_total_amount) / 100,2) as goods_total_amount")) + ->whereBetween("created_at", [$startTime, $endTime]) + ->groupBy("sku_id") + ->having(DB::raw("sum(order_goods_num)"), ">", 0) + ->orderBy("goods_total", "DESC") + ->paginate($request->get('per_page')); + $skuIds = collect($dailyRecord->items())->pluck('sku_id')->toArray(); + $goodsSkusMapKeyBySkuId = GoodsSku::query()->select("id", "title", "name", "stock", "sale_stock" + , "status", "external_sku_id")->whereIn("id", $skuIds) + ->get()->pluck(null, "id")->toArray(); + $dailyRecord->getCollection()->map(function ($v) use ($goodsSkusMapKeyBySkuId) { + if (!empty($goodsSkusMapKeyBySkuId[$v['sku_id']])) { + foreach ($goodsSkusMapKeyBySkuId[$v['sku_id']] as $key => $val) { + $v->$key = $val; + } + } else { + //出现异常的skuid + $v->title = "未知商品"; + $v->name = "未知商品"; + $v->stock = 0; + $v->sale_stock = 0; + $v->status = "下架"; + $v->id = 0; + } + }); + return $dailyRecord; + } + + public static function addSaleDataToGoodsSku($goodsSku, $orderItems) + { + $skuIds = collect($goodsSku)->pluck("id")->toArray(); + //查询8天内-昨天的数据 + $startTime = Carbon::now()->subDays(8)->startOfDay()->toDateTimeString(); + $endTime = Carbon::yesterday()->endOfDay()->toDateTimeString(); + $dailyRecord = DailyStockRecord::query()->whereIn("sku_id", $skuIds) + ->whereBetween("created_at", [$startTime, $endTime]) + ->get()->toArray(); + $dailyRecordGroupBySkuId = collect($dailyRecord)->groupBy("sku_id")->toArray(); + Log::info("dailyRecordGroupBySkuId", $dailyRecordGroupBySkuId); + $combineGoodsSkus = collect($goodsSku)->map(function ($v) use ($dailyRecordGroupBySkuId, $orderItems) { + $v['yesterday_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day") + ->take(1)->avg("order_goods_num") ?? 0, 2); + $v['three_day_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day") + ->take(3)->avg("order_goods_num") ?? 0, 2); + $v['seven_day_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day") + ->take(7)->avg("order_goods_num") ?? 0, 2); + return $v; + })->pluck(null, "external_sku_id")->toArray(); + Log::info("combineGoodsSkus", $combineGoodsSkus); + $orderItems->getCollection()->map(function ($v) use ($combineGoodsSkus) { + if (!empty($combineGoodsSkus[$v['external_sku_id']])) { + foreach ($combineGoodsSkus[$v['external_sku_id']] as $key => $val) { + $v->$key = $val; + } + } else { + //出现异常售卖的编码 + $v->title = "未知商品"; + $v->name = "未知商品"; + $v->stock = 0; + $v->sale_stock = 0; + $v->status = "下架"; + $v->yesterday_avg_num = 0; + $v->three_day_avg_num = 0; + $v->id = 0; + $v->seven_day_avg_num = 0; + } + }); + return $orderItems; + } + + public static function spuSaleStatistics(Request $request) + { + //spu 基本就是全统计了 + if (StaticTypeEnum::TODAY == $request->type) { + return static::spuSaleStatisticsByToday($request); + } else { + //统计历史数据 这里走缓存 + return static::spuSaleStatisticsByHistoryCache($request); + } + } + + public static function spuSaleStatisticsByToday(Request $request) + { + //实时统计 sku维度 + $orderItems = BusinessOrderItem::query() + ->leftJoin("business_orders as b", "business_order_id", "=", "b.id") + ->select("business_order_items.external_sku_id" + , DB::raw("sum(CASE WHEN b.shipping_status>0 THEN goods_number-already_cancel_number ELSE 0 END) as shipping_num") + , DB::raw("sum(CASE WHEN b.shipping_status=0 THEN goods_number-already_cancel_number ELSE 0 END) as unshipping_num") + , DB::raw("sum(goods_number-already_cancel_number) as goods_total") + , DB::raw("ROUND(sum(goods_amount) / 100,2) as goods_total_amount")) + ->where("business_order_items.created_at", ">", Carbon::now()->subDays(2)->startOfDay()->toDateTimeString()) + ->where("business_order_items.cancel_status", "=", 0) + ->groupBy('external_sku_id')->get()->toArray(); + $externalSkuIds = collect($orderItems)->pluck("external_sku_id")->toArray(); + $orderItemsKeyByExternalSkuId = collect($orderItems)->pluck(null, "external_sku_id")->toArray(); + $goodsSkuWithTypes = GoodsSku::query() + ->Join("goods", "goods_id", "=", "goods.id") + ->Join("goods_types", "goods.type_id", "=", "goods_types.id") + ->whereIn("external_sku_id", $externalSkuIds) + ->select("goods_skus.id", "goods_types.id as type_id", "goods_types.name", "external_sku_id", "stock", "sale_stock")->get() + ->toArray(); + + return collect($goodsSkuWithTypes)->map(function ($v) use ($orderItemsKeyByExternalSkuId) { + if (!empty($orderItemsKeyByExternalSkuId[$v['external_sku_id']])) { + return array_merge($v, $orderItemsKeyByExternalSkuId[$v['external_sku_id']]); + } + })->filter()->values()->groupBy('type_id')->map(function ($v, $key) { + return [ + "type_id" => $key, + "type_name" => $v[0]['name'] ?? '', + "stock" => $v->sum("stock"), + "sale_stock" => $v->sum("sale_stock"), + "shipping_num" => $v->sum("shipping_num"), + "unshipping_num" => $v->sum("unshipping_num"), + "goods_total" => $v->sum("goods_total"), + "goods_total_amount" => $v->sum("goods_total_amount"), + ]; + })->values()->toArray(); + } + + public static function spuSaleStatisticsByHistoryCache(Request $request) + { + $startTime = Carbon::parse($request->input("start_day"))->toDateTimeString(); + $endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString(); + $cacheKey = CacheKeyEnum::SPU_STATISTIC_BY_DATE . $request->input("start_day") . "_" . $request->input("end_day"); + $expireTime = Carbon::now()->addHour(); + return Cache::remember($cacheKey, $expireTime, function () use ($startTime, $endTime) { + return static::spuSaleStatisticsByHistory($startTime, $endTime); + }); + } + + public static function spuSaleStatisticsByHistory($startTime, $endTime) + { + $dailyAllRecord = DailyStockRecord::query() + ->select("sku_id", DB::raw("sum(order_goods_num) as goods_total") + , DB::raw("ROUND(sum(order_total_amount) / 100,2) as goods_total_amount")) + ->whereBetween("created_at", [$startTime, $endTime]) + ->groupBy("sku_id") + ->having(DB::raw("sum(order_goods_num)"), ">", 0) + ->get()->toArray(); + $skuIds = collect($dailyAllRecord)->pluck('sku_id')->toArray(); + $dailyRecordMapKeyBySkuId = collect($dailyAllRecord)->pluck(null, 'sku_id')->toArray(); + $goodsSkuWithTypes = GoodsSku::query() + ->Join("goods", "goods_id", "=", "goods.id") + ->Join("goods_types", "goods.type_id", "=", "goods_types.id") + ->select("goods_skus.id", "goods_types.id as type_id", "goods_types.name", "external_sku_id", "stock", "sale_stock") + ->whereIn("goods_skus.id", $skuIds) + ->get()->toArray(); + return collect($goodsSkuWithTypes)->map(function ($v) use ($dailyRecordMapKeyBySkuId) { + if (!empty($dailyRecordMapKeyBySkuId[$v['id']])) { + return array_merge($v, $dailyRecordMapKeyBySkuId[$v['id']]); + } + })->filter()->values()->groupBy('type_id')->map(function ($v, $key) { + return [ + "type_id" => $key, + "type_name" => $v[0]['name'] ?? '', + "stock" => $v->sum("stock"), + "sale_stock" => $v->sum("sale_stock"), + "goods_total" => $v->sum("goods_total"), + "goods_total_amount" => $v->sum("goods_total_amount"), + ]; + })->values()->toArray(); + } + public static function gmvStatistics(Request $request){ + //gmv + if (StaticTypeEnum::TODAY == $request->type) { + + } else { + //统计历史数据 这里走缓存 + $startTime = Carbon::parse($request->input("start_day"))->toDateTimeString(); + $endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString(); + + } + } + + + +} diff --git a/resources/lang/zh-CN/permission.php b/resources/lang/zh-CN/permission.php index e247fff..34bbe0a 100644 --- a/resources/lang/zh-CN/permission.php +++ b/resources/lang/zh-CN/permission.php @@ -513,7 +513,7 @@ return [ 'id' => 191, 'name' => '站內信', 'parent_id' => 19, - 'show' => 1, + 'show' => 0, ], 'website_message.index' => [ 'id' => 1911, @@ -598,4 +598,20 @@ return [ 'name' => '盘点记录批量导入', 'parent_id' => 193 ], + 'sale_statistics' => [ + 'id' => 181, + 'name' => '销售统计', + 'parent_id' => 18, + 'show' => 1, + ], + 'data_center.sale_statistics' => [ + 'id' => 1811, + 'name' => '销售统计', + 'parent_id' => 18 + ], + 'data_center.spu_sale_statistics' => [ + 'id' => 1812, + 'name' => 'spu销售统计', + 'parent_id' => 18 + ], ]; diff --git a/routes/api.php b/routes/api.php index 751c3d5..1a61195 100644 --- a/routes/api.php +++ b/routes/api.php @@ -87,7 +87,8 @@ Route::middleware(['auth:api', 'check.permissions'])->group(function () { Route::resource('supplier/daily_stock_record', 'Supplier\DailyStockRecordController',['only' => ['index', 'store']]); // 数据中心 销售报表 - Route::get('data_center/sku_sales_report', [DataCenterController::class, 'skuSalesReport'])->name('sku_sales_report.index'); + Route::get('data_center/sale_statistics', [DataCenterController::class, 'saleStatistics'])->name('data_center.sale_statistics'); + Route::get('data_center/spu_sale_statistics', [DataCenterController::class, 'spuSaleStatistics'])->name('data_center.spu_sale_statistics'); }); Route::get('stock/goods_skus', [GoodsSkusController::class, 'stockNum'])->middleware('auth:api');