services->setting->addons->get($mall_id, ReservationServiceEnum::ADDONS_NAME, 'basic') ?? []; if (empty($basic['is_enable']) && empty($params['store_id'])) { throw new \Exception('未开启预约服务'); } $max_distance = $basic['max_distance'] ?? 20; //限制 $is_working = $params['is_working'] ?? 1; $working_field = empty($params['store_id']) ? 'is_working' : 'store_status'; $where[] = ['mall_id' => $params['mall_id'],'is_working'=> 1,'status' => StatusEnum::ENABLED,'check_status'=>StatusEnum::ENABLED ]; $min_age = $params['min_age'] ?? 0; $max_age = $params['max_age'] ?? 0; $gender = $params['gender'] ?? 0; $name = $params['name'] ?? ''; $goods_id = $params['goods_id'] ?? 0; $sku_id = $params['sku_id'] ?? 0; $service_min = 30; $user_ids = []; $longitude = 'longitude'; $latitude = 'latitude'; $s_longitude = 'longitude'; $s_latitude = 'latitude'; if($goods_id && $sku_id){ $select_date = $params['date'] ?? date('Y-m-d'); $select_time = $params['time'] ?? ''; if (!empty($params['store_id'])) { // 取出门店下面的技师 if(empty($params['id'])){ $user_ids = ReservationServiceUser::find()->where(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id'],'status'=>StatusEnum::ENABLED])->select('user_id')->column(); if (empty($user_ids)) throw new \Exception("当前门店暂无技师"); }else{ $user_ids[] = $params['id']; } $sku = StoreGoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); $longitude = 'store_lng'; $latitude = 'store_lat'; $s_longitude = 'store_lng as longitude'; $s_latitude = 'store_lat as latitude'; } else { if(empty($params['id'])){ $support_user_list = ReservationServiceGoods::find()->where(['mall_id' => $params['mall_id'],'goods_id' => $goods_id,'status'=>StatusEnum::ENABLED])->select('user_id')->asArray()->all(); $user_ids = array_column($support_user_list,'user_id'); $user_ids = array_unique($user_ids); if(!$user_ids) throw new \Exception('没有技师支持该项目'); }else{ $user_ids[] = $params['id']; } $sku = GoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); if (!empty($select_time)) { $tech_ids = OrderReserve::find() ->where(['<=', 'service_start_at', strtotime($select_date . ' ' . $select_time . ':00')]) ->andWhere(['>', 'service_end_at', strtotime($select_date . ' ' . $select_time . ':00')]) ->andWhere(['!=', 'tech_id', 0]) ->andWhere(['!=', 'order_status', -4]) ->select('tech_id')->column(); $booked_user_ids = ReservationServiceUser::find()->where(['id' => $tech_ids, 'status' => StatusEnum::ENABLED])->select('user_id')->column(); } } $service_min = $sku['service_min'] ?? 30; //服务分钟 if($select_time){ $select_timestamp = strtotime($select_date.' '.$select_time); $select_time_arr = explode(':',$select_time); $start_time = $select_time_arr[0]*100+ $select_time_arr[1] *1; $end_time = $start_time + $service_min; $select_timestamp_end = $select_timestamp + 60 * $service_min; $select_week_day = date('w',strtotime($select_date)); $user_times = ReservationServiceWorkTime::find()->where(['mall_id' => $mall_id,'user_id' => $user_ids,'status'=>StatusEnum::ENABLED]) ->andWhere(['week_day' => $select_week_day]) ->andWhere(['<=','start_time',$start_time]) ->andWhere(['>=', 'end_time', $start_time]) ->select('user_id') ->asArray()->all(); if(!$user_times) throw new \Exception('该时间下没有技师支持该项目'); $user_ids = array_column($user_times,'user_id'); } //排除当前有项目的技师 $user_ids = self::checkCanBook($mall_id,$user_ids,$select_timestamp,$select_timestamp_end); if(!$user_ids) throw new \Exception('该时间已满员'); $scheduling_user_ids = self::getSchedulingUserIds($mall_id, $select_date, $select_time); } $sql = "SELECT id,mall_id,user_id,`level`, {$s_longitude}, {$s_latitude},`name`,service_rating,order_num,gender,age,`desc`,avatar,store_id, address, round( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * ({$params['lng']} - {$longitude}) / 360 ), 2 ) + COS(PI() * {$params['lat']} / 180) * COS({$latitude} * PI() / 180) * POW( SIN( PI() * ({$params['lat']} - {$latitude}) / 360 ), 2 ) ) ) ,1) AS distance FROM `qimall_addons_reservation_service_user` WHERE (`mall_id`={$mall_id}) AND (`status`=1) AND (`is_working`=1) AND (`check_status`= 1) AND (`{$working_field}` ={$is_working}) HAVING (`distance`<={$max_distance})"; if ($gender > 0){ $gender = $gender > 1 ? 2: 1; $sql .= " AND `gender` = " . $gender; } if ($min_age > 0 || $max_age > 0){ $sql .= " AND `age` >= " . $min_age; $sql .= " AND `age` <= " . $max_age; } if($name){ $name = trim($name); $sql .= " AND `name` LIKE '%" . $name . "%'"; } if($user_ids){ $sql .= " AND `user_id` in (" . implode(',',$user_ids) . ")"; } if (!empty($booked_user_ids)) { $sql .= " AND `user_id` not in (" . join(',', $booked_user_ids) . ")"; } if (!empty($scheduling_user_ids)) { $sql .= " AND `user_id` not in (" . join(',', $scheduling_user_ids) . ")"; } $count_sql = $sql; $orderBy = 'distance asc,id desc'; //1-距离 if (isset($params['sort']) && $params['sort'] > 1){ //1-距离,2-订单量 order_num ,3-好评 service_rating if($params['sort'] == 1){ $orderBy = 'distance asc'; //1-距离 }else{ $orderBy = $params['sort'] == 2 ? 'order_num desc' : 'service_rating desc'; } } $total_data = \Yii::$app->db->createCommand($count_sql)->queryAll(); $count = count($total_data); $sql .= " ORDER BY {$orderBy}"; $sql .= " limit " . ($page - 1) * $limit . ",{$limit}"; $list = \Yii::$app->db->createCommand($sql)->queryAll(); $default_jishi = []; if (!empty($list)) { $location_obj = new LocationHelper(); $store_ids = array_column($list,'store_id'); $store_ids = array_unique($store_ids); if (MallAddons::isInstall($mall_id, 'Store')) { $store_list = Store::find()->where(['id' => $store_ids, 'mall_id' => $mall_id])->select('id,store_name')->indexBy('id')->asArray()->all(); } $comments = OrderReserveComments::lists([ 'where' => [ ['tech_user_id' => array_column($list, 'user_id')], ['reserve_type' => StatusEnum::ENABLED], ['store_id'=>0] ], 'select'=> 'COUNT(*) as total, tech_user_id', 'group' => 'tech_user_id', 'index' => 'tech_user_id' ]); foreach ($list as &$item) { $item['store_name'] = $store_list[$item['store_id']]['store_name'] ?? '平台自营'; $distance = $location_obj->getDistanceByGeo($item['latitude'], $item['longitude'],$params['lat'] , $params['lng']); // $item['distance'] = bcmul(bcmul($distance, 1000, 4), 1, 1); //米 $item['distance'] = LocationHelper::formatDistance($distance); //米 $item['comment_num'] = $comments[$item['user_id']]['total'] ?? 0; $item['commentNum'] = $comments[$item['user_id']]['total'] ?? 0; $item['serviceNum'] = $item['order_num']; $item['detail'] = $item['desc']; $item['gender_txt'] = ReservationServiceEnum::GENDER_MAP[$item['gender']]; } if($goods_id && $sku_id){ //默认技师,时间 // $default_jishi = $list[0]; $service_user = new UserService(); foreach ($list as $key1 => $item1) { $default_jishi = $item1; $default_jishi_times = $service_user->getWorkingTime($mall_id, $item1['user_id']); if (!empty($default_jishi_times)) break; } unset($list[$key1]); array_unshift($list, $default_jishi); $time1 = date('Y-m-d 00:00:00'); $time2 = date('Y-m-d 23:59:59'); $buff = []; $service_min = $service_min <= 0 ? 30 : $service_min; for($i=strtotime($time1) ;$i<=strtotime($time2) ; $i=$i+60*$service_min){ $buff[]= ['time' => date("H:i",$i),'time_num' => 100 * date("H",$i)+date("i",$i)]; } $data = BackendService::getSetting($mall_id); $range_start_time = self::getRangeStartTime($data['basic']['range_start'] ?? 0); $time = time(); $today_day = date('Y-m-d', $time); if ($range_start_time) { $time += $range_start_time; } //最近7天是否可约 for($i = 0;$i < 7;$i++){ $w = date('w',strtotime("+$i days")); $date = date('Y-m-d',strtotime("+$i days")); $day_times = $default_jishi_times[$w] ?? []; if($day_times){ foreach($day_times as $day_time_info){ foreach($buff as $one_buff_key => $one_buff){ if ($today_day == $date) { $temp = strtotime($date . ' ' . $one_buff['time'] . ':00'); if ($temp < $time) { continue; } } $next_buff = $buff[$one_buff_key + 1] ?? []; if($day_time_info['start_time'] >= $one_buff['time_num'] && $next_buff && $day_time_info['start_time'] < $next_buff['time_num']){ //check $end_timestamp = strtotime($date.' '.$one_buff['time']) + 60 * $service_min; $check = self::checkCanBook($mall_id,[$default_jishi['user_id']],strtotime($date.' '.$one_buff['time']),$end_timestamp); if(!$check) continue; // 如果是今天 $now_time_number = intval(date('Hi')); if ($date == date('Y-m-d') && ($day_time_info['end_time']) < $now_time_number) continue; //符合 $min = intval(date('i')); $hour = intval(date('H')); $t = $one_buff['time_num'] > $now_time_number ? $one_buff['time'] : ($min >= 30 ? sprintf("%s:00", $hour+1) : sprintf("%s:30", $hour)); if ($one_buff['time_num'] > $now_time_number && $hour == 24 && $min >= 30) { $date = strtotime("+1 days", strtotime($date)); $t = "00:00"; } $default_jishi['lately_time_arr'] = [ 'date' => $date, 'time' => $t, 'week_day' => GoodsReserveTime::getWeekDay($date) ]; break 3; } } } } } } } $list_page['list'] = $list; $list_page['page'] = $page; $list_page['page_size'] = $limit; $list_page['count'] = $count; $list_page['page_count'] = ceil($count / $limit); $list_page['default_jishi'] = $default_jishi; return $list_page; }catch(\Exception $e){ $list_page['list'] = []; $list_page['page'] = 0; $list_page['page_size'] = 10; $list_page['count'] = 0; $list_page['page_count'] = 0; $list_page['error_msg'] = $e->getMessage(); return $list_page; } } /** * 获取该时间内的有门店排班的技师user_id * @param $mall_id * @param $select_date * @param $select_time * @return array */ public static function getSchedulingUserIds($mall_id, $select_date, $select_time) { $select_time = strtotime($select_date . ' ' . $select_time . ':00'); $date_time = strtotime($select_date . ' 00:00:00'); $scheduling_user_ids = [];//在门店排班内的技师user_id if (MallAddons::isInstall($mall_id, 'Store')) { $scheduling_list = StoreReserveTechnicianScheduling::lists([ 'where' => [ ['mall_id' => $mall_id], ['status' => StatusEnum::ENABLED], ['date_time' => $date_time], ['>', 'user_id', 0], ], 'with' => ['schedulingTime'], ]); if (!empty($scheduling_list)) { $store_ids = []; foreach ($scheduling_list as $scheduling) { if ($scheduling['is_all_day'] == 1) { $store_ids[] = $scheduling['store_id']; } } if (!empty($store_ids)) { $store_list = Store::lists([ 'where' => [ ['mall_id' => $mall_id], ['id' => $store_ids], ], 'index' => 'id' ]); } foreach ($scheduling_list as $scheduling) { if ($scheduling['is_all_day'] == 1) { $store_info = $store_list[$scheduling['store_id']] ?? []; $business_time_start = $store_info['business_time_start'] ?? ''; $business_time_end = $store_info['business_time_end'] ?? ''; $business_time_start = strtotime($select_date . ' ' . $business_time_start . ':00'); $business_time_end = strtotime($select_date . ' ' . $business_time_end . ':00'); if ($select_time >= $business_time_start && $select_time <= $business_time_end) { $scheduling_user_ids[] = $scheduling['user_id']; } } else { if (!empty($scheduling['schedulingTime'])) { foreach ($scheduling['schedulingTime'] as $val) { if ($select_time >= $val['start_time'] && $select_time <= $val['end_time']) { $scheduling_user_ids[] = $scheduling['user_id']; break; } } } } } } } return $scheduling_user_ids; } public static function getRangeStartTime($range_start) { $range_start_time = 0; switch ($range_start) { case 2: $range_start_time = 30 * 60; break; case 3: $range_start_time = 60 * 60; break; } return $range_start_time; } public static function searchBySecondaryCard($params,$page,$limit){ try{ $mall_id = $params['mall_id']; if(!$params['lat'] || !$params['lng']){ throw new \Exception('经纬度缺失'); } if(!MallAddons::isInstall($mall_id,'SecondaryCard')){ throw new \Exception('没有安装次卡插件'); } $select = 'id,reservation_time_type,reservation_time,store_type,use_type,reservation_model'; $secondary_card = SecondaryCard::getOne([['id'=>$params['secondary_card_id']]],true,[],false,$select); switch ($secondary_card['store_type']){ case 1: if (!MallAddons::isInstall($mall_id, 'Store')) { throw new \Exception('没有安装o2o门店插件'); } break; } if (!empty($params['store_id'] && $secondary_card['reservation_model'] == 2)) { $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $mall_id, 'store_id' => $params['store_id']]))->detail(); $employee_selection = $store_reserve_setting['employee_selection'] ?? 1; if ($employee_selection == 3) { $list_page['list'] = []; $list_page['page'] = 0; $list_page['page_size'] = 10; $list_page['count'] = 0; $list_page['page_count'] = 0; $list_page['error_msg'] = '技师不可选'; return $list_page; } } if($secondary_card['use_type'] != SecondaryCardEnum::USE_TYPE_ALL){ $secondary_card_relate_store_list = SecondaryCardRelateStore::lists([ 'where'=>[ ['mall_id'=>$mall_id], ['secondary_card_id'=>$params['secondary_card_id']], ['status'=>StatusEnum::ENABLED] ] ]); $store_id_arr = array_column($secondary_card_relate_store_list,'store_id'); } $longitude= $params['lng']; $latitude= $params['lat']; $use_type = $secondary_card['use_type']; switch ($secondary_card['store_type']) { //o2o门店 case 1: //预约模式:1:预约门店;2:预约服务人 switch ($secondary_card['reservation_model']) { case 1: $offset = ($page - 1) * $limit; $sql = " SELECT id,store_name,shop_mobile,latitude,longitude,logo_img,sales_volume,praise,address,business_time_start,business_time_end, round( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * ({$longitude} - longitude) / 360 ), 2 ) + COS(PI() * {$latitude} / 180) * COS(latitude * PI() / 180) * POW( SIN( PI() * ({$latitude} - latitude) / 360 ), 2 ) ) ) ,1) AS distance FROM `qimall_addons_store` WHERE (`mall_id`={$mall_id})"; if (!empty($use_type)) { if (!empty($store_id_arr)) { $o2o_store_ids = join(',', $store_id_arr); } else { $o2o_store_ids = 0; } $sql .= " AND (`id` in ({$o2o_store_ids})) "; } if(!empty($params['store_cate_id'])){ $sql .= " AND `c_id` = ".$params['store_cate_id']; } if(!empty($params['name'])){ $sql .= " AND `store_name` like '%".$params['name']."%' "; } $count_sql = $sql; $total_data = \Yii::$app->db->createCommand($count_sql)->queryAll(); $count = count($total_data); $orderBy = 'distance asc,id desc '; //1-距离 if (isset($params['sort']) && $params['sort'] > 1){ //1-距离,2-订单量 order_num ,3-好评 service_rating if($params['sort'] == 1){ $orderBy = 'distance asc'; //1-距离 }else{ $orderBy = $params['sort'] == 2 ? 'sales_volume desc' : 'praise desc '; } } $sql .= " ORDER BY {$orderBy} limit {$limit} offset {$offset} "; $list = \Yii::$app->db->createCommand($sql)->queryAll(); $list_page['list'] = $list; $list_page['page'] = $page; $list_page['page_size'] = $limit; $list_page['count'] = $count; $list_page['page_count'] = ceil($count / $limit); return $list_page; case 2: break; } break; } $basic = \Yii::$app->services->setting->addons->get($mall_id, ReservationServiceEnum::ADDONS_NAME, 'basic') ?? []; $max_distance = $basic['max_distance'] ?? 20; //限制 $is_working = $params['is_working'] ?? 1; $working_field = empty($params['store_id']) ? 'is_working' : 'store_status'; $where[] = ['mall_id' => $params['mall_id'],'is_working'=> 1,'status' => StatusEnum::ENABLED,'check_status'=>StatusEnum::ENABLED ]; $min_age = $params['min_age'] ?? 0; $max_age = $params['max_age'] ?? 0; $gender = $params['gender'] ?? 0; $name = $params['name'] ?? ''; $service_min = $secondary_card['appointment_interval_duration'] ?? 30; $user_ids = []; $longitude = 'longitude'; $latitude = 'latitude'; $s_longitude = 'longitude'; $s_latitude = 'latitude'; if (!empty($params['store_id']) || $secondary_card['use_type'] == SecondaryCardEnum::USE_TYPE_ALL) { $date = $params['date'] . ' ' . $params['time'] . ':00'; $start_time = strtotime($date); $end_time = $start_time + $service_min * 60; // 取出门店下面的技师 $where = ['mall_id' => $params['mall_id'],'status'=>StatusEnum::ENABLED]; $where['store_id'] = $params['store_id']; $where['secondary_card_id'] = $params['secondary_card_id']; $user_ids = ReservationServiceStoreSecondaryCard::find()->where($where)->select('user_id')->column(); if (empty($user_ids)) { throw new \Exception("当前门店暂无技师"); } $longitude = 'store_lng'; $latitude = 'store_lat'; $s_longitude = 'store_lng as longitude'; $s_latitude = 'store_lat as latitude'; $new_user_ids = self::getNewUserIds($user_ids, $params['date'], $start_time, $end_time); $reserve_user_ids = self::getReserveUserIds($mall_id, $params['store_id'], $date); $occupy_user_ids = self::getOccupyUserIds($mall_id, $params['store_id'], $start_time, $end_time); $user_ids = array_diff($new_user_ids, $reserve_user_ids, $occupy_user_ids); } if (empty($user_ids)) { throw new \Exception('无可预约技师'); } $sql = "SELECT id,mall_id,user_id,`level`, {$s_longitude}, {$s_latitude},`name`,service_rating,order_num,gender,age,`desc`,avatar,store_id, address, round( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * ({$params['lng']} - {$longitude}) / 360 ), 2 ) + COS(PI() * {$params['lat']} / 180) * COS({$latitude} * PI() / 180) * POW( SIN( PI() * ({$params['lat']} - {$latitude}) / 360 ), 2 ) ) ) ,1) AS distance FROM `qimall_addons_reservation_service_user` WHERE (`mall_id`={$mall_id}) AND (`status`=1) AND (`check_status`= 1) AND (`{$working_field}` ={$is_working}) HAVING (`distance`<={$max_distance})"; if ($gender > 0){ $gender = $gender > 1 ? 2: 1; $sql .= " AND `gender` = " . $gender; } if ($min_age > 0 || $max_age > 0){ $sql .= " AND `age` >= " . $min_age; $sql .= " AND `age` <= " . $max_age; } if($name){ $name = trim($name); $sql .= " AND `name` LIKE '%" . $name . "%'"; } $sql .= " AND `user_id` in (" . implode(',', $user_ids) . ")"; if (!empty($params['store_id'])) { $sql .= " AND `store_id` = " . $params['store_id']; } $count_sql = $sql; $orderBy = 'distance asc,id desc'; //1-距离 if (isset($params['sort']) && $params['sort'] > 1){ //1-距离,2-订单量 order_num ,3-好评 service_rating if($params['sort'] == 1){ $orderBy = 'distance asc'; //1-距离 }else{ $orderBy = $params['sort'] == 2 ? 'order_num desc' : 'service_rating desc'; } } $total_data = \Yii::$app->db->createCommand($count_sql)->queryAll(); $count = count($total_data); $sql .= " ORDER BY {$orderBy}"; $sql .= " limit " . ($page - 1) * $limit . ",{$limit}"; $list = \Yii::$app->db->createCommand($sql)->queryAll(); $default_jishi = []; if (!empty($list)) { $location_obj = new LocationHelper(); $store_ids = array_column($list,'store_id'); $store_ids = array_unique($store_ids); $store_list = Store::find()->where(['id' => $store_ids,'mall_id' => $mall_id])->select('id,store_name')->indexBy('id')->asArray()->all(); $comments = OrderReserveComments::lists([ 'where' => [ ['tech_user_id' => array_column($list, 'user_id')], ['reserve_type' => StatusEnum::ENABLED] ], 'select'=> 'COUNT(*) as total, tech_user_id', 'group' => 'tech_user_id', 'index' => 'tech_user_id' ]); foreach ($list as &$item) { $item['store_name'] = $store_list[$item['store_id']]['store_name'] ?? '平台自营'; $distance = $location_obj->getDistanceByGeo($item['latitude'], $item['longitude'],$params['lat'] , $params['lng']); $item['distance'] = LocationHelper::formatDistance($distance); //米 $item['comment_num'] = $comments[$item['user_id']]['total'] ?? 0; $item['commentNum'] = $comments[$item['user_id']]['total'] ?? 0; $item['serviceNum'] = $item['order_num']; $item['detail'] = $item['desc']; } } $list_page['list'] = $list; $list_page['page'] = $page; $list_page['page_size'] = $limit; $list_page['count'] = $count; $list_page['page_count'] = ceil($count / $limit); $list_page['default_jishi'] = $default_jishi; return $list_page; }catch(\Exception $e){ $list_page['list'] = []; $list_page['page'] = 0; $list_page['page_size'] = 10; $list_page['count'] = 0; $list_page['page_count'] = 0; $list_page['error_msg'] = $e->getMessage(); return $list_page; } } /** * 获取在排班时间内的技师user_id * @param $user_ids * @param $date * @param $start_time * @param $end_time * @return array */ protected static function getNewUserIds($user_ids, $date, $start_time, $end_time) { $day_start_time = strtotime($date . ' 00:00:00'); $new_user_ids = []; $user_ids_all = StoreReserveTechnicianScheduling::find()->where(['user_id' => $user_ids, 'is_all_day' => 1, 'date_time' => $day_start_time, 'status' => StatusEnum::ENABLED]) ->select('user_id')->column(); $new_user_ids = array_merge($new_user_ids, $user_ids_all); $scheduling_list = StoreReserveTechnicianScheduling::find()->where(['user_id' => $user_ids, 'is_all_day' => 0, 'date_time' => $day_start_time, 'status' => StatusEnum::ENABLED]) ->with(['schedulingTime'])->all(); foreach ($scheduling_list as $val) { foreach ($val['schedulingTime'] as $v) { if (($v['start_time'] <= $start_time && $start_time <= $v['end_time']) && ($v['start_time'] <= $end_time)) { $new_user_ids[] = $val['user_id']; break; } } } return $new_user_ids; } /** * 获取已预约的技师user_id * @param $mall_id * @param $store_id * @param $date * @return array */ protected static function getReserveUserIds($mall_id, $store_id, $date) { $store_order_reserve_list = StoreOrderReserve::find()->where(['store_id' => $store_id, 'mall_id' => $mall_id]) ->andWhere(['!=', 'order_status', -4]) ->andWhere(['>=', 'service_start_at', time()]) ->all(); $tech_id_ids = []; $date_time = strtotime($date); foreach ($store_order_reserve_list as $value) { if ($value['service_start_at'] <= $date_time && $value['service_end_at'] > $date_time) { $tech_id_ids[] = $value['tech_id']; } } $reserve_user_ids = ReservationServiceUser::find()->where(['id' => $tech_id_ids])->select('user_id')->column();//已被预约的技师id return $reserve_user_ids; } /** * 获取已占用的技师user_id * @param $mall_id * @param $store_id * @param $start_time * @param $end_time * @return array */ protected static function getOccupyUserIds($mall_id, $store_id, $start_time, $end_time) { $occupy_list = ReservationServiceOccupy::lists([ 'where' => [ ['mall_id' => $mall_id], ['store_id' => $store_id], ['status' => StatusEnum::ENABLED], ] ] );//已占用时间 $occupy_user_ids = []; foreach ($occupy_list as $val) { if ($start_time >= $val['start_at'] && $start_time <= $val['end_at'] && $end_time <= $val['end_at']) { $occupy_user_ids[] = $val['staff_user_id']; } } return $occupy_user_ids; } /** * 检测用户时间段内是否可约 * @param $mall_id * @param $user_ids * @param $start_time * @param $end_time * @return array */ public static function checkCanBook($mall_id,$user_ids,$start_time,$end_time) { $book_user_ids = ReservationServiceUserBookTime::find()->where(['mall_id' => $mall_id,'user_id' => $user_ids,'status'=>StatusEnum::ENABLED]) ->andWhere(['or',['and',['>','start_time',$start_time],['<','start_time',$end_time]],['and',['>','end_time',$start_time],['<','end_time',$end_time]]]) ->select('user_id') ->column(); if($book_user_ids){ $user_ids = array_diff($user_ids,$book_user_ids); } return $user_ids; } /** * 检测技师是否可预约 * @param $mall_id * @param $tech_id * @param string $select_time 预约日期时间 * @param $goods_id * @param $sku_id * @param $lat * @param $lng * @param int $store_id * @return bool */ public static function checkCanOrder($mall_id,$tech_id,$select_time,$goods_id,$sku_id,$lat,$lng,$store_id = 0, $addon = '') { try{ $user_service = new UserService(); $tech = $user_service->fetchById($mall_id,$tech_id); if(!is_array($tech)) throw new \Exception($tech); if(!$tech['is_working']) throw new \Exception('当前技师尚未开启接单'); $basic = \Yii::$app->services->setting->addons->get($mall_id, ReservationServiceEnum::ADDONS_NAME, 'basic') ?? []; $max_distance = $basic['max_distance'] ?? 20; //限制 $location_obj = new LocationHelper(); $distance = $location_obj->getDistanceByGeo($tech['latitude'], $tech['longitude'],$lat , $lng); if($distance > bcmul($max_distance, 1000, 2)) throw new \Exception('超出技师接单范围,请重新选择'); //时间判断 $select_timestamp = strtotime($select_time); if (!empty($store_id)) { switch ($addon) { case AddonsEnum::ADDONS_NAME_LAO_DA_FU: $support = \addons\LaoDaFu\common\models\StoreGoods::find()->where(['mall_id' => $mall_id]) ->andWhere(['id' => $goods_id]) ->andWhere(['status' => StatusEnum::ENABLED]) ->andWhere(['goods_type' => GoodsTypeEnum::GoodsTypeReserved])->asArray()->one(); if(!$support) throw new \Exception('当前技师不支持该项目'); $sku = \addons\LaoDaFu\common\models\StoreGoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); break; default: $support = StoreGoods::find()->where(['mall_id' => $mall_id]) ->andWhere(['id' => $goods_id]) ->andWhere(['status' => StatusEnum::ENABLED]) ->andWhere(['store_id' => $store_id]) ->andWhere(['goods_type' => GoodsTypeEnum::GoodsTypeReserved])->asArray()->one(); if(!$support) throw new \Exception('当前技师不支持该项目'); $sku = StoreGoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); $service_min = $sku['service_min'] ?? 0; //服务分钟 $select_timestamp_end = $select_timestamp + 60 * $service_min; $reserve_date_time = $select_time . ',' . date('Y-m-d H:i', $select_timestamp_end); $res = StoreOrderReserve::find()->where(['tech_id' => $tech['id'], 'reserve_date_time' => $reserve_date_time]) ->andWhere(['!=', 'order_status', -4]) ->one(); if ($res) throw new \Exception('该时间段已有预定'); } } else { $support =ReservationServiceGoods::find()->where(['mall_id' => $mall_id,'goods_id' => $goods_id, 'status'=>StatusEnum::ENABLED,'user_id' => $tech['user_id']])->asArray()->one(); if(!$support) throw new \Exception('当前技师不支持该项目'); $sku = GoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); $service_min = $sku['service_min'] ?? 0; //服务分钟 $select_timestamp_end = $select_timestamp + 60 * $service_min; $has_book = ReservationServiceUserBookTime::find()->where(['mall_id' => $mall_id, 'user_id' => $tech['user_id'], 'status' => StatusEnum::ENABLED]) ->andWhere(['or', ['and', ['>', 'start_time', $select_timestamp], ['<', 'start_time', $select_timestamp_end]], ['and', ['>', 'end_time', $select_timestamp], ['<', 'end_time', $select_timestamp_end]]]) ->all(); if ($has_book) throw new \Exception('该时间段已有预定'); } return true; }catch(\Exception $e){ self::$static_error = $e->getMessage(); return false; } } /** * 获取技师可预约时间 * @param array $tech * @return array|string */ public static function getTechLatelyCanReserveAt(array $tech) { $work_times = ReservationServiceWorkTime::lists([ 'where' => [ ['user_id' => $tech['user_id']], ['status' => StatusEnum::ENABLED] ], 'select'=> 'week_day, text', 'order' => 'week_day' ]); $times = []; for ($i = 0; $i < 1; $i++) { $day_time = time() + (86400 * $i); $week = date('w', $day_time); if (!empty($work_times[$week])) { $ts = explode("-", $work_times[$week]['text']); $day = date('Y-m-d', $day_time); $t_s = strtotime("{$day} {$ts[0]}:00"); $t_e = strtotime("{$day} {$ts[1]}:00"); // 这个时间段是否可约 $select_timestamp = $t_s; $select_timestamp_end = $t_e; $has_book = ReservationServiceUserBookTime::find()->where(['user_id' => $tech['user_id'],'status'=>StatusEnum::ENABLED]) ->andWhere(['or',['and',['>','start_time',$select_timestamp],['<','start_time',$select_timestamp_end]],['and',['>','end_time',$select_timestamp],['<','end_time',$select_timestamp_end]]]) ->all(); if(empty($has_book)) { return DateHelper::dayChineseText(strtotime($day)); } } } return null; } public static function checkDate($params,$page=1,$limit=1) { try{ if(!$params['lat'] || !$params['lng']){ throw new \Exception('经纬度缺失'); } $mall_id = $params['mall_id']; $basic = \Yii::$app->services->setting->addons->get($mall_id, ReservationServiceEnum::ADDONS_NAME, 'basic') ?? []; $max_distance = $basic['max_distance'] ?? 20; //限制 $is_working = $params['is_working'] ?? 1; $working_field = empty($params['store_id']) ? 'is_working' : 'store_status'; $goods_id = $params['goods_id'] ?? 0; $sku_id = $params['sku_id'] ?? 0; $user_ids = []; $longitude = 'longitude'; $latitude = 'latitude'; $s_longitude = 'longitude'; $s_latitude = 'latitude'; if($goods_id && $sku_id){ if (!empty($params['store_id'])) { // 取出门店下面的技师 $user_ids = ReservationServiceUser::find()->where(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id'],'status'=>StatusEnum::ENABLED])->select('user_id')->column(); if (empty($user_ids)) throw new \Exception("当前门店暂无技师"); $sku = StoreGoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); $longitude = 'store_lng'; $latitude = 'store_lat'; $s_longitude = 'store_lng as longitude'; $s_latitude = 'store_lat as latitude'; } else { $support_user_list = ReservationServiceGoods::find()->where(['mall_id' => $params['mall_id'],'goods_id' => $goods_id,'status'=>StatusEnum::ENABLED])->select('user_id')->asArray()->all(); $user_ids = array_column($support_user_list,'user_id'); $user_ids = array_unique($user_ids); if(!$user_ids) throw new \Exception('没有技师支持该项目'); $sku = GoodsReserveSku::findOne(['id' => $sku_id,'goods_id' => $goods_id]); } $select_date = $params['date'] ?? date('Y-m-d'); $start_time = strtotime($select_date.' 00:00:00'); $end_time = strtotime($select_date.' 23:59:59'); $select_week_day = date('w',strtotime($select_date)); $user_times = ReservationServiceWorkTime::find()->where(['mall_id' => $mall_id,'user_id' => $user_ids,'status'=>StatusEnum::ENABLED]) ->andWhere(['week_day' => $select_week_day]) ->andWhere(['<=','start_time',$start_time]) ->andWhere(['>=','end_time',$end_time]) ->select('user_id') ->asArray()->all(); if(!$user_times) throw new \Exception('暂无预约'); $user_ids = array_column($user_times,'user_id'); //排除当前有项目的技师 $user_ids = self::checkCanBook($mall_id,$user_ids,$start_time,$end_time); if(!$user_ids) throw new \Exception('暂无预约'); } $sql = "SELECT id,mall_id,user_id,`level`, {$s_longitude}, {$s_latitude},`name`,service_rating,order_num,gender,age,`desc`,avatar,store_id, address, round( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * ({$params['lng']} - {$longitude}) / 360 ), 2 ) + COS(PI() * {$params['lat']} / 180) * COS({$latitude} * PI() / 180) * POW( SIN( PI() * ({$params['lat']} - {$latitude}) / 360 ), 2 ) ) ) ,1) AS distance FROM `qimall_addons_reservation_service_user` WHERE (`mall_id`={$mall_id}) AND (`status`=1) AND (`is_working`=1) AND (`check_status`= 1) AND (`{$working_field}` ={$is_working}) HAVING (`distance`<={$max_distance})"; if($user_ids){ $sql .= " AND `user_id` in (" . implode(',',$user_ids) . ")"; } $sql .= " limit " . ($page - 1) * $limit . ",{$limit}"; $list = \Yii::$app->db->createCommand($sql)->queryAll(); if(empty($list)) throw new \Exception('暂无预约'); return true; }catch(\Exception $e){ throw new \Exception($e->getMessage()); } } /** * 获取技师某日的时间段 * @param $params * @return array * @throws \Exception */ public static function getTimes($params) { if(empty($params['tech_user_id'])){ $res = self::search($params,$params['page'],$params['limit']); $user_ids = []; if(!empty($res['list'])){ $user_ids = array_column($res['list'],'user_id'); } }else{ $user_ids = $params['tech_user_id']; } $time = time(); if (empty($params['store_id'])) { $buff = self::platformStaffTimeList($params,$user_ids,$time); }else{ $buff = self::storeStaffTimeList($params,$user_ids,$time); } return $buff; } /** * 获取平台技师某天的工作时间段 * @param $params * @param $user_ids * @param $time * @return array * @throws \Exception */ protected static function platformStaffTimeList($params,$user_ids,$time) { $buff = []; $goods_reserve = GoodsReserve::findOne(['goods_id' => $params['goods_id'], 'reserve_type' => GoodsReserve::RESERVE_TYPE_SERVICE]); if (!$goods_reserve) { throw new \Exception('项目不存在'); } $sku = GoodsReserveSku::findOne(['id' => $params['sku_id'], 'goods_id' => $params['goods_id']]); if (!$sku) { throw new \Exception('项目不存在'); } $work_time_list = ReservationServiceWorkTime::lists([ 'where'=>[ ['user_id'=>$user_ids], ['week_day'=> date('w',strtotime($params['date'].' 00:00:00'))], ['mall_id'=>$params['mall_id']], ['status'=>StatusEnum::ENABLED] ] ]); if(!empty($params['tech_user_id'])) { $list = ReservationServiceOrder::lists([ 'where' => [ ['mall_id' => $goods_reserve['mall_id']], ['staff_user_id' => $user_ids], ['store_id' => 0], ['>=','service_start_at',strtotime($params['date'].' 00:00:00')], ['<=','service_end_at',strtotime($params['date'].' 23:59:59')], ['!=','service_status',ReservationServiceEnum::RESERVE_ORDER_STATUS_CANCEL] ] ]); $scheduled_time_arr = [];//已预约时间 foreach ($list as $value){ $scheduled_time_arr[date("H:i",$value['service_start_at'])] = 1; } } $service_min = $sku['service_min'] ?: 10; //服务分钟 $time1 = date('Y-m-d 00:00:00'); $time2 = date('Y-m-d 23:59:59'); for($i=strtotime($time1) ;$i<=strtotime($time2) ; $i=$i+60*$service_min){ $is_true = 0; $temp_time = $i; foreach ($work_time_list as $item){ $time_arr = explode('-',$item['text']); $start_time = strtotime($params['date'].' '.$time_arr[0].':00'); $end_time = strtotime($params['date'].' '.$time_arr[1].':00'); if($temp_time>=$start_time && $temp_time<=$end_time){ $is_true = 1; break; } } if($is_true==1 && $i>$time){ $buff_time = date("H:i",$i); $is_reserve = 1; if(!empty($params['tech_user_id']) && isset($scheduled_time_arr[$buff_time])) { $is_reserve = 0; } $buff[]=['time'=>$buff_time,'is_reserve'=>$is_reserve]; } } return $buff; } /** * 获取门店技师某天的工作时间段 * @param $params * @param $user_ids * @param $time * @return array * @throws \Exception */ protected static function storeStaffTimeList($params,$user_ids,$time) { $buff = []; $goods_reserve = StoreGoodsReserve::findOne(['goods_id' => $params['goods_id'], 'reserve_type' => GoodsReserve::RESERVE_TYPE_SERVICE]); if (!$goods_reserve) { throw new \Exception('项目不存在1'); } $sku = StoreGoodsReserveSku::findOne(['id' => $params['sku_id'], 'goods_id' => $params['goods_id']]); if (!$sku) { throw new \Exception('项目不存在'); } if(!empty($params['tech_user_id'])) { $reservation_service_order_list = ReservationServiceOrder::lists([ 'where' => [ ['mall_id' => $goods_reserve['mall_id']], ['staff_user_id' => $user_ids], ['store_id' => $params['store_id']], ['>=','service_start_at',strtotime($params['date'].' 00:00:00')], ['<=','service_end_at',strtotime($params['date'].' 23:59:59')], ['!=','server_status',ReservationServiceEnum::CANCELED] ] ]); } $scheduling_list = StoreReserveTechnicianScheduling::lists([ 'where'=>[ ['store_id'=>$goods_reserve['store_id']], ['user_id'=>$user_ids], ['date_time'=>strtotime($params['date'].' 00:00:00')] ], 'with'=>'schedulingTime' ]); $occupy_list = ReservationServiceOccupy::lists([ 'where' => [ ['mall_id' => $goods_reserve['mall_id']], ['store_id' => $params['store_id']], ['staff_user_id' => $user_ids], ['status'=>StatusEnum::ENABLED] ] ] );//已占用时间 $service_min = $sku['service_min'] ?: 10; //服务分钟 $store = Store::findOne(['id'=>$goods_reserve['store_id']]); $business_time_start = $store['business_time_start']; $business_time_end = $store['business_time_end']; $time1 = strtotime($params['date'].' '.$business_time_start.':00'); $time2 =strtotime($params['date'].' '.$business_time_end.':00'); for($i=$time1 ;$i<=$time2 ; $i=$i+60*$service_min){ $is_true = 0; $temp_time = $i; foreach ($scheduling_list as $item){ //全天 if($item['is_all_day']){ $is_true = 1; break; }else{ //设置的时间段 foreach ($item['schedulingTime'] as $val){ if($temp_time>=$val['start_time'] && $temp_time<=$val['end_time']){ $is_true = 1; break; } } } } if($is_true==1 && $i>$time){ $buff_time = date("H:i",$i); $is_reserve = 1; if(!empty($params['tech_user_id'])) { //判断该时间段是否被人预约 foreach ($reservation_service_order_list as $service_order) { if ($service_order['service_start_at'] <= $temp_time && $service_order['service_end_at'] >= $temp_time) { $is_reserve = 0; } } if ($is_reserve == 1) { //判断该时间段是否被占用 foreach ($occupy_list as $occupy) { if ($occupy['start_time'] <= $temp_time && $occupy['end_time'] >= $temp_time) { $is_reserve = 0; } } } } $buff[]=['time'=>$buff_time,'is_reserve'=>$is_reserve]; } } return $buff; } public static function getDate($params) { $data = $params['data']; $range_end = $data['basic']['range_end']??1; $range_end_arr = ReservationServiceEnum::RANGE_END_ARR_MAP; $date_list = []; $time = time(); $i=0; $days = $range_end_arr[$range_end]??7; while (true){ if($i>=$days){ break; } $temp_time = $time + $i*86400; $date = date('Y-m-d',$temp_time); $date_list[]=['date'=>$date,'is_reserve'=>1, 'week_day' => GoodsReserveTime::getWeekDay($date)]; $i++; } if(!empty($params['start'])){ $start_time = strtotime($params['start'].' 00:00:00'); $i=0; $temp_date_list = array_column($date_list,null,'date'); $date_list = []; while (true){ if($i>=$days){ break; } $temp_time = $start_time + $i*86400; $date = date('Y-m-d',$temp_time); $is_reserve = 0; if(isset($temp_date_list[$date])){ $is_reserve = 1; } $date_list[]=['date'=>$date,'is_reserve'=>$is_reserve, 'week_day' => GoodsReserveTime::getWeekDay($date)]; $i++; } } return $date_list; } /** * 获取某个技师可以预约日期 * @param $params * @return array|void */ public static function getReservationDate($params) { if(empty($params['store_id'])){ $work_time = ReservationServiceWorkTime::lists([ 'where'=>[ ['user_id'=>$params['tech_user_id']], ['mall_id'=>$params['mall_id']], ['status'=>StatusEnum::ENABLED] ], 'index'=>'week_day' ]); $data = BackendService::getSetting($params['mall_id']); $range_end = $data['basic']['range_end']??1; $range_end_arr = ReservationServiceEnum::RANGE_END_ARR_MAP; $date_list = []; $start_time = time(); $today_end_time = strtotime(date('Y-m-d 23:59:59')); $tomorrow_start_time = strtotime(date('Y-m-d 00:00:00',strtotime('+1 day'))); $tomorrow_end_time = strtotime(date('Y-m-d 23:59:59',strtotime('+1 day'))); $after_tomorrow_start_time = strtotime(date('Y-m-d 00:00:00',strtotime('+2 day'))); $after_tomorrow_end_time = strtotime(date('Y-m-d 23:59:59',strtotime('+2 day'))); $i=0; $week_date_map = ReservationServiceEnum::WEEK_DATE_MAP; while (true){ $range_end_limit = $range_end_arr[$range_end]; if($i>=$range_end_limit){ break; } $time = $start_time + $i*86400; $week_day = date('w',$time); if(!isset($work_time[$week_day])){ continue; } $date = date('m-d',$time); if($time<=$today_end_time){ $text = '今天'; }elseif($time>=$tomorrow_start_time && $time<=$tomorrow_end_time){ $text = '明天'; }elseif($time>=$after_tomorrow_start_time && $time<=$after_tomorrow_end_time){ $text = '后天'; }else{ $text = $week_date_map[$week_day]??''; } $date_list[]=['date_day'=>[ 'date'=>$date,'text'=>$text ]]; $i++; } return $date_list; } } /** * 平台预约商品详情-服务时间列表 * @param $params * @return array * @throws \Exception */ public static function serviceTimeList($params) { $reservation_service_user = ReservationServiceUser::findOne(['user_id' => $params['id'], 'status' => StatusEnum::ENABLED]); if (empty($reservation_service_user)) { throw new \Exception('技师不存在'); } $data = BackendService::getSetting($params['mall_id']); $params['data'] = $data; $list = self::getDate($params); $sku = GoodsReserveSku::findOne(['id' => $params['sku_id']]); if (!$sku) { throw new \Exception('项目不存在'); } if (!empty($reservation_service_user['store_id'])) { $store = Store::findOne(['id' => $reservation_service_user['store_id']]); if (!empty($params['start'])) { $date = $params['start']; } else { $date = date('Y-m-d', time()); } $times = strtotime($date); $business_time_start = strtotime($date . ' ' . $store['business_time_start'] . ':00'); $business_time_end = strtotime($date . ' ' . $store['business_time_end'] . ':00'); $scheduling = StoreReserveTechnicianScheduling::lists([ 'where' => [ ['user_id' => $reservation_service_user['user_id']], ['>=', 'date_time', $times] ], 'with' => ['schedulingTime'], 'index' => 'date_time' ] );//技师在门店的排班时间 } $service_min = $sku['service_min']; $week_day_arr = []; $start_time = 0; $end_time = 0; $time = time(); $today = date('Y-m-d',$time); foreach ($list as $k=>$item){ $temp_time = strtotime($item['date'].' 00:00:00'); if($k==0){ $start_time =$temp_time; } $end_time =$temp_time; $week_day_arr[]=date('w',$temp_time); } $work_time_arr = ReservationServiceWorkTime::lists([ 'where'=>[ ['user_id'=>$params['id']], ['week_day'=> $week_day_arr], ['mall_id'=>$params['mall_id']], ['status'=>StatusEnum::ENABLED] ], ]); $work_time_list = []; foreach ($work_time_arr as $val) { $work_time_list[$val['week_day']][] = $val; } $order_list = OrderReserve::find() ->where(['tech_id' => $reservation_service_user['id'], 'mall_id' => $reservation_service_user['mall_id'],]) ->andWhere(['!=', 'order_status', -4]) ->andWhere(['>=', 'service_start_at', time()]) ->asArray()->all(); $range_start_time = self::getRangeStartTime($data['basic']['range_start'] ?? 0); $time += $range_start_time; foreach ($list as &$item) { $date_time = strtotime($item['date']); $temp_time = $date_time; $temp_week_day = date('w',$temp_time); $time_list = []; if (!empty($reservation_service_user['store_id'])) { $business_time_start = strtotime($item['date'] . ' ' . $store['business_time_start'] . ':00'); $business_time_end = strtotime($item['date'] . ' ' . $store['business_time_end'] . ':00'); } $day_is_reserve = 0; if (isset($work_time_list[$temp_week_day])) { foreach ($work_time_list[$temp_week_day] as $value) { $time_arr = explode('-', $value['text']); $work_start_time_temp = strtotime($item['date'] . ' ' . $time_arr[0] . ':00'); $work_end_time_temp = strtotime($item['date'] . ' ' . $time_arr[1] . ':00'); $work_start_time = strtotime($item['date'] . '00:00:00'); $work_end_time = strtotime($item['date'] . '23:59:59'); for ($i = $work_start_time; $i <= $work_end_time; $i = $i + 60 * $service_min) { if ($today == $item['date']) { if ($i < $time) { continue; } } if (!empty($reservation_service_user['store_id'])) { if (isset($scheduling) && isset($scheduling[$work_start_time])) { if (!empty($scheduling[$work_start_time]['is_all_day'])) { if ($i >= $business_time_start && $i <= $business_time_end) { $time_list[$i] = ['time' => date("H:i", $i), 'is_reserve' => 0]; continue; } } else { $is_continue = false; if (isset($scheduling[$work_start_time]['schedulingTime'])) { foreach ($scheduling[$work_start_time]['schedulingTime'] as $val) { $te_start_time = $val['start_time']; $te_end_time = $val['end_time']; if ($te_start_time < $business_time_start) { $te_start_time = $business_time_start; } if ($te_end_time > $business_time_end) { $te_end_time = $business_time_end; } if ($i >= $te_start_time && $i <= $te_end_time) { $is_continue = true; } } } if ($is_continue) { $time_list[$i] = ['time' => date("H:i", $i), 'is_reserve' => 0]; continue; } } } } if ($i < $work_start_time_temp || $i > $work_end_time_temp) { continue; } if ($item['date'] == $today) { if ($i < $time) { $time_list[$i] = ['time' => date("H:i", $i), 'is_reserve' => 0]; continue; } } $buff_time = date("H:i", $i); $is_reserve = 1; if (!empty($order_list)) { foreach ($order_list as $v) { if ($i >= $v['service_start_at'] && $i < $v['service_end_at']) { $is_reserve = 0; break; } } } if ($is_reserve) { $day_is_reserve = 1; } $time_list[$i] = ['time' => $buff_time, 'is_reserve' => $is_reserve]; } } } sort($time_list); $item['time_list'] = $time_list; $item['is_reserve'] = $day_is_reserve; } return $list; } /** * h5端-预约时间列表-到店分配/技师不选项 * @param $params * @return array * @throws \Exception */ public static function getReservationTimeList(int $mall_id, $params) { $config = BackendService::getSetting($mall_id); $range_start = $config['range_start']; $range_end = $config['range_end']; $store_goods_reserve_sku = GoodsReserveSku::getOne([ ['id' => $params['sku_id']] ], true, ['reserve']); if (empty($store_goods_reserve_sku)) { throw new \Exception('商品不存在'); } $service_min = self::getServiceMin(1, $store_goods_reserve_sku['service_min'] ?? 0); if (empty($service_min)) { throw new \Exception('该商品没有设置服务时长'); } $date = $params['date']; $appointments_number = $store_goods_reserve_sku['reserve']['appointments_number'] ?? 0; if (!empty($appointments_number)) { $reservation_service_order_list = OrderReserve::lists([ 'where' => [ ['mall_id' => $mall_id], ['goods_id' => $store_goods_reserve_sku['goods_id']], ['>=', 'service_start_at', strtotime($date . ' 00:00:00')], ['<=', 'service_end_at', strtotime($date . ' 23:59:59')], ['!=', 'order_status', -4], ['create_type' => 1], ], 'group' => 'service_start_at', 'select' => 'count(*) as counts,service_start_at', 'index' => 'service_start_at' ]); } $business_time_start = "08:00"; $business_time_end = "22:00"; $start_time = self::getRangeStartTime($range_start); $time1 = "{$date} {$business_time_start}:00"; $time2 = "{$date} {$business_time_end}:59"; $buff = []; $time = time(); $start = strtotime($time1); $end = strtotime($time2); //切割时间 for ($i = $start; $i <= $end; $i = $i + 60 * $service_min) { $is_reserve = 1; $hour_minute = date("H:i", $i); $buff[] = ['time' => $hour_minute, 'is_reserve' => $is_reserve]; } $range_end_arr = ReservationServiceEnum::RANGE_END_ARR_MAP; $days = $range_end_arr[$range_end] ?? 7; $date_list = []; $today_time = strtotime(date('Y-m-d', $time) . ' 00:00:00'); if (!empty($params['date'])) { $time = strtotime($params['date'] . ' 00:00:00'); } else { $time = $today_time; } $end_time = $time + $days * 24 * 3600; for ($i = $time; $i < $end_time; $i += 24 * 60 * 60) { $is_reserve = 1; $time_list = $buff; $date = date('Y-m-d', $i); $new_time_list = []; foreach ($time_list as &$item) { $temp_time_start = strtotime("{$date} " . $item['time'] . ":00"); if (!empty($appointments_number)) { if (isset($reservation_service_order_list[$temp_time_start]) && $reservation_service_order_list[$temp_time_start]['counts'] >= $appointments_number) { $item['is_reserve'] = 0; } } $te_time = strtotime($date . ' ' . $item['time'] . ':00'); if ($te_time < time()) { continue; } if ((time() + $start_time) > $te_time) { continue; } $new_time_list[] = $item; } unset($item); $arr = ['date' => $date, 'week_day' => GoodsReserveTime::getWeekDay($date), 'time_list' => $new_time_list, 'is_reserve' => $is_reserve]; $date_list[] = $arr; } return $date_list; } public static function getServiceMin($appointment_interval, $min) { switch ($appointment_interval) { case 1: $service_min = $min; break; default: $service_min = $min ?? 30; } return $service_min; } }