ReserveBoardService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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\ReservationServiceUser;
  7. use addons\SecondaryCard\common\models\SecondaryCard;
  8. use addons\Store\common\enums\ReserveServiceEnum;
  9. use addons\Store\common\models\StoreGoods;
  10. use addons\Store\common\models\StoreGoodsReserveSku;
  11. use addons\Store\common\models\StoreReserveTechnicianScheduling;
  12. use common\enums\GoodsTypeEnum;
  13. use common\enums\StatusEnum;
  14. use common\models\mall\MallAddons;
  15. class ReserveBoardService extends BaseService
  16. {
  17. public function index($params)
  18. {
  19. if (empty($params['date'])) {
  20. $date = date('Y-m-d');
  21. } else {
  22. $date = date('Y-m-d', strtotime($params['date']));
  23. }
  24. $technician_display = empty($params['technician_display']) ? 0 : 1;
  25. $date_time_start = strtotime($date . ' 00:00:00');
  26. $date_time_end = strtotime($date . ' 23:59:59');
  27. $unassigned = $this->getUnassigned($date_time_start, $date_time_end);
  28. $tech_user_ids = [];
  29. $staffs = $this->getStaffs($technician_display, $date, $tech_user_ids);
  30. $staffs_user_list = $this->getStaffsUser($tech_user_ids);
  31. $appointments = $this->getAppointments($staffs, $date_time_start, $date_time_end);
  32. $store = $params['store'];
  33. $business_time_start = strtotime($date . ' ' . $store['business_time_start'] . ':00');
  34. $business_time_end = strtotime($date . ' ' . $store['business_time_end'] . ':00');
  35. $H = date('H', $business_time_start);
  36. $start_time = $H * 60;
  37. $H = date('H', $business_time_end);
  38. $minute = date('i', $business_time_end);
  39. $end_time = $H * 60 + $minute;
  40. return ['unassigned' => $unassigned, 'staffs' => $staffs, 'staffs_user_list' => $staffs_user_list, 'appointments' => $appointments,
  41. 'start_time' => $start_time,
  42. 'end_time' => $end_time,
  43. 'store_name' => $store['store_name'],
  44. ReservationServiceEnum::SERVER_STATUS_MAP
  45. ];
  46. }
  47. protected function getUnassigned($date_time_start, $date_time_end)
  48. {
  49. $unassigned = [];//未分配
  50. $unassigned_list = ReservationServiceOrder::lists([
  51. 'where' => [
  52. ['mall_id' => $this->mall_id],
  53. ['store_id' => $this->store_id],
  54. ['staff_user_id' => 0],
  55. ['!=', 'service_status', ReserveServiceEnum::RESERVE_ORDER_STATUS_CLOSED],
  56. ['>=', 'service_start_at', $date_time_start],
  57. ['<=', 'service_start_at', $date_time_end],
  58. [
  59. 'or',
  60. ['>', 'goods_id', 0],
  61. [
  62. 'and',
  63. ['>', 'secondary_card_id', 0],
  64. ['reservation_model' => 2]
  65. ],
  66. ]
  67. ],
  68. 'with' => ['user', 'orderDetail'],
  69. ]);
  70. $time = time();
  71. $goods_ids = array_column($unassigned_list, 'goods_id');
  72. $goods_list = StoreGoods::lists(['where' => [['id' => $goods_ids]], 'select' => 'id,goods_name', 'index' => 'id']);
  73. $service_type_map = ReserveServiceEnum::SERVICE_TYPE_MAP;
  74. foreach ($unassigned_list as $item) {
  75. $row['id'] = $item['id'];
  76. $row['name'] = $item['user']['nickname'];
  77. $row['phone'] = $item['user']['mobile'];
  78. $row['schedule'] = ['startTime' => date('H:i', $item['service_start_at']), 'endTime' => date('H:i', $item['service_end_at'])];
  79. $row['late'] = 0;
  80. $row['project'] = $goods_list[$item['goods_id']]['goods_name'] ?? '';
  81. if ($time > $item['service_start_at']) {
  82. $row['late'] = intval(($time - $item['service_start_at']) / 60);
  83. }
  84. $row['service_type_text'] = $service_type_map[$item['service_type']] ?? '';
  85. $row['server_status'] = $item['server_status'];
  86. $row['create_type'] = $item['create_type'];
  87. $row['order_no'] = $item['order_no'];
  88. $unassigned[] = $row;
  89. }
  90. return $unassigned;
  91. }
  92. protected function getStaffs($technician_display, $date, &$tech_user_ids)
  93. {
  94. $date_time = strtotime($date);
  95. $where = [
  96. ['mall_id' => $this->mall_id],
  97. ['store_id' => $this->store_id],
  98. ['status' => StatusEnum::ENABLED],
  99. ];
  100. if (!empty($technician_display)) {
  101. $scheduling_list = StoreReserveTechnicianScheduling::lists([
  102. 'where' => [
  103. ['mall_id' => $this->mall_id],
  104. ['date_time' => $date_time],
  105. ['status' => StatusEnum::ENABLED]
  106. ],
  107. ]);
  108. $user_ids = array_column($scheduling_list, 'user_id');
  109. $where[] = ['user_id' => $user_ids];
  110. }
  111. $list = ReservationServiceUser::lists([
  112. 'where' => $where,
  113. 'select' => 'id,name,user_id',
  114. 'order' => 'id desc'
  115. ]);
  116. $rows = [];
  117. foreach ($list as $item) {
  118. $tech_user_ids[] = $item['user_id'];
  119. $rows[] = $item['user_id'] . ':' . $item['name'];
  120. }
  121. return $rows;
  122. }
  123. protected function getStaffsUser($tech_user_ids)
  124. {
  125. $list = ReservationServiceUser::lists([
  126. 'where' => [
  127. ['mall_id' => $this->mall_id],
  128. ['store_id' => $this->store_id],
  129. ['user_id' => $tech_user_ids]
  130. ],
  131. 'select' => 'user_id,name',
  132. 'order' => 'user_id desc'
  133. ]);
  134. return $list;
  135. }
  136. protected function getAppointments($staffs, $date_time_start, $date_time_end)
  137. {
  138. $appointments_list = ReservationServiceOrder::lists([
  139. 'where' => [
  140. ['mall_id' => $this->mall_id],
  141. ['store_id' => $this->store_id],
  142. ['!=', 'staff_user_id', 0],
  143. ['!=', 'server_status', ReservationServiceEnum::CANCELED],
  144. ['>=', 'service_start_at', $date_time_start],
  145. ['<=', 'service_start_at', $date_time_end],
  146. ],
  147. 'with' => ['user', 'staff' => function ($q) {
  148. $q->where(['status' => StatusEnum::ENABLED]);
  149. }, 'orderDetail'],
  150. 'order' => 'service_start_at asc',
  151. ]);
  152. $occupy_list = ReservationServiceOccupy::lists([
  153. 'where' => [
  154. ['mall_id' => $this->mall_id],
  155. ['store_id' => $this->store_id],
  156. ['status' => StatusEnum::ENABLED],
  157. ['!=', 'staff_user_id', 0],
  158. ['>=', 'start_at', $date_time_start],
  159. ['<=', 'start_at', $date_time_end],
  160. ],
  161. 'with' => ['staff' => function ($q) {
  162. $q->where(['status' => StatusEnum::ENABLED]);
  163. }],
  164. 'order' => 'start_at asc',
  165. ]);
  166. $appointments = [];
  167. $rows = [];
  168. $goods_reserve_sku_id = array_column($appointments_list, 'goods_reserve_sku_id');
  169. $goods_sku_list = StoreGoodsReserveSku::lists(['where' => [['id' => $goods_reserve_sku_id]], 'with' => ['goods' => function ($q) {
  170. $q->select('id,goods_name');
  171. }], 'select' => 'id,name,goods_id', 'index' => 'id']);
  172. if (MallAddons::isInstall($this->mall_id, 'SecondaryCard')) {
  173. $secondary_card_id = array_column($appointments_list, 'secondary_card_id');
  174. $secondary_card_list = SecondaryCard::lists(['where' => [['id' => $secondary_card_id]], 'select' => 'id,name', 'index' => 'id']);
  175. }
  176. $service_type_map = ReserveServiceEnum::SERVICE_TYPE_MAP;
  177. foreach ($appointments_list as $item) {
  178. $reserve_time = date('H:i', $item['service_start_at']);
  179. $key = $item['staff']['user_id'] . ':' . $item['staff']['name'];
  180. $occupy_time = ($item['service_end_at'] - $item['service_start_at']) / 60;
  181. $project = '';
  182. if (isset($goods_sku_list[$item['goods_reserve_sku_id']])) {
  183. $project = $goods_sku_list[$item['goods_reserve_sku_id']]['name'];
  184. if (empty($project)) {
  185. $project = $goods_sku_list[$item['goods_reserve_sku_id']]['goods']['goods_name'];
  186. }
  187. }
  188. if (isset($secondary_card_list[$item['secondary_card_id']])) {
  189. $project = $secondary_card_list[$item['secondary_card_id']]['name'];
  190. }
  191. $rows[$reserve_time][$key][] = [
  192. 'name' => $item['user']['nickname'],
  193. 'phone' => $item['user']['mobile'],
  194. 'staff' => $item['staff']['name'],
  195. 'schedule' => ['startTime' => date('H:i', $item['service_start_at']), 'endTime' => date('H:i', $item['service_end_at'])],
  196. 'project' => $project,
  197. 'service_type_text' => $service_type_map[$item['service_type']] ?? '',
  198. 'duration' => $occupy_time . '分钟',
  199. 'id' => $item['id'],
  200. 'server_status' => $item['server_status'],
  201. 'create_type' => $item['create_type'],
  202. 'order_no' => $item['order_no'],
  203. ];
  204. }
  205. foreach ($occupy_list as $val) {
  206. $reserve_time = date('H:i', $val['start_at']);
  207. $key = $val['staff']['user_id'] . ':' . $val['staff']['name'];
  208. $occupy_time = ($val['end_at'] - $val['start_at']) / 60;
  209. $rows[$reserve_time][$key][] = [
  210. 'name' => '',
  211. 'phone' => '',
  212. 'staff' => $val['staff']['name'],
  213. 'schedule' => ['startTime' => date('H:i', $val['start_at']), 'endTime' => date('H:i', $val['end_at'])],
  214. 'project' => '',
  215. 'service_type_text' => '',
  216. 'duration' => $occupy_time . '分钟',
  217. 'id' => $val['id'],
  218. 'server_status' => ReservationServiceEnum::OCCUPY,
  219. 'create_type' => 3,
  220. ];
  221. }
  222. foreach ($rows as $k => $v) {
  223. $temp = $v;
  224. $temp['time'] = $k;
  225. $appointments[] = $temp;
  226. }
  227. return $appointments;
  228. }
  229. public function getTimeRange($params)
  230. {
  231. $date = $params['date'] ?? date('Y-m-d');
  232. $store = $params['store'];
  233. $business_time_start = strtotime($date . ' ' . $store['business_time_start'] . ':00');
  234. $business_time_end = strtotime($date . ' ' . $store['business_time_end'] . ':00');
  235. $time = time();
  236. if ($time > $business_time_start) {
  237. $start = $time;
  238. } else {
  239. $start = $business_time_start;
  240. }
  241. $rows = [];
  242. $minute = date('i', $start);
  243. $H = date('H', $start);
  244. $minute_temp = '00';
  245. switch (true) {
  246. case $minute >= 0 && $minute < 10:
  247. $minute_temp = '10';
  248. break;
  249. case $minute > 10 && $minute < 19:
  250. $minute_temp = '20';
  251. break;
  252. case $minute > 20 && $minute < 29:
  253. $minute_temp = '30';
  254. break;
  255. case $minute > 30 && $minute < 39:
  256. $minute_temp = '40';
  257. break;
  258. case $minute > 40 && $minute < 49:
  259. $minute_temp = '50';
  260. break;
  261. case $minute > 50 && $minute < 59:
  262. $H++;
  263. $minute_temp = '00';
  264. break;
  265. }
  266. $date = date('Y-m-d', $start);
  267. $start = strtotime($date . ' ' . $H . ':' . $minute_temp . ':00');
  268. for ($i = $start; $i <= $business_time_end; $i += 10 * 60) {
  269. $rows[] = ['time' => date('H:i', $i)];
  270. }
  271. return $rows;
  272. }
  273. /**
  274. * 设置占用
  275. * @param $params
  276. * @return array
  277. * @throws \Exception
  278. */
  279. public function setOccupyData($params)
  280. {
  281. $date = date('Y-m-d', strtotime($params['date']));
  282. $start_time = strtotime($date . ' ' . $params['form']['start_at'] . ':00');
  283. $staff_user_id = $params['form']['staff_user_id'];
  284. $occupy_hour = !empty($params['form']['occupy_hour']) ? $params['form']['occupy_hour'] : 0;
  285. $occupy_minute = !empty($params['form']['occupy_minute']) ? $params['form']['occupy_minute'] : 0;
  286. $end_time = $start_time + $occupy_hour * 3600 + $occupy_minute * 60;
  287. $res = ReservationServiceOrder::find()->where([
  288. 'mall_id' => $this->mall_id, 'store_id' => $this->store_id, 'staff_user_id' => $staff_user_id
  289. ])->andWhere(['!=', 'server_status', ReservationServiceEnum::CANCELED])
  290. ->andWhere([
  291. 'or',
  292. [
  293. 'and',
  294. ['>=', 'service_start_at', $start_time],
  295. ['<=', 'service_start_at', $end_time],
  296. ],
  297. [
  298. 'and',
  299. ['>', 'service_end_at', $start_time],
  300. ['<=', 'service_end_at', $end_time],
  301. ]
  302. ])
  303. ->one();
  304. $reservation_service_user = ReservationServiceUser::findOne(['mall_id' => $this->mall_id, 'store_id' => $this->store_id, 'user_id' => $staff_user_id, 'status' => StatusEnum::ENABLED]);
  305. if (empty($reservation_service_user)) {
  306. throw new \Exception('技师不存在');
  307. }
  308. if (!empty($res)) {
  309. throw new \Exception($reservation_service_user['name'] . '技师在该时间有预约,请重新选择');
  310. }
  311. $model = ReservationServiceOccupy::find()->where([
  312. 'mall_id' => $this->mall_id, 'store_id' => $this->store_id, 'staff_user_id' => $staff_user_id,
  313. 'start_at' => $start_time, 'status' => StatusEnum::ENABLED
  314. ])->andWhere([
  315. 'or',
  316. [
  317. 'and',
  318. ['>=', 'start_at', $start_time],
  319. ['<=', 'start_at', $end_time],
  320. ],
  321. [
  322. 'and',
  323. ['>=', 'end_at', $start_time],
  324. ['<=', 'end_at', $end_time],
  325. ]
  326. ])->one();
  327. if (!empty($model)) {
  328. throw new \Exception('该时间段已被占用');
  329. }
  330. $model = new ReservationServiceOccupy();
  331. $model->loadDefaultValues();
  332. $model->mall_id = $this->mall_id;
  333. $model->store_id = $this->store_id;
  334. $model->staff_user_id = $staff_user_id;
  335. $model->start_at = $start_time;
  336. $model->end_at = $end_time;
  337. $model->save();
  338. return ['id' => $model['id']];
  339. }
  340. public function canceledOccupy($params)
  341. {
  342. $model = ReservationServiceOccupy::findOne(['id' => $params['id'], 'store_id' => $this->store_id, 'mall_id' => $this->mall_id]);
  343. if (empty($model)) {
  344. throw new \Exception('占用记录不存在');
  345. }
  346. $model->status = StatusEnum::DISABLED;
  347. $res = $model->save();
  348. if ($res == false) {
  349. throw new \Exception($model->getErrorMessage());
  350. }
  351. return ['id' => $model['id']];
  352. }
  353. public function getSkuList()
  354. {
  355. $goods_list = StoreGoods::lists([
  356. 'where' => [
  357. ['store_id' => $this->store_id],
  358. ['status' => StatusEnum::ENABLED],
  359. ['check_status' => StatusEnum::ENABLED],
  360. ['is_on_sale' => StatusEnum::ENABLED],
  361. ['goods_type' => GoodsTypeEnum::GoodsTypeReserved],
  362. ],
  363. 'select' => 'id,goods_name',
  364. 'index' => 'id'
  365. ]);
  366. $goods_ids = array_column($goods_list, 'id');
  367. $sku_list = StoreGoodsReserveSku::lists([
  368. 'where' => [
  369. ['store_id' => $this->store_id],
  370. ['goods_id' => $goods_ids],
  371. ],
  372. 'select' => 'id,goods_id,service_min,name',
  373. ]);
  374. foreach ($sku_list as &$sku) {
  375. $sku['goods_name'] = $goods_list[$sku['goods_id']]['goods_name'] . ':' . $sku['name'];
  376. }
  377. return $sku_list;
  378. }
  379. public function setEditData($params)
  380. {
  381. $params['sku_id'] = $params['master'][0]['sku_id'];
  382. $params['nickname'] = $params['username'] ?? '';
  383. $params['reservation_model'] = 2;
  384. $ret = (new ReserveOrderService(['store_id' => $this->store_id, 'mall_id' => $this->mall_id]))->setEditData($params);
  385. return ['id' => $ret['id']];
  386. }
  387. }