orderCountingRecords($start, $end, $unit, $ownerIds);//当前用户只有一个货主权限,只需执行旧方法即可 $queryCondition = $this->transfersToCondition($start, $end, $unit, $ownerIds);//根据条件获取查询条件 $md5OwnerIds = md5(join(Arr::sortRecursive($ownerIds)));//将$ownerIds排序,保证md5加密后结果一致 将当前用户拥有的ownerIds进行md5加密 foreach ($queryCondition['data'] as $dateStr => $ownerIds) { $key = 'new_order_counting_records_' . $dateStr . '_' . $md5OwnerIds . '_' . $queryCondition['unit']; $data = cache()->remember($key, $this->getTtl($dateStr, $unit), function () use ($dateStr, $unit, $ownerIds) { //如果缓存不存在,则将$dateStr作为查询条件,使用之前的接口查询改日的订单量统计结果,由于接口返回collect,去其中的第一个 //因为查询条件仅为一天 return $this->orderCountingRecords($dateStr, $dateStr, $unit, $ownerIds)->first(); }); $dataList->push($data); } return $dataList; } public function orderCountingRecords($start, $end, $unit, $ownerIds) { $orders = $this->get($start, $end, $unit, $ownerIds); $dataList = collect(); $orders->groupBy('date_target')->sortKeys()->each(function ($items) use (&$dataList, $unit) { $counter = $items->reduce(function ($sum, $item) { return $sum + $item->amount; }, 0); $date_target = $items[0]->date_target; $dataList->push([ 'counter' => $counter, 'date_target' => $date_target, ]); }); return $dataList; } public function logisticsCountingRecords($start, $end, $ownerIds) { $key = 'logisticsCountingRecords_' . $start . '_' . $end . '_' . json_encode($ownerIds); return Cache::remember($key, config('cache.expirations.logisticsCountingRecords'), function () use ($start, $end, $ownerIds) { $dataList = collect(); $resultOrders = $this->get($start, $end, '日', $ownerIds); $resultOrders->groupBy('logistic_id')->each(function ($item) use (&$dataList) { $counter = $item->reduce(function ($sum, $item) { return $sum + $item->amount; }, 0); $dataList->push([ 'value' => $counter, 'logistic_id' => $item[0]->logistic_id, ]); }); $map = []; $logistics = Logistic::query()->whereIn('id', data_get($dataList, '*.logistic_id'))->get(); $logistics->each(function ($logistic) use (&$map) { $map[$logistic->id] = $logistic; }); return $dataList->map(function (&$item) use ($map) { $logistic = $map[$item['logistic_id']] ?? ''; $item['name'] = $logistic->name ?? ''; return $item; }); }); } public function warehouseCountingRecords($start, $end, $ownerIds) { $key = 'warehouseCountingRecords_' . $start . '_' . $end . '_' . json_encode($ownerIds); return Cache::remember($key, config('cache.expirations.warehouseCountingRecords'), function () use ($start, $end, $ownerIds) { $dataList = collect(); $resultOrders = $this->get($start, $end, '日', $ownerIds); $resultOrders->groupBy('warehouse_id')->each(function ($item) use (&$dataList) { $counter = $item->reduce(function ($sum, $item) { return $sum + $item->amount; }, 0); $warehouse = Warehouse::query()->find($item[0]->warehouse_id); $dataList->push([ 'value' => $counter, 'warehouse_id' => $item[0]->warehouse_id, 'name' => $warehouse ? $warehouse->name : '仓库为空', 'code' => $warehouse ? $warehouse->code : 'NULL', ]); }); return $dataList; }); } public function get($start, $end, $unit, $ownerIds) { $queryCondition = $this->transfersToCondition($start, $end, $unit, $ownerIds); return $this->getOrderCountingRecords($queryCondition); } public function getFromCache($queryCondition): array { $lackingCondition = []; $orderCountingRecords_FromCache = collect(); $lackingCondition['unit'] = $queryCondition['unit']; foreach ($queryCondition['data'] as $dateStr => $ownerIds) { foreach ($ownerIds as $ownerId) { $key = 'order_counting_records_' . $dateStr . '_' . $ownerId . '_' . $queryCondition['unit']; $items = cache()->get($key); if (empty($items)) { if (empty($lackingCondition['data'][$dateStr])) { $lackingCondition['data'][$dateStr] = []; } $lackingCondition['data'][$dateStr][] = $ownerId; } else { foreach ($items as $item) { $orderCountingRecords_FromCache->push($item); } } } } return [$orderCountingRecords_FromCache, $lackingCondition]; } public function getFromMiddleTable($queryCondition): array { $orderSqlBuilder = OrderCountingRecord::query(); $unit = $queryCondition['unit']; foreach ($queryCondition['data'] as $date => $owners) { $orderSqlBuilder->orWhere(function ($query) use ($owners, $date, $unit) { $query->whereIn('owner_id', $owners)->where('date_target', $date)->where('counting_unit', $unit); }); } $dataFromMiddleTable = $orderSqlBuilder->get(); $this->insertIntoCache($dataFromMiddleTable, $unit); $queryCondition = $this->unQueryCondition($dataFromMiddleTable, $queryCondition); $orderCountingRecords_fromSelfTable = $dataFromMiddleTable; $lackingCondition = $queryCondition; return [$orderCountingRecords_fromSelfTable, $lackingCondition]; } /** * 根据$queryCondition从多种数据源中查询数据 * 并将结果插入中间表或者缓存 * @param $queryCondition * @return Collection|mixed|\Tightenco\Collect\Support\Collection */ public function getOrderCountingRecords($queryCondition) { list($orderCountingRecords_fromCache, $lackingCondition) = $this->getFromCache($queryCondition); if (empty($lackingCondition['data'])) { return $orderCountingRecords_fromCache; } list($orderCountingRecords_fromSelfTable, $lackingCondition) = $this->getFromMiddleTable($lackingCondition); if (empty($lackingCondition['data'])) { return $orderCountingRecords_fromCache->concat($orderCountingRecords_fromSelfTable); } /** * 1 如果查询粒度大于日 则进入getFromLowerUnit() * 2 如果查询粒度为日 则进入dataFromOrder() */ if ($lackingCondition['unit'] != '日') {//日为最低粒度,不用进入此方法 $orderCountingRecords_combinedByLower = $this->getFromLowerUnit($lackingCondition); } else { $orderCountingRecords_FromOrder = $this->dataFromOrder($lackingCondition); } $result = collect(); if (isset($orderCountingRecords_FromOrder) && !$orderCountingRecords_FromOrder->isEmpty()) { $result = $result->concat($orderCountingRecords_FromOrder); } if (!$orderCountingRecords_fromCache->isEmpty()) { $result = $result->concat($orderCountingRecords_fromCache); } if (!$orderCountingRecords_fromSelfTable->isEmpty()) { $result = $result->concat($orderCountingRecords_fromSelfTable); } if (isset($orderCountingRecords_combinedByLower)) { $result = $result->concat($orderCountingRecords_combinedByLower); } return $result; } public function getFromLowerUnit($queryCondition) { switch ($queryCondition['unit']) { case '年': $lowUnit = '月'; $conditionClone = ['unit' => $lowUnit, 'data' => []]; foreach ($queryCondition['data'] as $date => $ownerIds) { $startAt = $date; $endAt = Carbon::parse($date)->endOfYear()->toDateString(); $items = $this->transfersToCondition( $startAt, $endAt, $lowUnit, $ownerIds )['data']; foreach ($items as $dateStr => $item) { if (empty($conditionClone['data'][$dateStr])) $conditionClone['data'][$dateStr] = $dateStr; $conditionClone['data'][$dateStr] = ($item); } } $orderCountingRecords_days = $this->getOrderCountingRecords($conditionClone); $orderCountingRecords_combinedByLower = $this->turnGradingUpToLow($orderCountingRecords_days, $lowUnit, 'year'); $this->insertIntoMiddleTable($orderCountingRecords_combinedByLower, '年'); $this->insertIntoCache(collect($orderCountingRecords_combinedByLower), '年'); break; case '月': $lowUnit = '日'; $conditionClone = ['unit' => $lowUnit, 'data' => []]; foreach ($queryCondition['data'] as $date => $ownerIds) { $startAt = $date; $endAt = Carbon::parse($date)->endOfMonth()->toDateString(); $items = $this->transfersToCondition( $startAt, $endAt, $lowUnit, $ownerIds )['data']; foreach ($items as $dateStr => $item) { if (empty($conditionClone['data'][$dateStr])) $conditionClone['data'][$dateStr] = $dateStr; $conditionClone['data'][$dateStr] = ($item); } } $orderCountingRecords_days = $this->getOrderCountingRecords($conditionClone); $orderCountingRecords_combinedByLower = $this->turnGradingUpToLow($orderCountingRecords_days, $lowUnit, 'month'); $this->insertIntoMiddleTable($orderCountingRecords_combinedByLower, '月'); $this->insertIntoCache(collect($orderCountingRecords_combinedByLower), '月'); break; case '日': return [[], $queryCondition]; default: } return $orderCountingRecords_combinedByLower; } public function transfersToCondition($start, $end, $unit, $ownerIds): array { if (Carbon::parse($end)->gt(now())) $end = now()->toDateString();//如果$end超过当前时间,将end置为当前时间 $condition = [ 'data' => [], 'unit' => $unit,]; $startAt = Carbon::parse($start); $dates = []; switch ($unit) { case '年': $dates = collect($startAt->yearsUntil($end, 1)->toArray()) ->map(function (Carbon $date) { return $date->firstOfYear(); }); break; case '月': $dates = collect($startAt->monthsUntil($end, 1)->toArray()) ->map(function (Carbon $date) { return $date->firstOfMonth(); }); break; case '日': $dates = collect($startAt->daysUntil($end, 1)->toArray()) ->map(function (Carbon $date) { return $date->startOfDay(); }); break; default: } foreach ($dates as $day) { $condition['data'][$day->toDateString()] = $ownerIds; } return $condition; } public function dataFromOrder($queryCondition) { $orderSqlBuilder = Order::query()->selectRaw("owner_id,warehouse_id,shop_id,logistic_id,count(1) as amounts ,DATE_FORMAT(updated_at,'%Y-%m-%d') as date_target"); foreach ($queryCondition['data'] as $dateStr => $ownerIds) { $orderSqlBuilder->orWhere(function ($query) use ($ownerIds, $dateStr) { $query->whereIn('owner_id', $ownerIds)->where('updated_at', '>=', $dateStr)->where('updated_at', '<', Carbon::parse($dateStr)->addDay()->toDateString())->where('wms_status', '订单完成'); }); } $dataFromOrder = $orderSqlBuilder->groupBy(['owner_id', 'warehouse_id', 'shop_id', 'logistic_id', 'date_target'])->get(); $dataFromMiddleTable = $this->ordersToOrderCountingRecords($dataFromOrder, $queryCondition['unit']); $this->setDefultData($dataFromMiddleTable, $queryCondition); $this->insertIntoMiddleTable($dataFromMiddleTable, $queryCondition['unit']); $this->insertIntoCache($dataFromMiddleTable, $queryCondition['unit']); return $dataFromMiddleTable; } public function insertIntoMiddleTable($data, $unit) { $filter = $data->filter(function ($item) use ($unit) { return $this->isNotCurrentDate($item->date_target, $unit); }); $filter->chunk(1000)->each(function ($item) { OrderCountingRecord::query()->insert($item->toArray()); }); } public function insertIntoCache($collect, $unit) { $map = []; $collect->each(function ($order) use ($unit, &$map) { $key = "order_counting_records_{$order->date_target}_{$order->owner_id}_{$unit}"; if (empty($map[$key])) $map[$key] = []; $map[$key][] = $order; }); foreach ($map as $key => $orders) { Cache::put($key, collect($orders), $this->getTtl($orders[0]['date_target'], $unit)); } } public function isNotCurrentDate($dateStr, $unit): bool { switch ($unit) { case '月': return !Carbon::parse($dateStr)->isCurrentMonth(); case '年': return !Carbon::parse($dateStr)->isCurrentYear(); default: return !Carbon::parse($dateStr)->isCurrentDay(); } } public function unQueryCondition($dataFromMiddleTable, $unQueryCondition) { $map = []; $dataFromMiddleTable->each(function ($item) use (&$map) { $key = 'owner_id=' . $item->owner_id . ' date_target' . $item->date_target; $map[$key] = true; }); foreach ($unQueryCondition['data'] as $dateStr => $ownerIds) { foreach ($ownerIds as $key => $ownerId) { $key1 = 'owner_id=' . $ownerId . ' date_target' . $dateStr; if (isset($map[$key1])) { //中间表查找到数据 unset($unQueryCondition['data'][$dateStr][$key]); } } if (empty($unQueryCondition['data'][$dateStr])) { unset($unQueryCondition['data'][$dateStr]); } } return $unQueryCondition; } public function ordersToOrderCountingRecords($dataFromOrder, $unit) { $dataFromMiddleTable = collect(); //当日当周当月当年的数据不能插入到中间表 $dateTime = Carbon::now()->toDateTimeString(); foreach ($dataFromOrder as $order) { $carbon = Carbon::parse($order->date_target); $year = $carbon->year; $week = $carbon->week; $month = $carbon->month; $dataFromMiddleTable->push(new OrderCountingRecord([ 'owner_id' => $order->owner_id, 'shop_id' => $order->shop_id, 'warehouse_id' => $order->warehouse_id, 'logistic_id' => $order->logistic_id, 'date_target' => $order->date_target, 'counting_unit' => $unit, 'amount' => $order->amounts ?? 0, 'week' => $year . '-' . $week, 'month' => $year . '-' . $month, 'year' => $year . '', 'created_at' => $dateTime, 'updated_at' => $dateTime, ])); } return $dataFromMiddleTable; } public function count($orderCountingRecords_days, $unit) { $isEnd = false; $result = collect(); $item = null; $amount = 0; foreach ($orderCountingRecords_days as $itemToCount) { if ($itemToCount instanceof OrderCountingRecord) { if (!$isEnd) { $isEnd = true; $item = clone($itemToCount); } $amount += $itemToCount->amount; } else { $result = $result->concat($this->count($itemToCount, $unit)); } } if ($isEnd) { switch ($unit) { case "日": $item['date_target'] = Carbon::parse($item['date_target'])->firstOfMonth()->toDateString(); $item['counting_unit'] = '月'; break; case "月": $item['date_target'] = Carbon::parse($item['date_target'])->firstOfYear()->toDateString(); $item['counting_unit'] = '年'; break; } $item['amount'] = $amount; return $result->push(new OrderCountingRecord([ 'owner_id' => $item->owner_id, 'shop_id' => $item->shop_id, 'warehouse_id' => $item->warehouse_id, 'logistic_id' => $item->logistic_id, 'date_target' => $item->date_target, 'counting_unit' => $item->counting_unit, 'amount' => $item->amount, 'year' => $item->year, 'month' => $item->month, 'week' => $item->week ])); } return $result; } public function turnGradingUpToLow($orderCountingRecords_days, $unit, $grading) { $orderCountingRecords_days = $orderCountingRecords_days->groupBy(['owner_id', 'shop_id', 'warehouse_id', 'logistic_id', $grading]); return $this->count($orderCountingRecords_days, $unit); } public function setDefultData($dataFromMiddleTable, $queryCondition) { $map = []; foreach ($dataFromMiddleTable as $data) { if (empty($map[$data->date_target . '-' . $data->owner_id])) $map[$data->date_target . '-' . $data->owner_id] = true; } foreach ($queryCondition['data'] as $dateStr => $ownerIds) { foreach ($ownerIds as $ownerId) { if (empty($map[$dateStr . '-' . $ownerId]) && Carbon::parse($dateStr)->isBefore(Carbon::now())) { $carbon = Carbon::parse($dateStr); $year = $carbon->year; $week = $carbon->week; $month = $carbon->month; $dataFromMiddleTable->push(new OrderCountingRecord([ 'owner_id' => $ownerId, 'shop_id' => null, 'warehouse_id' => null, 'logistic_id' => null, 'date_target' => $dateStr, 'counting_unit' => $queryCondition['unit'], 'amount' => 0, 'week' => $year . '-' . $week, 'month' => $year . '-' . $month, 'year' => $year . '', 'created_at' => $carbon->toDateString(), 'updated_at' => $carbon->toDateString(), ])); } } } } private function getTtl($dateStr, $unit) { if ($this->isNotCurrentDate($dateStr, $unit)) { LogService::log('NewOrderCountingRecordService', '缓存设置为永久', $dateStr); $ttl = config('cache.expirations.orderCountingRecord');//非当前日期的缓存为永久 } else { LogService::log('NewOrderCountingRecordService', '缓存设置为临时', $dateStr); $ttl = config('cache.expirations.orderCountingRecord');//当前日期缓存为1800s } return $ttl; } public function getWareHouseRecordsApi($start, $end, $ownerIds): array { $orderCountingRecords = OrderCountingRecord::query() ->selectRaw("sum(amount) as value ,warehouse_id") ->with('warehouse:id,name,code') ->whereBetween('date_target', [$start, $end]) ->whereIn('owner_id', $ownerIds) ->groupBy('warehouse_id') ->get(); $result = []; foreach ($orderCountingRecords as $orderCountingRecord) { $result[] = [ 'logistic_id' => $orderCountingRecord->warehouse_id, 'value' => $orderCountingRecord->value, 'name' => $orderCountingRecord->warehouse->name ?? '', 'code' => $orderCountingRecord->warehouse->code ?? '', ]; } return $result; } public function getLogisticRecordsApi($start, $end, $ownerIds): array { $orderCountingRecords = OrderCountingRecord::query() ->selectRaw("sum(amount) as value ,logistic_id") ->with('logistic:id,name') ->whereBetween('date_target', [$start, $end]) ->whereIn('owner_id', $ownerIds) ->groupBy('logistic_id') ->get(); $result = []; foreach ($orderCountingRecords as $orderCountingRecord) { $result[] = [ 'logistic_id' => $orderCountingRecord->logistic_id, 'value' => $orderCountingRecord->value, 'name' => $orderCountingRecord->logistic->name ?? '', ]; } return $result; } /** * 查询订单量趋势 * @param $start string * @param $end string * @param $unit string * @param $ownerIds array * @return Builder[] */ public function getOrderCountingRecordsApi(string $start, string $end, string $unit, array $ownerIds): array { $orderCountingRecords = OrderCountingRecord::query() ->selectRaw("sum(amount) as counter ,date_target") ->whereBetween('date_target', [$start, $end]) ->where('counting_unit', $unit) ->whereIn('owner_id', $ownerIds) ->groupBy('date_target') ->get()->toArray(); if (now()->toDateString() == $end) {//查询时间包含当天 switch ($unit) { case '日': //查询当天统计 $startDateTime = Carbon::parse($end)->startOfDay()->toDateTimeString(); $endDateTime = Carbon::parse($end)->endOfDay()->toDateTimeString(); $date_format = "'%Y-%m-%d'"; break; case '月': //查询当月统计 $startDateTime = Carbon::parse($end)->startOfMonth()->startOfDay()->toDateTimeString(); $endDateTime = Carbon::parse($end)->endOfMonth()->endOfDay()->toDateTimeString(); $date_format = "'%Y-%m'"; break; case '年': //查询当年统计 $startDateTime = Carbon::parse($end)->startOfYear()->startOfDay()->toDateTimeString(); $endDateTime = Carbon::parse($end)->endOfYear()->endOfDay()->toDateTimeString(); $date_format = "'%Y'"; break; } $order = Order::query() ->selectRaw("count(1) as amounts ,DATE_FORMAT(updated_at,{$date_format}) as date_target") ->whereBetween('updated_at', [$startDateTime, $endDateTime]) ->where('wms_status', '订单完成') ->whereIn('owner_id', $ownerIds) ->groupBy('date_target') ->first(); $orderCountingRecords[] = [ "counter" => $order->amounts ?? 0, "date_target" => $order->date_target ?? (function () use ($unit) { switch ($unit) { case '日': return now()->toDateString(); case '月': return now()->startOfMonth()->toDateString(); case '年': return now()->startOfYear()->toDateString(); } })(), ]; } return $orderCountingRecords; } /** * 统计订单量 从$start 开始统计 默认截止到当前日期的前一天 * @param $start string * @param null $end string * @param $unit string */ public function recordOrder(string $start, $end = null, string $unit = '日') { ini_set('max_execution_time', 2 * 60 * 60); ini_set('memory_limit', '1024M'); switch ($unit) { case '日': $this->recordByDay($start, $end, $unit); break; case'月': $this->recordByMonth($start, $end, $unit); break; case'年': $this->recordByYear($start, $end, $unit); break; default: } } /** * 清空统计缓存 */ public function clearCacheOrderRecord() { } /** * 日统计 * @param string $start * @param $end * @param string $unit */ public function recordByDay(string $start, $end = null, string $unit = '日'): void { $startDateTime = Carbon::parse($start)->startOfDay()->toDateTimeString(); if (is_null($end)) { $end = now()->subDay()->endOfDay(); } $endDateTime = Carbon::parse($end)->endOfDay()->toDateTimeString(); Order::query() ->selectRaw("owner_id,warehouse_id,logistic_id,count(1) as amounts ,DATE_FORMAT(updated_at,'%Y-%m-%d') as date_target") ->whereBetween('updated_at', [$startDateTime, $endDateTime]) ->where('wms_status', '订单完成') ->groupBy('owner_id', 'warehouse_id', 'logistic_id', 'date_target') ->chunk(1000, function ($orders) use ($unit) { $insertData = []; foreach ($orders as $order) { $insertData[] = [ 'owner_id' => $order->owner_id, 'warehouse_id' => $order->warehouse_id, 'logistic_id' => $order->logistic_id, 'date_target' => $order->date_target, 'counting_unit' => $unit, 'amount' => $order->amounts ?? 0, 'year' => Carbon::parse($order->date_target)->year, 'month' => Carbon::parse($order->date_target)->year . '-' . Carbon::parse($order->date_target)->month, ]; } OrderCountingRecord::query()->insert($insertData); }); } public function recordByMonth(string $start, $end = null, $unit = '月') { $startDate = Carbon::parse($start)->startOfMonth()->toDateString(); if (is_null($end)) { $end = now()->subMonth()->endOfDay(); } $endDate = Carbon::parse($end)->endOfDay()->toDateString(); OrderCountingRecord::query() ->selectRaw("owner_id,warehouse_id,logistic_id,sum(amount) as amount_sum,month,year,date_target") ->whereBetween('date_target', [$startDate, $endDate]) ->where('counting_unit', '日') ->groupBy('owner_id', 'warehouse_id', 'logistic_id', 'month') ->chunk(1000, function ($records) use ($unit) { $insertData = []; foreach ($records as $record) { $insertData[] = [ 'owner_id' => $record->owner_id, 'warehouse_id' => $record->warehouse_id, 'logistic_id' => $record->logistic_id, 'counting_unit' => $unit, 'date_target' => Carbon::parse($record->date_target)->startOfMonth()->toDateString(), 'amount' => $record->amount_sum, 'year' => $record->year, 'month' => $record->month, ]; } OrderCountingRecord::query()->insertOrIgnore($insertData); }); } public function recordByYear(string $start, $end = null, $unit = '年') { $startYear = Carbon::parse($start)->year; if (is_null($end)) { $end = now()->subYear()->toDateString(); } $endYear = Carbon::parse($end)->year; $orderCountingRecords = OrderCountingRecord::query() ->selectRaw("owner_id,warehouse_id,logistic_id,sum(amount) as amount_sum,year,date_target") ->whereBetween('year', [$startYear, $endYear]) ->groupBy('owner_id', 'warehouse_id', 'logistic_id', 'year') ->get(); $insertData = []; foreach ($orderCountingRecords as $orderCountingRecord) { $insertData[] = [ 'owner_id' => $orderCountingRecord->owner_id, 'warehouse_id' => $orderCountingRecord->warehouse_id, 'logistic_id' => $orderCountingRecord->logistic_id, 'counting_unit' => $unit, 'date_target' => Carbon::parse($orderCountingRecord->date_target)->startOfYear()->toDateString(), 'amount' => $orderCountingRecord->amount_sum, 'year' => $orderCountingRecord->year, ]; } OrderCountingRecord::query()->insertOrIgnore($insertData); } }