NewOrderCountingRecordService.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <?php
  2. namespace App\Services;
  3. use App\Logistic;
  4. use App\Order;
  5. use App\OrderCountingRecord;
  6. use App\Warehouse;
  7. use Carbon\Carbon;
  8. use Illuminate\Support\Facades\Cache;
  9. class NewOrderCountingRecordService
  10. {
  11. public function orderCountingRecords($start, $end, $unit, $ownerIds)
  12. {
  13. $orders = $this->get($start, $end, $unit, $ownerIds);
  14. $dataList = collect();
  15. $orders->groupBy('date_target')->sortKeys()->each(function ($items) use (&$dataList, $unit) {
  16. $counter = $items->reduce(function ($sum, $item) {
  17. return $sum + $item->amount;
  18. }, 0);
  19. $date_target = $items[0]->date_target;
  20. $dataList->push([
  21. 'counter' => $counter,
  22. 'date_target' => $date_target,
  23. ]);
  24. });
  25. return $dataList;
  26. }
  27. public function logisticsCountingRecords($start, $end, $ownerIds)
  28. {
  29. $key = 'logisticsCountingRecords_' . $start . '_' . $end . '_' . json_encode($ownerIds);
  30. return Cache::remember($key, config('cache.expirations.oftenChange'), function () use ($start, $end, $ownerIds) {
  31. $dataList = collect();
  32. $resultOrders = $this->get($start, $end, '日', $ownerIds);
  33. $resultOrders->groupBy('logistic_id')->each(function ($item) use (&$dataList) {
  34. $counter = $item->reduce(function ($sum, $item) {
  35. return $sum + $item->amount;
  36. }, 0);
  37. $dataList->push([
  38. 'value' => $counter,
  39. 'logistic_id' => $item[0]->logistic_id,
  40. ]);
  41. });
  42. $map = [];
  43. $logistics = Logistic::query()->whereIn('id', data_get($dataList, '*.logistic_id'))->get();
  44. $logistics->each(function ($logistic) use (&$map) {
  45. $map[$logistic->id] = $logistic;
  46. });
  47. return $dataList->map(function (&$item) use ($map) {
  48. $logistic = $map[$item['logistic_id']] ?? '';
  49. $item['name'] = $logistic->name ?? '';
  50. return $item;
  51. });
  52. });
  53. }
  54. public function warehouseCountingRecords($start, $end, $ownerIds)
  55. {
  56. $key = 'warehouseCountingRecords_' . $start . '_' . $end . '_' . json_encode($ownerIds);
  57. return Cache::remember($key, config('cache.expirations.oftenChange'), function () use ($start, $end, $ownerIds) {
  58. $dataList = collect();
  59. $resultOrders = $this->get($start, $end, '日', $ownerIds);
  60. $resultOrders->groupBy('warehouse_id')->each(function ($item) use (&$dataList) {
  61. $counter = $item->reduce(function ($sum, $item) {
  62. return $sum + $item->amount;
  63. }, 0);
  64. $dataList->push([
  65. 'value' => $counter,
  66. 'warehouse_id' => $item[0]->warehouse_id,
  67. ]);
  68. });
  69. $map = [];
  70. $logistics = Warehouse::query()->whereIn('id', data_get($dataList, '*.warehouse_id'))->get();
  71. $logistics->each(function ($warehouse) use (&$map) {
  72. $map[$warehouse->id] = $warehouse;
  73. });
  74. return $dataList->map(function (&$item) use ($map) {
  75. $warehouse = $map[$item['warehouse_id']] ?? '';
  76. $item['code'] = $warehouse->name ?? '';
  77. switch ($item['code']) {
  78. case 'WH01':
  79. $item['name'] = '松江一仓';
  80. break;
  81. case 'WH02':
  82. $item['name'] = '松江二仓';
  83. break;
  84. case 'WH03':
  85. $item['name'] = '嘉定一仓';
  86. break;
  87. default:
  88. $item['name'] = '仓库为空';
  89. break;
  90. }
  91. return $item;
  92. });
  93. });
  94. }
  95. public function get($start, $end, $unit, $ownerIds)
  96. {
  97. $queryCondition = $this->transfersToCondition($start, $end, $unit, $ownerIds);
  98. return $this->getOrderCountingRecords($queryCondition);
  99. }
  100. public function getFromCache($queryCondition): array
  101. {
  102. $lackingCondition = [];
  103. $orderCountingRecords_FromCache = collect();
  104. $lackingCondition['unit'] = $queryCondition['unit'];
  105. foreach ($queryCondition['data'] as $dateStr => $ownerIds) {
  106. foreach ($ownerIds as $ownerId) {
  107. $key = 'order_counting_records_' . $dateStr . '_' . $ownerId . '_' . $queryCondition['unit'];
  108. $items = cache()->get($key);
  109. if (empty($items)) {
  110. if (empty($lackingCondition['data'][$dateStr])) {
  111. $lackingCondition['data'][$dateStr] = [];
  112. }
  113. $lackingCondition['data'][$dateStr][] = $ownerId;
  114. } else {
  115. foreach ($items as $item) {
  116. $orderCountingRecords_FromCache->push($item);
  117. }
  118. }
  119. }
  120. }
  121. return [$orderCountingRecords_FromCache, $lackingCondition];
  122. }
  123. public function getFromMiddleTable($queryCondition): array
  124. {
  125. $orderSqlBuilder = OrderCountingRecord::query();
  126. $unit = $queryCondition['unit'];
  127. foreach ($queryCondition['data'] as $date => $owners) {
  128. $orderSqlBuilder->orWhere(function ($query) use ($owners, $date, $unit) {
  129. $query->whereIn('owner_id', $owners)->where('date_target', $date)->where('counting_unit', $unit);
  130. });
  131. }
  132. $dataFromMiddleTable = $orderSqlBuilder->get();
  133. $this->insertIntoCache($dataFromMiddleTable, $unit);
  134. $queryCondition = $this->unQueryCondition($dataFromMiddleTable, $queryCondition);
  135. $orderCountingRecords_fromSelfTable = $dataFromMiddleTable;
  136. $lackingCondition = $queryCondition;
  137. return [$orderCountingRecords_fromSelfTable, $lackingCondition];
  138. }
  139. public function getOrderCountingRecords($queryCondition)
  140. {
  141. list($orderCountingRecords_fromCache, $lackingCondition)
  142. = $this->getFromCache($queryCondition);
  143. if (empty($lackingCondition['data'])) {
  144. return $orderCountingRecords_fromCache;
  145. }
  146. list($orderCountingRecords_fromSelfTable, $lackingCondition)
  147. = $this->getFromMiddleTable($lackingCondition);
  148. if (empty($lackingCondition['data'])) {
  149. return $orderCountingRecords_fromCache->concat($orderCountingRecords_fromSelfTable);
  150. }
  151. /**
  152. * 1 如果查询粒度大于日 则进入getFromLowerUnit()
  153. * 2 如果查询粒度为日 则进入dataFromOrder()
  154. */
  155. if ($lackingCondition['unit'] != '日') {//日为最低粒度,不用进入此方法
  156. $orderCountingRecords_combinedByLower = $this->getFromLowerUnit($lackingCondition);
  157. }else{
  158. $orderCountingRecords_FromOrder = $this->dataFromOrder($lackingCondition);
  159. }
  160. $result = collect();
  161. if (isset($orderCountingRecords_FromOrder) && !$orderCountingRecords_FromOrder->isEmpty()) {
  162. $result = $result->concat($orderCountingRecords_FromOrder);
  163. }
  164. if (!$orderCountingRecords_fromCache->isEmpty()) {
  165. $result = $result->concat($orderCountingRecords_fromCache);
  166. }
  167. if (!$orderCountingRecords_fromSelfTable->isEmpty()) {
  168. $result = $result->concat($orderCountingRecords_fromSelfTable);
  169. }
  170. if (isset($orderCountingRecords_combinedByLower)) {
  171. $result = $result->concat($orderCountingRecords_combinedByLower);
  172. }
  173. return $result;
  174. }
  175. public function getFromLowerUnit($queryCondition)
  176. {
  177. switch ($queryCondition['unit']) {
  178. case '年':
  179. $lowUnit = '月';
  180. $conditionClone = ['unit' => $lowUnit, 'data' => []];
  181. foreach ($queryCondition['data'] as $date => $ownerIds) {
  182. $startAt = $date;
  183. $endAt = Carbon::parse($date)->endOfYear()->toDateString();
  184. $items = $this->transfersToCondition(
  185. $startAt, $endAt, $lowUnit, $ownerIds
  186. )['data'];
  187. foreach ($items as $dateStr => $item) {
  188. if (empty($conditionClone['data'][$dateStr])) $conditionClone['data'][$dateStr] = $dateStr;
  189. $conditionClone['data'][$dateStr] = ($item);
  190. }
  191. }
  192. $orderCountingRecords_days = $this->getOrderCountingRecords($conditionClone);
  193. $orderCountingRecords_combinedByLower = $this->turnGradingUpToLow($orderCountingRecords_days, $lowUnit, 'year');
  194. $this->insertIntoMiddleTable($orderCountingRecords_combinedByLower, '年');
  195. $this->insertIntoCache(collect($orderCountingRecords_combinedByLower),'年');
  196. break;
  197. case '月':
  198. $lowUnit = '日';
  199. $conditionClone = ['unit' => $lowUnit, 'data' => []];
  200. foreach ($queryCondition['data'] as $date => $ownerIds) {
  201. $startAt = $date;
  202. $endAt = Carbon::parse($date)->endOfMonth()->toDateString();
  203. $items = $this->transfersToCondition(
  204. $startAt, $endAt, $lowUnit, $ownerIds
  205. )['data'];
  206. foreach ($items as $dateStr => $item) {
  207. if (empty($conditionClone['data'][$dateStr])) $conditionClone['data'][$dateStr] = $dateStr;
  208. $conditionClone['data'][$dateStr] = ($item);
  209. }
  210. }
  211. $orderCountingRecords_days = $this->getOrderCountingRecords($conditionClone);
  212. $orderCountingRecords_combinedByLower = $this->turnGradingUpToLow($orderCountingRecords_days, $lowUnit, 'month');
  213. $this->insertIntoMiddleTable($orderCountingRecords_combinedByLower, '月');
  214. $this->insertIntoCache(collect($orderCountingRecords_combinedByLower),'月');
  215. break;
  216. case '日':
  217. return [[], $queryCondition];
  218. default:
  219. }
  220. return $orderCountingRecords_combinedByLower;
  221. }
  222. public function transfersToCondition($start, $end, $unit, $ownerIds): array
  223. {
  224. $condition = [
  225. 'data' => [],
  226. 'unit' => $unit,];
  227. $startAt = Carbon::parse($start);
  228. $dates = [];
  229. switch ($unit) {
  230. case '年':
  231. $dates = collect($startAt->yearsUntil($end, 1)->toArray())
  232. ->map(function (Carbon $date) {
  233. return $date->firstOfYear();
  234. });
  235. break;
  236. case '月':
  237. $dates = collect($startAt->monthsUntil($end, 1)->toArray())
  238. ->map(function (Carbon $date) {
  239. return $date->firstOfMonth();
  240. });
  241. break;
  242. case '日':
  243. $dates = collect($startAt->daysUntil($end, 1)->toArray())
  244. ->map(function (Carbon $date) {
  245. return $date->startOfDay();
  246. });
  247. break;
  248. default:
  249. }
  250. foreach ($dates as $day) {
  251. $condition['data'][$day->toDateString()] = $ownerIds;
  252. }
  253. return $condition;
  254. }
  255. public function dataFromOrder($queryCondition)
  256. {
  257. $orderSqlBuilder = Order::query()->selectRaw("owner_id,warehouse_id,shop_id,logistic_id,count(1) as amounts ,DATE_FORMAT(created_at,'%Y-%m-%d') as date_target");
  258. foreach ($queryCondition['data'] as $dateStr => $ownerIds) {
  259. $orderSqlBuilder->orWhere(function ($query) use ($ownerIds, $dateStr) {
  260. $query->whereIn('owner_id', $ownerIds)->where('created_at', '>=', $dateStr)->where('created_at', '<', Carbon::parse($dateStr)->addDay()->toDateString())->where('wms_status', '订单完成');
  261. });
  262. }
  263. $dataFromOrder = $orderSqlBuilder->groupBy(['owner_id', 'warehouse_id', 'shop_id', 'logistic_id', 'date_target'])->get();
  264. $dataFromMiddleTable = $this->ordersToOrderCountingRecords($dataFromOrder, $queryCondition['unit']);
  265. $this->setDefultData($dataFromMiddleTable, $queryCondition);
  266. $this->insertIntoMiddleTable($dataFromMiddleTable, $queryCondition['unit']);
  267. $this->insertIntoCache($dataFromMiddleTable, $queryCondition['unit']);
  268. return $dataFromMiddleTable;
  269. }
  270. public function insertIntoMiddleTable($data, $unit)
  271. {
  272. $filter = $data->filter(function ($item) use ($unit) {
  273. return $this->isNotCurrentDate($item->date_target, $unit);
  274. });
  275. $filter->chunk(1000)->each(function ($item) {
  276. $insert_params = [];
  277. foreach ($item as $data) {
  278. $insert_params[] =$data->toArray();
  279. }
  280. OrderCountingRecord::query()->insert($insert_params);
  281. unset($insert_params);
  282. });
  283. }
  284. public function insertIntoCache($collect, $unit)
  285. {
  286. $map = [];
  287. $collect->each(function ($order) use ($unit, &$map) {
  288. $key = "order_counting_records_{$order->date_target}_{$order->owner_id}_{$unit}";
  289. if (empty($map[$key])) $map[$key] = [];
  290. $map[$key][] = $order;
  291. });
  292. foreach ($map as $key => $orders) {
  293. $ttl = config('cache.expirations.forever');
  294. if (!$this->isNotCurrentDate($orders[0]['date_target'], $unit)) {
  295. $ttl = config('cache.expirations.oftenChange');
  296. }
  297. Cache::put($key, collect($orders), $ttl);
  298. }
  299. }
  300. public function isNotCurrentDate($dateStr, $unit): bool
  301. {
  302. switch ($unit) {
  303. case '月':
  304. return !Carbon::parse($dateStr)->isCurrentMonth();
  305. case '年':
  306. return !Carbon::parse($dateStr)->isCurrentYear();
  307. default:
  308. return !Carbon::parse($dateStr)->isCurrentDay();
  309. }
  310. }
  311. public function unQueryCondition($dataFromMiddleTable, $unQueryCondition)
  312. {
  313. $map = [];
  314. $dataFromMiddleTable->each(function ($item) use (&$map) {
  315. $key = 'owner_id=' . $item->owner_id . ' date_target' . $item->date_target;
  316. $map[$key] = true;
  317. });
  318. foreach ($unQueryCondition['data'] as $dateStr => $ownerIds) {
  319. foreach ($ownerIds as $key => $ownerId) {
  320. $key1 = 'owner_id=' . $ownerId . ' date_target' . $dateStr;
  321. if (isset($map[$key1])) {
  322. //中间表查找到数据
  323. unset($unQueryCondition['data'][$dateStr][$key]);
  324. }
  325. }
  326. if (empty($unQueryCondition['data'][$dateStr])) {
  327. unset($unQueryCondition['data'][$dateStr]);
  328. }
  329. }
  330. return $unQueryCondition;
  331. }
  332. public function ordersToOrderCountingRecords($dataFromOrder, $unit)
  333. {
  334. $dataFromMiddleTable = collect();
  335. //当日当周当月当年的数据不能插入到中间表
  336. $dateTime = Carbon::now()->toDateTimeString();
  337. foreach ($dataFromOrder as $order) {
  338. $carbon = Carbon::parse($order->date_target);
  339. $year = $carbon->year;
  340. $week = $carbon->week;
  341. $month = $carbon->month;
  342. $dataFromMiddleTable->push(new OrderCountingRecord([
  343. 'owner_id' => $order->owner_id,
  344. 'shop_id' => $order->shop_id,
  345. 'warehouse_id' => $order->warehouse_id,
  346. 'logistic_id' => $order->logistic_id,
  347. 'date_target' => $order->date_target,
  348. 'counting_unit' => $unit,
  349. 'amount' => $order->amounts,
  350. 'week' => $year . '-' . $week,
  351. 'month' => $year . '-' . $month,
  352. 'year' => $year . '',
  353. 'created_at' => $dateTime,
  354. 'updated_at' => $dateTime,
  355. ]));
  356. }
  357. return $dataFromMiddleTable;
  358. }
  359. public function count($orderCountingRecords_days, $unit)
  360. {
  361. $isEnd = false;
  362. $result = collect();
  363. $item = null;
  364. $amount = 0;
  365. foreach ($orderCountingRecords_days as $itemToCount) {
  366. if ($itemToCount instanceof OrderCountingRecord) {
  367. if (!$isEnd) {
  368. $isEnd = true;
  369. $item = clone($itemToCount);
  370. }
  371. $amount += $itemToCount->amount;
  372. } else {
  373. $result = $result->concat($this->count($itemToCount, $unit));
  374. }
  375. }
  376. if ($isEnd) {
  377. switch ($unit) {
  378. case "日":
  379. $item['date_target'] = Carbon::parse($item['date_target'])->firstOfMonth()->toDateString();
  380. $item['counting_unit'] = '月';
  381. break;
  382. case "月":
  383. $item['date_target'] = Carbon::parse($item['date_target'])->firstOfYear()->toDateString();
  384. $item['counting_unit'] = '年';
  385. break;
  386. }
  387. $item['amount'] = $amount;
  388. return $result->push($item);
  389. }
  390. return $result;
  391. }
  392. public function turnGradingUpToLow($orderCountingRecords_days, $unit, $grading)
  393. {
  394. $orderCountingRecords_days = $orderCountingRecords_days->groupBy(['owner_id', 'shop_id', 'warehouse_id', 'logistic_id', $grading]);
  395. return $this->count($orderCountingRecords_days, $unit);
  396. }
  397. public function setDefultData($dataFromMiddleTable, $queryCondition)
  398. {
  399. $map = [];
  400. foreach ($dataFromMiddleTable as $data) {
  401. if (empty($map[$data->date_target . '-' . $data->owner_id])) $map[$data->date_target . '-' . $data->owner_id] = true;
  402. }
  403. foreach ($queryCondition['data'] as $dateStr => $ownerIds) {
  404. foreach ($ownerIds as $ownerId) {
  405. if (empty($map[$dateStr . '-' . $ownerId]) && Carbon::parse($dateStr)->isBefore(Carbon::now())) {
  406. $carbon = Carbon::parse($dateStr);
  407. $year = $carbon->year;
  408. $week = $carbon->week;
  409. $month = $carbon->month;
  410. $dataFromMiddleTable->push(new OrderCountingRecord([
  411. 'owner_id' => $ownerId,
  412. 'shop_id' => null,
  413. 'warehouse_id' => null,
  414. 'logistic_id' => null,
  415. 'date_target' => $dateStr,
  416. 'counting_unit' => $queryCondition['unit'],
  417. 'amount' => 0,
  418. 'week' => $year . '-' . $week,
  419. 'month' => $year . '-' . $month,
  420. 'year' => $year . '',
  421. 'created_at' => $carbon->toDateString(),
  422. 'updated_at' => $carbon->toDateString(),
  423. ]));
  424. }
  425. }
  426. }
  427. }
  428. }