NewOrderCountingRecordService.php 21 KB

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