erp/app/Services/Statistic/SaleDataService.php

346 lines
16 KiB
PHP
Raw Normal View History

<?php
2024-08-14 17:02:31 +08:00
namespace App\Services\Statistic;
use App\Events\BatchStockUpdateEvent;
use App\Http\Enum\CacheKeyEnum;
use App\Http\Enum\StaticTypeEnum;
use App\Models\BusinessOrderItem;
use App\Models\CombinationGood;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku;
2024-08-14 17:02:31 +08:00
use App\Models\LossRecords;
use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
class SaleDataService
{
public static function saleStatistics(Request $request)
{
if (StaticTypeEnum::TODAY == $request->type) {
//实时统计 sku维度
return static::skuSaleStatisticsByToday($request);
} else {
//历史数据查询
return static::skuSaleStatisticsByHistory($request);
}
}
public static function skuSaleStatisticsByToday(Request $request)
{
[$startTime, $endTime] = static::getTimeRange($request);
$build = BusinessOrderItem::query();
if (!empty($request->sku_id)) {
$externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
->pluck("external_sku_id")->first();
if (!empty($externalSkuId)) {
$build->where("external_sku_id", "=", $externalSkuId);
} else {
return [];
}
}
$orderItems = $build
->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"))
->whereBetween("business_order_items.created_at", [$startTime, $endTime])
2024-08-14 17:02:31 +08:00
->where("external_sku_id", "!=", "")
->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, $endTime] = static::getTimeRange($request);
$build = DailyStockRecord::query();
if (!empty($request->sku_id)) {
$build->where("sku_id", "=", $request->sku_id);
}
$dailyRecord = $build->select("sku_id", DB::raw("sum(order_goods_num) as goods_total")
, DB::raw("sum(order_total_amount) 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)
2024-08-14 17:02:31 +08:00
->whereBetween("day", [$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);
}
}
2024-08-14 17:02:31 +08:00
public static function getTimeRange(Request $request)
{
if (!empty($request->input("start_time")) && !empty($request->input("start_time"))) {
2024-08-14 17:02:31 +08:00
$startTime = Carbon::parse($request->input("start_time"))->toDateTimeString();
$endTime = Carbon::parse($request->input("end_time"))->toDateTimeString();
} else {
2024-08-14 17:02:31 +08:00
$startTime = Carbon::parse($request->input("start_day"))->toDateTimeString();
$endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString();
}
return [$startTime, $endTime];
2024-08-14 17:02:31 +08:00
}
public
static function spuSaleStatisticsByToday(Request $request)
{
[$startTime, $endTime] = static::getTimeRange($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"))
->whereBetween("business_order_items.created_at", [$startTime, $endTime])
2024-08-14 17:02:31 +08:00
->where("external_sku_id", "!=", "")
->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();
}
/**
* 通过历史报表统计spu数据-缓存
* @param Request $request
* @return mixed
*/
2024-08-14 17:02:31 +08:00
public
static function spuSaleStatisticsByHistoryCache(Request $request)
{
[$startTime, $endTime] = static::getTimeRange($request);
$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);
});
}
/**
* 通过历史报表统计spu数据
* @param $startTime
* @param $endTime
* @return array
*/
2024-08-14 17:02:31 +08:00
public
static function spuSaleStatisticsByHistory($startTime, $endTime)
{
$dailyAllRecord = DailyStockRecord::query()
->select("sku_id", DB::raw("sum(order_goods_num) as goods_total")
, DB::raw("sum(order_total_amount) as goods_total_amount"))
2024-08-14 17:02:31 +08:00
->whereBetween("day", [$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();
}
2024-08-14 17:02:31 +08:00
public
static function gmvStatistics(Request $request)
{
2024-08-09 17:17:32 +08:00
if (StaticTypeEnum::TODAY == $request->type) {
[$startTime, $endTime] = static::getTimeRange($request);
2024-08-14 17:02:31 +08:00
$build = BusinessOrderItem::query();
if (!empty($request->sku_id)) {
$externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
->pluck("external_sku_id")->first();
if (!empty($externalSkuId)) {
$build->where("external_sku_id", "=", $externalSkuId);
} else {
return [];
}
}
$orderItems = $build->select("business_order_items.external_sku_id"
, "goods_number", "already_cancel_number", "goods_amount", "created_at")
->whereBetween("business_order_items.created_at", [$startTime, $endTime])
2024-08-09 17:17:32 +08:00
->where("business_order_items.cancel_status", "=", 0)
->get()->toArray();
$interval = $request->input("interval", 30);
return collect($orderItems)->groupBy(function ($v) use ($startTime, $interval) {
$diff = Carbon::parse($v['created_at'])->diffInMinutes(Carbon::parse($startTime));
return (int)floor($diff / $interval);
})->map(function ($v, $key) use ($startTime, $interval) {
return [
"sort_key" => $key,
"interval" => $interval,
"time_start" => Carbon::parse($startTime)->addMinutes($key * $interval)->toTimeString(),
"time_end" => Carbon::parse($startTime)->addMinutes(($key + 1) * $interval)->toTimeString(),
"goods_total" => $v->sum("goods_number") - $v->sum("already_cancel_number"),
2024-08-14 17:02:31 +08:00
"goods_total_amount" => round($v->sum("goods_amount") / 100, 2),
2024-08-09 17:17:32 +08:00
];
})->sort()->values()->toArray();
} else {
//gmv 统计历史数据
[$startTime, $endTime] = static::getTimeRange($request);
2024-08-14 17:02:31 +08:00
$build = DailyStockRecord::query();
if (!empty($request->sku_id)) {
$build->where("sku_id", "=", $request->sku_id);
}
return $build->select("day", DB::raw("sum(order_total_amount) as goods_total_amount")
, DB::raw("sum(order_goods_num) as goods_total"))
->whereBetween("day", [$startTime, $endTime])
2024-08-09 17:17:32 +08:00
->groupBy("day")->orderBy("day")
->get()->toArray();
}
}
2024-08-14 17:02:31 +08:00
public
static function lossRecordStatistics(Request $request)
{
$startTime = Carbon::parse($request->input("start_time"))->toDateTimeString();
$endTime = Carbon::parse($request->input("end_time"))->toDateTimeString();
$build = LossRecords::query();
if (!empty($request->sku_id)) {
$externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
->pluck("external_sku_id")->first();
if (!empty($externalSkuId)) {
$build->where("external_sku_id", "=", $externalSkuId);
} else {
return [];
}
}
return $build->select("date", DB::raw("sum(num) as total_num")
, DB::raw("sum(num*cost) as total_loss_amount"))
->whereBetween("date", [$startTime, $endTime])
->groupBy("date")->orderBy("date")
->get()->toArray();
}
}