StoreReserveSettingService.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <?php
  2. namespace addons\Store\common\services;
  3. use addons\ReservationService\common\enums\ReservationServiceEnum;
  4. use addons\ReservationService\common\models\ReservationServiceOccupy;
  5. use addons\ReservationService\common\models\ReservationServiceOrder;
  6. use addons\ReservationService\common\models\ReservationServiceStoreGoods;
  7. use addons\ReservationService\common\models\ReservationServiceUser;
  8. use addons\Store\common\enums\ReserveServiceEnum;
  9. use addons\Store\common\models\Store;
  10. use addons\Store\common\models\StoreGoodsReserveSku;
  11. use addons\Store\common\models\StoreOrderReserve;
  12. use addons\Store\common\models\StoreReserveSetting;
  13. use addons\Store\common\models\StoreReserveTechnicianScheduling;
  14. use addons\Store\common\models\StoreReserveTechnicianSchedulingTime;
  15. use common\enums\StatusEnum;
  16. use common\helpers\ArrayHelper;
  17. use common\models\goods\GoodsReserveTime;
  18. use common\models\order\OrderReserveComments;
  19. class StoreReserveSettingService extends BaseService
  20. {
  21. public function edit($params)
  22. {
  23. $params['store_id'] = $this->store_id;
  24. $params['mall_id'] = $this->mall_id;
  25. $model = StoreReserveSetting::setData($params);
  26. return ['id' => $model['id']];
  27. }
  28. public function detail()
  29. {
  30. $model = StoreReserveSetting::findOne(['store_id' => $this->store_id, 'status' => StatusEnum::ENABLED]);
  31. $setting = [];
  32. if ($model) {
  33. $setting = json_decode($model['setting'], true);
  34. }
  35. return $setting;
  36. }
  37. public function list($store_ids)
  38. {
  39. $list = StoreReserveSetting::lists(['where' => [
  40. ['store_id' => $store_ids, 'status' => StatusEnum::ENABLED]
  41. ]]);
  42. $setting = [];
  43. foreach ($list as $item) {
  44. $temp = json_decode($item['setting'], true);
  45. if (!empty($temp)) {
  46. $setting[$item['store_id']] = $temp;
  47. }
  48. }
  49. return $setting;
  50. }
  51. /**
  52. * 门店可预约时间列表
  53. * @param $params
  54. * @return array
  55. * @throws \Exception
  56. */
  57. public function storeReservationTimeList($params)
  58. {
  59. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $this->store_id]))->detail();
  60. $range_start = $store_reserve_setting['range_start'];
  61. $range_end = $store_reserve_setting['range_end'];
  62. $store = Store::findOne(['id' => $this->store_id]);
  63. $business_time_start = $store['business_time_start'];
  64. $business_time_end = $store['business_time_end'];
  65. $store_goods_reserve_sku = StoreGoodsReserveSku::findOne(['id' => $params['sku_id']]);
  66. $service_min = $this->getServiceMin($store_reserve_setting['appointment_interval'], $store_goods_reserve_sku['service_min'] ?? 0);
  67. if (empty($service_min)) {
  68. throw new \Exception('该商品没有设置服务时长');
  69. }
  70. $range_end_arr = ReserveServiceEnum::RANGE_END_ARR_MAP;
  71. $days = $range_end_arr[$range_end] ?? 7;
  72. $range_start_time = $this->getRangeStartTime($range_start);
  73. $time1 = date("Y-m-d {$business_time_start}:00");
  74. $time2 = date("Y-m-d {$business_time_end}:59");
  75. $buff = [];
  76. $start = strtotime($time1);
  77. $end = strtotime($time2);
  78. for ($i = $start; $i <= $end; $i = $i + 60 * $service_min) {
  79. $buff[] = ['time' => date("H:i", $i), 'is_reserve' => 1];
  80. }
  81. //切割时间
  82. $today = false;
  83. $today_day = date('Y-m-d', time());
  84. $today_time = strtotime($today_day . ' 00:00:00');
  85. if (!empty($params['start'])) {
  86. $time = strtotime($params['start'] . ' 00:00:00');
  87. if ($today_time == $time) {
  88. $today = true;
  89. }
  90. } else {
  91. $time = $today_time;
  92. $today = true;
  93. }
  94. $date_list = [];
  95. $end_time = $time + $days * 24 * 3600;
  96. for ($i = $time; $i < $end_time; $i += 24 * 60 * 60) {
  97. $is_reserve = 1;
  98. $time_list = $buff;
  99. $date = date('Y-m-d', $i);
  100. if ($today) {//今天
  101. foreach ($time_list as $key => $val) {
  102. $te_time = strtotime(date('Y-m-d ' . $val['time']));
  103. if ($date == $today_day && $te_time < (time() + $range_start_time)) {
  104. unset($time_list[$key]);
  105. }
  106. }
  107. $time_list = array_values($time_list);
  108. }
  109. $arr = ['date' => $date, 'week_day' => GoodsReserveTime::getWeekDay($date), 'time_list' => $time_list, 'is_reserve' => $is_reserve];
  110. $date_list[] = $arr;
  111. }
  112. return ['date_list' => $date_list, 'service_min' => $service_min];
  113. }
  114. /**
  115. * h5端-预约时间列表-到店分配/技师不选项
  116. * @param $params
  117. * @return array
  118. * @throws \Exception
  119. */
  120. public function toStoreReservationTimeList($params)
  121. {
  122. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $this->store_id]))->detail();
  123. $range_start = $store_reserve_setting['range_start'];
  124. $range_end = $store_reserve_setting['range_end'];
  125. $store = Store::findOne(['id' => $this->store_id]);
  126. $business_time_start = $store['business_time_start'];
  127. $business_time_end = $store['business_time_end'];
  128. $store_goods_reserve_sku = StoreGoodsReserveSku::getOne([
  129. ['id' => $params['sku_id']]
  130. ], true, ['reserve']);
  131. if (empty($store_goods_reserve_sku)) {
  132. throw new \Exception('商品不存在');
  133. }
  134. $service_min = $this->getServiceMin($store_reserve_setting['appointment_interval'] ?? 1, $store_goods_reserve_sku['service_min'] ?? 0);
  135. if (empty($service_min)) {
  136. throw new \Exception('该商品没有设置服务时长');
  137. }
  138. $date = $params['date'];
  139. $appointments_number = $store_goods_reserve_sku['reserve']['appointments_number'] ?? 0;
  140. if (!empty($appointments_number)) {
  141. $reservation_service_order_list = StoreOrderReserve::lists([
  142. 'where' => [
  143. ['mall_id' => $this->mall_id],
  144. ['store_id' => $this->store_id],
  145. ['goods_id' => $store_goods_reserve_sku['goods_id']],
  146. ['>=', 'service_start_at', strtotime($date . ' 00:00:00')],
  147. ['<=', 'service_end_at', strtotime($date . ' 23:59:59')],
  148. ['!=', 'order_status', -4],
  149. ['create_type' => 1],
  150. ],
  151. 'group' => 'service_start_at',
  152. 'select' => 'count(*) as counts,service_start_at',
  153. 'index' => 'service_start_at'
  154. ]);
  155. }
  156. $start_time = $this->getRangeStartTime($range_start);
  157. $time1 = "{$date} {$business_time_start}:00";
  158. $time2 = "{$date} {$business_time_end}:59";
  159. $buff = [];
  160. $time = time();
  161. $start = strtotime($time1);
  162. $end = strtotime($time2);
  163. //切割时间
  164. for ($i = $start; $i <= $end; $i = $i + 60 * $service_min) {
  165. $is_reserve = 1;
  166. $hour_minute = date("H:i", $i);
  167. $buff[] = ['time' => $hour_minute, 'is_reserve' => $is_reserve];
  168. }
  169. $range_end_arr = ReserveServiceEnum::RANGE_END_ARR_MAP;
  170. $days = $range_end_arr[$range_end] ?? 7;
  171. $date_list = [];
  172. $today_time = strtotime(date('Y-m-d', $time) . ' 00:00:00');
  173. if (!empty($params['date'])) {
  174. $time = strtotime($params['date'] . ' 00:00:00');
  175. } else {
  176. $time = $today_time;
  177. }
  178. $end_time = $time + $days * 24 * 3600;
  179. for ($i = $time; $i < $end_time; $i += 24 * 60 * 60) {
  180. $is_reserve = 1;
  181. $time_list = $buff;
  182. $date = date('Y-m-d', $i);
  183. $new_time_list = [];
  184. foreach ($time_list as &$item) {
  185. $temp_time_start = strtotime("{$date} " . $item['time'] . ":00");
  186. if (!empty($appointments_number)) {
  187. if (isset($reservation_service_order_list[$temp_time_start]) && $reservation_service_order_list[$temp_time_start]['counts'] >= $appointments_number) {
  188. $item['is_reserve'] = 0;
  189. }
  190. }
  191. $te_time = strtotime($date . ' ' . $item['time'] . ':00');
  192. if ($te_time < time()) {
  193. continue;
  194. }
  195. if ((time() + $start_time) > $te_time) {
  196. continue;
  197. }
  198. $new_time_list[] = $item;
  199. }
  200. $arr = ['date' => $date, 'week_day' => GoodsReserveTime::getWeekDay($date), 'time_list' => $new_time_list, 'is_reserve' => $is_reserve];
  201. $date_list[] = $arr;
  202. }
  203. return $date_list;
  204. }
  205. /**
  206. * 技师可预约时间列表
  207. * @param $params
  208. * @return array
  209. * @throws \Exception
  210. */
  211. public function techniciansReservationTimeList($params)
  212. {
  213. $reservation_service_user = ReservationServiceUser::findOne(['id' => $params['technicians_id']]);
  214. if (empty($reservation_service_user)) {
  215. throw new \Exception('技师不存在');
  216. }
  217. $list = $this->storeReservationTimeList($params);
  218. $date_list = $list['date_list'];
  219. $service_min = $list['service_min'];
  220. $date_time_arr = [];
  221. foreach ($date_list as $value) {
  222. $date_time_arr[] = strtotime($value['date']);
  223. }
  224. $scheduling_list = StoreReserveTechnicianScheduling::lists([
  225. 'where' => [
  226. ['user_id' => $reservation_service_user['user_id']],
  227. ['date_time' => $date_time_arr],
  228. ['status' => StatusEnum::ENABLED]
  229. ],
  230. 'with' => [
  231. 'schedulingTime' => function ($q) {
  232. $q->where(['status' => StatusEnum::ENABLED]);
  233. }
  234. ],
  235. 'index' => 'date_time'
  236. ]);
  237. $reservation_service_order_list = ReservationServiceOrder::lists([
  238. 'where' => [
  239. ['mall_id' => $reservation_service_user['mall_id']],
  240. ['store_id' => $params['store_id']],
  241. ['staff_user_id' => $reservation_service_user['user_id']],
  242. ['>=', 'service_start_at', strtotime($params['date'] . ' 00:00:00')],
  243. ['<=', 'service_end_at', strtotime($params['date'] . ' 23:59:59')],
  244. ['!=', 'server_status', ReservationServiceEnum::CANCELED]
  245. ]
  246. ]);
  247. $scheduled_time_arr = [];//已预约时间
  248. foreach ($list as $value) {
  249. $scheduled_time_arr[date("H:i", $value['service_start_at'])] = 1;
  250. }
  251. $occupy_list = ReservationServiceOccupy::lists([
  252. 'where' => [
  253. ['mall_id' => $reservation_service_user['mall_id']],
  254. ['store_id' => $params['store_id']],
  255. ['staff_user_id' => $reservation_service_user['user_id']],
  256. ['status' => StatusEnum::ENABLED]
  257. ]
  258. ]
  259. );//已占用时间
  260. foreach ($date_list as &$item) {
  261. $date_time = strtotime($item['date']);
  262. if (isset($scheduling_list[$date_time]) && empty($scheduling_list[$date_time]['is_all_day'])) {
  263. $scheduling = $scheduling_list[$date_time];
  264. foreach ($item['time_list'] as $k => &$v) {
  265. $v['is_reserve'] = 0;
  266. $temp_time = strtotime("{$item['date']} {$v['time']}:00") + $service_min * 60;
  267. foreach ($scheduling['schedulingTime'] as $scheduling_time) {
  268. if ($temp_time >= $scheduling_time['start_time'] && $temp_time <= $scheduling_time['end_time']) {
  269. $v['is_reserve'] = 1;
  270. }
  271. }
  272. }
  273. }
  274. foreach ($item['time_list'] as $k => &$v) {
  275. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  276. if (empty($v['is_reserve'])) {
  277. continue;
  278. }
  279. foreach ($reservation_service_order_list as $value) {
  280. if ($temp_time >= $value['service_start_at'] && $temp_time <= $value['service_end_at']) {
  281. $v['is_reserve'] = 0;
  282. break;
  283. }
  284. }
  285. }
  286. foreach ($item['time_list'] as $k => &$v) {
  287. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  288. if (empty($v['is_reserve'])) {
  289. continue;
  290. }
  291. foreach ($occupy_list as $occupy) {
  292. if ($temp_time >= $occupy['start_at'] && $temp_time <= $occupy['end_at']) {
  293. $v['is_reserve'] = 0;
  294. break;
  295. }
  296. }
  297. }
  298. }
  299. return $date_list;
  300. }
  301. public function serviceTimeList($params)
  302. {
  303. $reservation_service_user = ReservationServiceUser::findOne(['user_id' => $params['id'], 'status' => StatusEnum::ENABLED]);
  304. if (empty($reservation_service_user)) {
  305. throw new \Exception('技师不存在');
  306. }
  307. $params['start'] = date('Y-m-d', time());
  308. $list = $this->storeReservationTimeList($params);
  309. $date_list = $list['date_list'];
  310. $service_min = $list['service_min'];
  311. $date_time_arr = [];
  312. foreach ($date_list as $value) {
  313. $date_time_arr[] = strtotime($value['date']);
  314. }
  315. $scheduling_list = StoreReserveTechnicianScheduling::lists([
  316. 'where' => [
  317. ['user_id' => $reservation_service_user['user_id']],
  318. ['date_time' => $date_time_arr],
  319. ['status' => StatusEnum::ENABLED]
  320. ],
  321. 'with' => [
  322. 'schedulingTime' => function ($q) {
  323. $q->where(['status' => StatusEnum::ENABLED]);
  324. }
  325. ],
  326. 'index' => 'date_time'
  327. ]);
  328. $reservation_service_order_list = StoreOrderReserve::find()->where([
  329. 'tech_id' => $reservation_service_user['id'],
  330. 'store_id' => $params['store_id'],
  331. 'mall_id' => $reservation_service_user['mall_id'],
  332. ])->andWhere(['!=', 'order_status', -4])
  333. ->andWhere(['>=', 'service_start_at', time()])
  334. ->asArray()
  335. ->all();
  336. $scheduled_time_arr = [];//已预约时间
  337. foreach ($reservation_service_order_list as $value) {
  338. $scheduled_time_arr[date("H:i", $value['service_start_at'])] = 1;
  339. }
  340. $store_goods_reserve_sku = StoreGoodsReserveSku::getOne([
  341. ['id' => $params['sku_id']]
  342. ], true, ['reserve']);
  343. if (empty($store_goods_reserve_sku)) {
  344. throw new \Exception('商品不存在');
  345. }
  346. $appointments_number = $store_goods_reserve_sku['reserve']['appointments_number'] ?? 0;
  347. $occupy_list = ReservationServiceOccupy::lists([
  348. 'where' => [
  349. ['mall_id' => $reservation_service_user['mall_id']],
  350. ['store_id' => $params['store_id']],
  351. ['staff_user_id' => $reservation_service_user['user_id']],
  352. ['status' => StatusEnum::ENABLED]
  353. ]
  354. ]
  355. );//已占用时间
  356. if (!empty($appointments_number)) {
  357. $store_order_reserve_list = StoreOrderReserve::lists([
  358. 'where' => [
  359. ['mall_id' => $this->mall_id],
  360. ['store_id' => $this->store_id],
  361. ['goods_id' => $store_goods_reserve_sku['goods_id']],
  362. ['!=', 'order_status', -4],
  363. ['create_type' => 1],
  364. ],
  365. 'group' => 'service_start_at',
  366. 'select' => 'count(*) as counts,service_start_at',
  367. 'index' => 'service_start_at'
  368. ]);
  369. foreach ($store_order_reserve_list as &$temp) {
  370. $temp['service_start_at_date'] = date('Y-m-d H:i', $temp['service_start_at']);
  371. }
  372. }
  373. foreach ($date_list as $k => &$item) {
  374. $date_time = strtotime($item['date']);
  375. if (!isset($scheduling_list[$date_time])) {
  376. $item['is_reserve'] = 0;
  377. foreach ($item['time_list'] as &$v) {
  378. $v['is_reserve'] = 0;
  379. }
  380. continue;
  381. }
  382. if (isset($scheduling_list[$date_time]) && empty($scheduling_list[$date_time]['is_all_day'])) {
  383. $scheduling = $scheduling_list[$date_time];
  384. foreach ($item['time_list'] as $k => &$v) {
  385. $v['is_reserve'] = 0;
  386. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  387. foreach ($scheduling['schedulingTime'] as $scheduling_time) {
  388. if ($temp_time >= $scheduling_time['start_time'] && $temp_time <= $scheduling_time['end_time']) {
  389. $v['is_reserve'] = 1;
  390. }
  391. }
  392. }
  393. }
  394. foreach ($item['time_list'] as $k => &$v) {
  395. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  396. if (!empty($appointments_number) && isset($store_order_reserve_list) && $store_order_reserve_list[$temp_time]['counts'] >= $appointments_number) {
  397. $v['is_reserve'] = 0;
  398. }
  399. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  400. if (empty($v['is_reserve'])) {
  401. continue;
  402. }
  403. foreach ($reservation_service_order_list as $value) {
  404. if ($temp_time >= $value['service_start_at'] && $temp_time < $value['service_end_at']) {
  405. $v['is_reserve'] = 0;
  406. break;
  407. }
  408. }
  409. }
  410. foreach ($item['time_list'] as $k => &$v) {
  411. $temp_time = strtotime("{$item['date']} {$v['time']}:00");
  412. if (empty($v['is_reserve'])) {
  413. continue;
  414. }
  415. foreach ($occupy_list as $occupy) {
  416. $temp_time_end = $temp_time + $service_min * 60;
  417. if ($temp_time >= $occupy['start_at'] && $temp_time <= $occupy['end_at'] && $temp_time_end <= $occupy['end_at']) {
  418. $v['is_reserve'] = 0;
  419. break;
  420. }
  421. }
  422. }
  423. $date_is_reserve = 0;
  424. foreach ($item['time_list'] as $val) {
  425. if ($val['is_reserve']) {
  426. $date_is_reserve = 1;
  427. break;
  428. }
  429. }
  430. $item['is_reserve'] = $date_is_reserve;
  431. }
  432. return array_values($date_list);
  433. }
  434. public function search($params)
  435. {
  436. $date = $params['date'] . ' ' . $params['time'] . ':00';
  437. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $this->store_id]))->detail();
  438. $employee_selection = $store_reserve_setting['employee_selection'] ?? 1;
  439. if ($employee_selection == 3) {
  440. $list_page['list'] = [];
  441. $list_page['page'] = 0;
  442. $list_page['page_size'] = 10;
  443. $list_page['count'] = 0;
  444. $list_page['page_count'] = 0;
  445. $list_page['error_msg'] = '技师不可选';
  446. return $list_page;
  447. }
  448. $service_min = 0;
  449. $store_goods_reserve_sku = StoreGoodsReserveSku::findOne(['id' => $params['sku_id']]);
  450. switch ($store_reserve_setting['appointment_interval']) {
  451. case 1:
  452. $service_min = $store_goods_reserve_sku['service_min'] ?? 0;
  453. break;
  454. }
  455. $appointments_number = $store_goods_reserve_sku['reserve']['appointments_number'] ?? 0;
  456. $start_time = strtotime($date);
  457. $end_time = $start_time + $service_min * 60;
  458. if (!empty($appointments_number)) {
  459. $store_order_reserve_list = StoreOrderReserve::lists([
  460. 'where' => [
  461. ['mall_id' => $this->mall_id],
  462. ['store_id' => $this->store_id],
  463. ['goods_id' => $store_goods_reserve_sku['goods_id']],
  464. ['>=', 'service_start_at', $start_time],
  465. ['<=', 'service_end_at', $end_time],
  466. ['!=', 'order_status', -4],
  467. ['create_type' => 1],
  468. ],
  469. 'group' => 'service_start_at',
  470. 'select' => 'count(*) as counts,service_start_at',
  471. 'index' => 'service_start_at'
  472. ]);
  473. $is_false = 0;
  474. if (isset($store_order_reserve_list) && $store_order_reserve_list[$start_time]['counts'] >= $appointments_number) {
  475. $is_false = 1;
  476. }
  477. if ($is_false) {
  478. $list_page['list'] = [];
  479. $list_page['page'] = 0;
  480. $list_page['page_size'] = 10;
  481. $list_page['count'] = 0;
  482. $list_page['page_count'] = 0;
  483. $list_page['is_full'] = 1;
  484. return $list_page;
  485. }
  486. }
  487. $day_start_time = strtotime($params['date'] . ' 00:00:00');
  488. $user_ids = ReservationServiceStoreGoods::find()->where(['store_id' => $params['store_id'], 'goods_id' => $params['goods_id'], 'status' => StatusEnum::ENABLED])
  489. ->select('user_id')->column();
  490. $new_user_ids = [];
  491. $user_ids_all = StoreReserveTechnicianScheduling::find()->where(['user_id' => $user_ids, 'is_all_day' => 1, 'date_time' => $day_start_time, 'status' => StatusEnum::ENABLED])
  492. ->select('user_id')->column();
  493. $new_user_ids = array_merge($new_user_ids, $user_ids_all);
  494. $scheduling_list = StoreReserveTechnicianScheduling::find()->where(['user_id' => $user_ids, 'is_all_day' => 0, 'date_time' => $day_start_time, 'status' => StatusEnum::ENABLED])
  495. ->with(['schedulingTime'])->all();
  496. foreach ($scheduling_list as $val) {
  497. foreach ($val['schedulingTime'] as $v) {
  498. if (($v['start_time'] <= $start_time && $start_time <= $v['end_time']) && ($v['start_time'] <= $end_time)) {
  499. $new_user_ids[] = $val['user_id'];
  500. break;
  501. }
  502. }
  503. }
  504. $store_order_reserve_list = StoreOrderReserve::find()->where([
  505. 'store_id' => $params['store_id'],
  506. ])->andWhere(['!=', 'order_status', -4])
  507. ->andWhere(['>=', 'created_at', time() - 30 * 24 * 3600])
  508. ->all();
  509. $date_time = strtotime($date);
  510. $tech_id_ids = [];
  511. foreach ($store_order_reserve_list as $value) {
  512. if ($value['service_start_at'] <= $date_time && $value['service_end_at'] > $date_time) {
  513. $tech_id_ids[] = $value['tech_id'];
  514. }
  515. }
  516. $reserve_user_ids = ReservationServiceUser::find()->where(['id' => $tech_id_ids])->select('user_id')->column();
  517. $occupy_list = ReservationServiceOccupy::lists([
  518. 'where' => [
  519. ['mall_id' => $this->mall_id],
  520. ['store_id' => $params['store_id']],
  521. ['status' => StatusEnum::ENABLED],
  522. ]
  523. ]
  524. );//已占用时间
  525. $staff_user_ids = [];
  526. foreach ($occupy_list as $val) {
  527. if ($start_time >= $val['start_at'] && $start_time <= $val['end_at'] && $end_time <= $val['end_at']) {
  528. $staff_user_ids[] = $val['staff_user_id'];
  529. }
  530. }
  531. $user_ids = array_diff($new_user_ids, $reserve_user_ids, $staff_user_ids);
  532. $where = [
  533. ['store_id' => $params['store_id']],
  534. ['user_id' => $user_ids],
  535. ['status' => StatusEnum::ENABLED],
  536. ['store_status' => StatusEnum::ENABLED]
  537. ];
  538. if (!empty($params['name'])) {
  539. $where[] = ['like', 'name', trim($params['name'])];
  540. }
  541. if (!empty($params['min_age'])) {
  542. $where[] = ['>=', 'age', $params['min_age']];
  543. }
  544. if (!empty($params['max_age'])) {
  545. $where[] = ['<=', 'age', $params['max_age']];
  546. }
  547. if (!empty($params['gender'])) {
  548. $where[] = ['gender' => $params['gender']];
  549. }
  550. $orderBy = 'id desc';
  551. if (!empty($user_ids)) {
  552. $user_ids = join(',', $user_ids);
  553. $orderBy = ["field(user_id,{$user_ids})" => []]; //1-距离
  554. }
  555. if (isset($params['sort']) && $params['sort'] > 1) {
  556. //1-距离,2-订单量 order_num ,3-好评 service_rating
  557. if ($params['sort'] == 1) {
  558. $orderBy = 'distance asc'; //1-距离
  559. } else {
  560. $orderBy = $params['sort'] == 2 ? 'order_num desc' : 'service_rating desc';
  561. }
  562. }
  563. $params['order'] = $orderBy;
  564. $params['where'] = $where;
  565. $list = ReservationServiceUser::listPage($params);
  566. $default_jishi = [];
  567. if (!empty($list['list'])) {
  568. $store_ids = array_column($list['list'], 'store_id');
  569. $store_ids = array_unique($store_ids);
  570. $store_list = Store::lists([
  571. 'where' => [
  572. ['id' => $store_ids]
  573. ],
  574. 'select' => 'id,store_name',
  575. 'index' => 'id'
  576. ]);
  577. $comments = OrderReserveComments::lists([
  578. 'where' => [
  579. ['tech_user_id' => array_column($list['list'], 'user_id')],
  580. ['reserve_type' => StatusEnum::ENABLED],
  581. ['store_id' => $store_ids]
  582. ],
  583. 'select' => 'COUNT(*) as total, tech_user_id',
  584. 'group' => 'tech_user_id',
  585. 'index' => 'tech_user_id'
  586. ]);
  587. $default_jishi = $list['list'][0];
  588. foreach ($list['list'] as &$item) {
  589. $item['store_name'] = $store_list[$item['store_id']]['store_name'] ?? '';
  590. $item['commentNum'] = $comments[$item['user_id']]['total'] ?? 0;
  591. $item['comment_num'] = $item['commentNum'];
  592. $item['serviceNum'] = $item['order_num'];
  593. $item['detail'] = $item['desc'];
  594. }
  595. }
  596. if (!empty($params['is_default_jishi']) && !empty($default_jishi)) {
  597. $params['technicians_id'] = $default_jishi['id'];
  598. $date_list = $this->techniciansReservationTimeList($params);
  599. $lately_time_arr = [];
  600. if (!empty($date_list)) {
  601. $time = '';
  602. foreach ($date_list[0]['time_list'] as $k => $v) {
  603. if ($v['is_reserve']) {
  604. $time = $v['time'];
  605. break;
  606. }
  607. }
  608. $lately_time_arr = [
  609. 'date' => $date_list[0]['date'],
  610. 'time' => $time,
  611. 'week_day' => $date_list[0]['week_day'],
  612. ];
  613. }
  614. $default_jishi['lately_time_arr'] = $lately_time_arr;
  615. $list['default_jishi'] = $default_jishi;
  616. }
  617. return $list;
  618. }
  619. //门店-预约商品详情-前四个技师
  620. public function topFourth($params)
  621. {
  622. $store_goods_reserve_sku = StoreGoodsReserveSku::findOne(['id' => $params['sku_id']]);
  623. if (empty($store_goods_reserve_sku)) {
  624. throw new \Exception('预约商品不存在');
  625. }
  626. $this->store_id = $store_goods_reserve_sku['store_id'];
  627. $params['store_id'] = $store_goods_reserve_sku['store_id'];
  628. $params['goods_id'] = $store_goods_reserve_sku['goods_id'];
  629. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $params['store_id']]))->detail();
  630. $service_min = $this->getServiceMin($store_reserve_setting['appointment_interval'], $store_goods_reserve_sku['service_min'] ?? 0);
  631. $range_start_time = $this->getRangeStartTime($store_reserve_setting['range_start']);
  632. if (empty($params['id'])) {
  633. $user_ids = ReservationServiceStoreGoods::find()->where(['store_id' => $params['store_id'], 'goods_id' => $params['goods_id'], 'status' => StatusEnum::ENABLED])
  634. ->select('user_id')->column();
  635. } else {
  636. $user_ids = $params['id'];
  637. }
  638. $time = time();
  639. $service_user_list = ReservationServiceUser::lists(['where' => [
  640. ['user_id' => $user_ids],
  641. ['store_id' => $this->store_id],
  642. ['status' => StatusEnum::ENABLED]
  643. ]]);
  644. $user_ids = array_column($service_user_list, 'user_id');
  645. //排班列表
  646. $scheduling_list = StoreReserveTechnicianScheduling::lists([
  647. 'where' => [
  648. ['user_id' => $user_ids],
  649. ['status' => StatusEnum::ENABLED],
  650. ['>=', 'date_time', strtotime(date('Y-m-d', $time) . ' 00:00:00')]
  651. ],
  652. 'order' => 'date_time asc,is_all_day desc',
  653. 'with' => ['schedulingTime']
  654. ]);
  655. $store = Store::findOne(['id' => $params['store_id']]);
  656. $top_fourth_list = [];
  657. $params['user_ids'] = $user_ids;
  658. $params['time'] = $time;
  659. $params['service_min'] = $service_min;
  660. foreach ($scheduling_list as $scheduling) {
  661. $params['staff_user_id'] = $scheduling['user_id'];
  662. if ($scheduling['is_all_day'] == 1) { //全天上班
  663. $date = date('Y-m-d', $scheduling['date_time']);
  664. $start_time = strtotime($date . ' ' . $store['business_time_start'] . ':00');
  665. $end_time = strtotime($date . ' ' . $store['business_time_end'] . ':59');
  666. if ($end_time < $time) {
  667. continue;
  668. }
  669. $params['start_time'] = $start_time - $range_start_time;
  670. $params['end_time'] = $end_time;
  671. if (isset($top_fourth_list[$scheduling['user_id']])) {
  672. continue;
  673. }
  674. $top_three_item = $this->getTopThreeItem($params);
  675. if (empty($top_three_item)) {
  676. continue;
  677. }
  678. $top_fourth_list[$top_three_item['user_id']] = $top_three_item;
  679. } else {
  680. foreach ($scheduling['schedulingTime'] as $scheduling_time) {
  681. $start_time = $scheduling_time['start_time'];
  682. $end_time = $scheduling_time['end_time'];
  683. if ($end_time < $time) {
  684. continue;
  685. }
  686. $params['start_time'] = $start_time + $range_start_time;
  687. $params['end_time'] = $end_time;
  688. if (isset($top_fourth_list[$scheduling['user_id']])) {
  689. continue;
  690. }
  691. $top_three_item = $this->getTopThreeItem($params);
  692. if (empty($top_three_item)) {
  693. continue;
  694. }
  695. $top_fourth_list[$top_three_item['user_id']] = $top_three_item;
  696. }
  697. }
  698. }
  699. $new_top_fourth_list = [];
  700. $top_fourth_list = ArrayHelper::arraySort($top_fourth_list, 'time_stamp', 'asc');
  701. foreach ($top_fourth_list as $value) {
  702. if (count($new_top_fourth_list) >= 4) {
  703. break;
  704. }
  705. $new_top_fourth_list[] = $value;
  706. }
  707. $user_ids = array_column($new_top_fourth_list, 'user_id');
  708. $reservation_service_user_list = ReservationServiceUser::lists([
  709. 'where' => [
  710. ['user_id' => $user_ids],
  711. ['status' => StatusEnum::ENABLED],
  712. ['store_status' => StatusEnum::ENABLED]
  713. ],
  714. 'index' => 'user_id',
  715. 'select' => 'id,user_id,name,avatar'
  716. ]);
  717. foreach ($new_top_fourth_list as $k => &$top_three) {
  718. $top_three['name'] = $top_three['avatar'] = $top_three['tech_id'] = '';
  719. if ($reservation_service_user_list[$top_three['user_id']]) {
  720. $top_three['tech_id'] = $reservation_service_user_list[$top_three['user_id']]['id'];
  721. $top_three['name'] = $reservation_service_user_list[$top_three['user_id']]['name'];
  722. $top_three['avatar'] = $reservation_service_user_list[$top_three['user_id']]['avatar'];
  723. } else {
  724. unset($new_top_fourth_list[$k]);
  725. }
  726. }
  727. $employee_selection = $store_reserve_setting['employee_selection'] ?? 1;
  728. if (in_array($employee_selection, [1, 3]) && count($new_top_fourth_list) == 4) {
  729. array_pop($new_top_fourth_list);
  730. }
  731. $store_data = [
  732. 'store_id' => $store['id'],
  733. 'store_name' => $store['store_name'],
  734. 'logo_img' => $store['logo_img'],
  735. ];
  736. return ['list' => array_values($new_top_fourth_list), 'store_data' => $store_data];
  737. }
  738. public function getRangeStartTime($range_start)
  739. {
  740. $range_start_time = 0;
  741. switch ($range_start) {
  742. case 2:
  743. $range_start_time = 30 * 60;
  744. break;
  745. case 3:
  746. $range_start_time = 60 * 60;
  747. break;
  748. }
  749. return $range_start_time;
  750. }
  751. public function getServiceMin($appointment_interval, $min)
  752. {
  753. switch ($appointment_interval) {
  754. case 1:
  755. $service_min = $min;
  756. break;
  757. default:
  758. $service_min = $min ?? 30;
  759. }
  760. return $service_min;
  761. }
  762. protected function getTopThreeItem($params)
  763. {
  764. $start_time = $params['start_time'];
  765. $end_time = $params['end_time'];
  766. $service_min = $params['service_min'];
  767. $time = $params['time'];
  768. $staff_user_id = $params['staff_user_id'];
  769. $user_ids = $params['user_ids'];
  770. //技师已被占用的时间段
  771. $reservation_service_order_list = ReservationServiceOrder::lists([
  772. 'where' => [
  773. ['store_id' => $params['store_id']],
  774. ['staff_user_id' => $user_ids],
  775. ['!=', 'server_status', ReservationServiceEnum::CANCELED],
  776. ],
  777. 'with' => ['goodsReserve']
  778. ]);
  779. $reservation_service_order_arr = [];
  780. foreach ($reservation_service_order_list as $item) {
  781. $reservation_service_order_arr[$item['staff_user_id']][] = $item;
  782. }
  783. for ($i = $start_time; $i <= $end_time; $i = $i + 60 * $service_min) {
  784. if ($i < $time) {
  785. continue;
  786. }
  787. $is_occupy = 0;//是否被占用
  788. if (isset($reservation_service_order_arr[$staff_user_id])) {
  789. foreach ($reservation_service_order_arr[$staff_user_id] as $reservation_service_order) {
  790. $temp_service_min = $reservation_service_order['goodsReserve']['service_min'] * 60;
  791. $service_start_at = $reservation_service_order['service_start_at'];
  792. $service_end_at = $reservation_service_order['service_start_at'] + $temp_service_min;
  793. if ($service_start_at == $i || ($i > $service_start_at && $i < $service_end_at)) {
  794. //可预约时间已被占用
  795. $is_occupy = 1;
  796. break;
  797. }
  798. }
  799. }
  800. if ($is_occupy) {
  801. continue;
  802. }
  803. return ['user_id' => $staff_user_id, 'time' => date("H:i", $i), 'time_stamp' => $i];
  804. }
  805. return [];
  806. }
  807. /**
  808. * 服务人员页面-门店-可预约日期
  809. * @return array[]
  810. * @throws \Exception
  811. */
  812. public function getDate($params)
  813. {
  814. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $this->store_id]))->detail();
  815. $range_end = $store_reserve_setting['range_end'];
  816. $range_end_arr = ReservationServiceEnum::RANGE_END_ARR_MAP;
  817. $days = $range_end_arr[$range_end] ?? 7;
  818. $date_list = [];
  819. $time = time();
  820. $i = 0;
  821. while (true) {
  822. if ($i >= $days) {
  823. break;
  824. }
  825. $temp_time = $time + $i * 86400;
  826. $date = date('Y-m-d', $temp_time);
  827. $date_list[] = ['date' => $date, 'is_reserve' => 1, 'week_day' => GoodsReserveTime::getWeekDay($date)];
  828. $i++;
  829. }
  830. if (!empty($params['start'])) {
  831. $start_time = strtotime($params['start'] . ' 00:00:00');
  832. $i = 0;
  833. $temp_date_list = array_column($date_list, null, 'date');
  834. $date_list = [];
  835. while (true) {
  836. if ($i >= $days) {
  837. break;
  838. }
  839. $temp_time = $start_time + $i * 86400;
  840. $date = date('Y-m-d', $temp_time);
  841. $is_reserve = 0;
  842. if (isset($temp_date_list[$date])) {
  843. $is_reserve = 1;
  844. }
  845. $date_list[] = ['date' => $date, 'is_reserve' => $is_reserve, 'week_day' => GoodsReserveTime::getWeekDay($date)];
  846. $i++;
  847. }
  848. }
  849. return ['date_list' => $date_list];
  850. }
  851. /**
  852. * 服务人员页面-门店-获取可预约的时间段
  853. * @param $params
  854. * @return array
  855. * @throws \Exception
  856. */
  857. public function getTimes($params)
  858. {
  859. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $this->store_id]))->detail();
  860. $range_start = $store_reserve_setting['range_start'];
  861. $store = Store::findOne(['id' => $this->store_id]);
  862. $business_time_start = $store['business_time_start'];
  863. $business_time_end = $store['business_time_end'];
  864. $store_goods_reserve_sku = StoreGoodsReserveSku::findOne(['id' => $params['sku_id']]);
  865. $service_min = $this->getServiceMin($store_reserve_setting['appointment_interval'], $store_goods_reserve_sku['service_min'] ?? 30);
  866. if (empty($service_min)) {
  867. throw new \Exception('该商品没有设置服务时长');
  868. }
  869. $start_time = $this->getRangeStartTime($range_start);
  870. $time1 = $params['date'] . ' ' . $business_time_start . ':00';
  871. $time2 = $params['date'] . ' ' . $business_time_end . ':59';
  872. $buff = [];
  873. $start = strtotime($time1);
  874. $end = strtotime($time2);
  875. for ($i = $start; $i <= $end; $i = $i + 60 * $service_min) {
  876. $buff[] = ['time' => date("H:i", $i), 'is_reserve' => 1];
  877. }
  878. //切割时间
  879. $time = time();
  880. $time_list = $buff;
  881. foreach ($time_list as $key => &$val) {
  882. if (($time + $start_time) > strtotime($params['date'] . ' ' . $val['time'] . ':00')) {
  883. unset($time_list[$key]);
  884. }
  885. $temp_time = strtotime($params['date'] . ' ' . $val['time'] . ':00') + $service_min * 60;
  886. if ($temp_time > $end) {
  887. $val['is_reserve'] = 0;
  888. }
  889. }
  890. $time_list = array_values($time_list);
  891. return ['time_list' => $time_list];
  892. }
  893. }