BikeController.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Mead
  5. * Date: 2019/8/19
  6. * Time: 2:11 PM
  7. */
  8. namespace App\Http\Controllers\V1;
  9. use App\Handlers\Activities\RidingCardActivityHandler;
  10. use App\Handlers\BaseBikeControl;
  11. use App\Handlers\BikeHandler;
  12. use App\Handlers\BikeStatusInfoSyncHandler;
  13. use App\Handlers\ConvertHandler;
  14. use App\Http\Requests\BikeCloseOrderRequest;
  15. use App\Http\Requests\BikeNoRequest;
  16. use App\Http\Requests\BikeOpenStoreOrderRequest;
  17. use App\Http\Requests\LocationRequest;
  18. use App\Jobs\BikeTemporaryWaitOverThirtyAutoCloseOrderJob;
  19. use App\Jobs\SendSMSBikeTemporaryWaitOverThirtyJob;
  20. use App\Maps\CacheMap;
  21. use App\Models\Area;
  22. use App\Models\AreaSetting;
  23. use App\Models\Bike;
  24. use App\Models\Order;
  25. use App\Models\OrderBikeOperate;
  26. use App\Models\User;
  27. use App\Repositories\AreaRepository;
  28. use App\Repositories\AreaSettingRepository;
  29. use App\Repositories\BikeRepository;
  30. use App\Repositories\LocationLogRepository;
  31. use App\Repositories\OrderRepository;
  32. use App\Repositories\ParkingRepository;
  33. use App\Repositories\PunishmentOrderRepository;
  34. use App\Repositories\RentOrderRepository;
  35. use App\Repositories\UserRepository;
  36. use App\Transformers\AreaTransformer;
  37. use App\Transformers\BikeTransformer;
  38. use App\Transformers\OrderTransformer;
  39. use Carbon\Carbon;
  40. use Illuminate\Support\Facades\Cache;
  41. use Illuminate\Support\Facades\DB;
  42. use Illuminate\Support\Facades\Log;
  43. use Illuminate\Support\Facades\Redis;
  44. use Symfony\Component\HttpKernel\Exception\HttpException;
  45. class BikeController extends BaseController
  46. {
  47. /**
  48. * 获取附近车
  49. * @param LocationRequest $request
  50. * @return mixed
  51. * User: Mead
  52. */
  53. public function index(LocationRequest $request)
  54. {
  55. $lat = $request->get('lat');
  56. $lng = $request->get('lng');
  57. try {
  58. // 查找最近车的bike_no
  59. $options = ['WITHDIST' => true, 'SORT' => 'ASC', 'WITHCOORD' => true];
  60. $redis = Redis::connection();
  61. $nearby_bikes = $redis->georadius(Bike::REDIS_BIKE_LOCATION_TAG, $lng, $lat, 1, 'km', $options);
  62. $bikes = [];
  63. if (count($nearby_bikes)) {
  64. $bikes = array_map(function ($val) {
  65. return [
  66. 'bike_no' => $val[0],
  67. 'distance' => $val[1],
  68. 'latitude' => $val[2][1],
  69. 'longitude' => $val[2][0],
  70. 'iconPath' => 'http://resource.weilaibike.com/bluebike.png',
  71. 'width' => 24,
  72. 'height' => 28,
  73. 'zIndex' => 1111,
  74. 'id' => $val[0]
  75. ];
  76. }, $nearby_bikes);
  77. $options = ['SORT' => 'ASC', 'COUNT' => 1];
  78. $redis = Redis::connection();
  79. $area_ids = $redis->georadius(Area::REDIS_AREAS_LOCATION_TAG, $lng, $lat, 30, 'km', $options);
  80. if (count($area_ids) === 1) {
  81. $area_id = $area_ids[0];
  82. $bike_nos = array_column($bikes, 'bike_no');
  83. $batteryPower = (new AreaSettingRepository(new AreaSetting()))->byAreaIdGetBatteryPower($area_id);
  84. if ($batteryPower) {
  85. $b_nos = Bike::whereIn('bike_no', $bike_nos)->where('battery_power', '<=', $batteryPower)->pluck('bike_no');
  86. if (count($bike_nos)) {
  87. foreach ($b_nos as $b_no) {
  88. $key = array_search($b_no, $bike_nos);
  89. unset($bikes[$key]);
  90. }
  91. }
  92. }
  93. $bikes = array_values($bikes);
  94. }
  95. }
  96. return $this->response->array($bikes);
  97. } catch (\Exception $exception) {
  98. $this->errorException($exception->getMessage());
  99. }
  100. }
  101. /**
  102. * 获取单个车的信息
  103. * @param $bike_no
  104. * @param BikeRepository $bikeRepository
  105. * @return \Dingo\Api\Http\Response
  106. * User: Mead
  107. */
  108. public function show($bike_no, BikeRepository $bikeRepository)
  109. {
  110. try {
  111. $bike = $bikeRepository->byBikeNo($bike_no);
  112. return $this->response->item($bike, BikeTransformer::class);
  113. } catch (\Exception $exception) {
  114. return $this->errorException('获取单车数据异常');
  115. }
  116. }
  117. /**
  118. * 响铃
  119. * @param BikeNoRequest $request
  120. * @return mixed
  121. * User: Mead
  122. */
  123. public function findBike($bike_no, BikeRepository $bikeRepository)
  124. {
  125. try {
  126. if (!$bikeRepository->byNoIsBikeCanRide($bike_no)) {
  127. return $this->errorNoValidation('车找不到或车暂停使用');
  128. }
  129. $box_no = $bikeRepository->byNoGetBoxNO($bike_no);
  130. $re = (new BaseBikeControl($box_no))::bellBike();
  131. return $this->response->array([
  132. 'is_ok' => $re
  133. ]);
  134. } catch (\Exception $exception) {
  135. return $this->errorException($exception->getMessage());
  136. }
  137. }
  138. /**
  139. * 用车订单(开车)
  140. * User: Mead
  141. */
  142. public function openLock(BikeOpenStoreOrderRequest $request, OrderRepository $orderRepository, BikeRepository $bikeRepository, RentOrderRepository $rentOrderRepository, AreaSettingRepository $areaSettingRepository, PunishmentOrderRepository $punishmentOrderRepository, AreaRepository $areaRepository, UserRepository $userRepository)
  143. {
  144. $bike_no = $request->get('bike_no');
  145. $bike_id = $bikeRepository->byBikeNoGetId($bike_no);
  146. $area_id = $request->get('area_id');
  147. $lat = $request->get('lat');
  148. $lng = $request->get('lng');
  149. $cache_key = "RIDE_ORDER_{$bike_no}";
  150. if (Cache::has($cache_key)) {
  151. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  152. }
  153. Cache::put($cache_key, 1, Carbon::now()->addSeconds(5));
  154. if (!$bike_id) {
  155. return $this->errorNoValidation('非法请求');
  156. }
  157. // 判断用户押金,授权,认证,手机号状态是否正常
  158. $user = $this->user;
  159. // if ((int)$user->is_deposit === User::DEPOSIT_NO) {
  160. // return $this->errorNoValidation('请您先交纳押金');
  161. // }
  162. // if ((int)$user->deposit_type === User::DEPOSIT_CARD && (int)$user->is_deposit === User::DEPOSIT_OK) {
  163. // // 押金类型为免押金卡 判断是否过期
  164. // if (!$userRepository->isDepositCardExpired($user->id)) {
  165. // return $this->errorNoValidation('免押金卡已到期,请您先交纳押金');
  166. // }
  167. // }
  168. if ((int)$user->is_bind_mobile === User::BIND_MOBILE_NO) {
  169. return $this->errorNoValidation('请先绑定您的手机号');
  170. }
  171. if ((int)$user->is_card_certified === User::CARD_NO) {
  172. return $this->errorNoValidation('请您先完善实名认证');
  173. }
  174. if ((int)$user->is_match_ride_age === User::RIDE_BIKE_AGE_NO) {
  175. return $this->errorNoValidation('未成年人禁止骑车');
  176. }
  177. // $area = $areaRepository->byIdGetModelRedis($area_id);
  178. // if ($area) {
  179. // return $this->errorNoValidation('暂无运营区域!');
  180. // }
  181. $punish = $punishmentOrderRepository->checkNoPayModel($user->id);
  182. if (!$punish) return $this->errorNoValidation('您有罚单未支付,请先处理');
  183. $setting = $areaSettingRepository->byAreaId($area_id);
  184. if ($setting['is_advance_recharge'] && $user->wallet_money <= 0) {
  185. return $this->errorNoValidation('余额不足,请先充值');
  186. }
  187. if ((int)$setting['is_deposit']) {
  188. if ((int)$user->is_deposit === User::DEPOSIT_NO) {
  189. return $this->errorNoValidation('请您先交纳押金');
  190. }
  191. }
  192. // 判断用户是否有为支付的订单
  193. if ($orderRepository->byUserIdCheckIsExistRideOrder($user->id)) {
  194. return $this->errorNoValidation('您有未完成的订单,请先处理');
  195. }
  196. if ($setting['is_open_day_rent'] && $rentOrderRepository->byUserIdCheckIsExistNoPayOrder($user->id)) {
  197. return $this->errorNoValidation('您有未支付的租车订单,请先处理');
  198. }
  199. // 判断车的状态是正常
  200. if (!$bikeRepository->isBikeCanRide($bike_id)) {
  201. return $this->errorNoValidation('该车暂不能用');
  202. }
  203. // 判断用户是否在车的距离范围内
  204. $bike = $bikeRepository->byIdGetModel($bike_id);
  205. //以车的区域为主
  206. if ($bike->put_area_id !== $area_id) {
  207. $area_id = $bike->put_area_id;
  208. $setting = $areaSettingRepository->byAreaId($area_id);
  209. if ($setting['is_advance_recharge'] && $user->wallet_money <= 0) {
  210. return $this->errorNoValidation('余额不足,请先充值');
  211. }
  212. if ($setting['is_open_day_rent'] && $rentOrderRepository->byUserIdCheckIsExistNoPayOrder($user->id)) {
  213. return $this->errorNoValidation('您有未支付的租车订单,请先处理');
  214. }
  215. }
  216. $options = ['SORT' => 'ASC'];
  217. $redis = Redis::connection();
  218. $nearby_bikes = $redis->georadius(Bike::REDIS_BIKE_LOCATION_TAG, $lng, $lat, 1, 'km', $options);
  219. if (!in_array($bike->bike_no, $nearby_bikes)) {
  220. return $this->errorNoValidation('小主,咱俩有点远!');
  221. }
  222. $box_no = $bikeRepository->byNoGetBoxNO($bike_no);
  223. try {
  224. $order = DB::transaction(function () use ($bikeRepository, $user, $bike_no, $bike_id, $area_id, $lat, $lng, $box_no) {
  225. $users = User::find($user['id']);
  226. // 创建订单
  227. $data = [
  228. 'user_id' => $user['id'],
  229. 'no' => Order::makeNo(),
  230. 'bike_no' => $bike_no,
  231. 'bike_id' => $bike_id,
  232. 'area_id' => $area_id,
  233. 'phone_detail' => $users->userPhoneDetail->detail ?? '',
  234. 'start_use_bike_time' => now(),
  235. 'start_use_bike_location' => [
  236. 'latitude' => $lat,
  237. 'longitude' => $lng,
  238. ],
  239. 'status' => Order::STATUS_RIDE_BIKE
  240. ];
  241. $order = Order::create($data);
  242. // 开锁
  243. (new BaseBikeControl($box_no))::openLock();
  244. // 记录日志信息
  245. OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_OPEN_BIKE, $bike_id, $user->id, $lat, $lng);
  246. // 更新车的信息(真在骑行中)
  247. $bikeRepository->byIdUpdateRideOk($bike_id);
  248. // 更新orders redis
  249. (new BikeStatusInfoSyncHandler())->toBikeRideStatus(BikeStatusInfoSyncHandler::ROLE_USER, $bike_no, [
  250. 'id' => $order->id,
  251. 'bike_id' => $order->bike_id,
  252. 'area_id' => $order->area_id
  253. ]);
  254. return $order;
  255. });
  256. (new BaseBikeControl($box_no))::openLock();
  257. if (!$user->register_area_id) {
  258. $user->register_area_id = $area_id;
  259. $user->register_area = $areaRepository->byIdGetName($area_id);
  260. $user->save();
  261. }
  262. } catch (\Exception $exception) {
  263. return $this->errorNoValidation($exception->getMessage());
  264. }
  265. return $this->response->item($order, OrderTransformer::class);
  266. }
  267. /**
  268. * 结束订单
  269. * User: Mead
  270. */
  271. public function closeLock(BikeCloseOrderRequest $request, OrderRepository $orderRepository, AreaSettingRepository $areaSettingRepository, BikeRepository $bikeRepository, LocationLogRepository $locationLogRepository)
  272. {
  273. try {
  274. $bike_no = $request->get('bike_no');
  275. $order_no = $request->get('order_no');
  276. $lat = $request->get('lat');
  277. $lng = $request->get('lng');
  278. $order = $orderRepository->byNoGetRideOrder($order_no);
  279. if (!$order) {
  280. return $this->errorNoValidation('订单不存在或订单已结算');
  281. }
  282. $user_id = $this->user->id;
  283. $cache_key = "RIDE_CLOSE_ORDER_{$user_id}";
  284. if (Cache::has($cache_key)) {
  285. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  286. }
  287. Cache::put($cache_key, 1, Carbon::now()->addSeconds(3));
  288. // 30秒内关车,关闭订单
  289. $second = Carbon::now()->diffInSeconds(Carbon::parse($order->start_use_bike_time));
  290. if ($second < AreaSetting::CLOSE_BIKE_TIME) {
  291. // 检查用户是否恶意临时关锁
  292. if ($orderRepository->checkUserMoreCloseOrder($this->user->id)) {
  293. return $this->errorNoValidation('您今天太频繁操作车辆');
  294. }
  295. }
  296. $box_no = $bikeRepository->byNoGetBoxNO($order->bike_no);
  297. //获取车的最后位置
  298. $location = $locationLogRepository->byBikeNoGetLastLocation($order->bike_no);
  299. //运动中不能锁车
  300. if ($location['speed']) {
  301. (new BaseBikeControl($box_no))::nowBikeLocation();
  302. // return $this->errorNoValidation('运动中不能关锁', 450);
  303. }
  304. // 更新车的信息
  305. $bikeModel = $bikeRepository->byIdGetModel($order->bike_id);
  306. $bikeModel->is_riding = Bike::RIDING_NO;
  307. $bikeModel->last_use_bike_end_time = date('Y-m-d H:i:s');
  308. $bikeModel->save();
  309. // 删除redis订单
  310. if ($location['lat'] <= 0) {
  311. $location['lat'] = $lat;
  312. $location['lng'] = $lng;
  313. }
  314. // 删除临时停车key
  315. $key = sprintf(CacheMap::BIKE_TEM_WAIT, $bike_no);
  316. app()->redis->del($key);
  317. // 判断是否经常这样操作(未做)
  318. if ($second <= AreaSetting::CLOSE_BIKE_TIME) {
  319. (new BaseBikeControl($box_no))::closeLock();
  320. // 记录日志信息
  321. OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_CLONE_BIKE, $order->bike_id, $this->user->id, $lat, $lng);
  322. // 关闭订单
  323. $order->status = Order::STATUS_CLOSE_ORDER;
  324. $order->end_use_bike_time = now();
  325. $order->end_use_bike_location = [
  326. 'latitude' => $lat,
  327. 'longitude' => $lng,
  328. ];
  329. $order->save();
  330. //更新redis状态
  331. (new BikeStatusInfoSyncHandler())->toBikeWaitRideStatus($bike_no, $location['lng'], $location['lat']);
  332. // 记录临时关锁
  333. return $this->response->item($order, OrderTransformer::class);
  334. } else {
  335. (new BaseBikeControl($box_no))::closeLock();
  336. // 记录日志信息
  337. OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_CLONE_BIKE, $order->bike_id, $this->user->id, $lat, $lng);
  338. }
  339. // 保存轨迹信息
  340. // 结束订单
  341. $setting = $areaSettingRepository->byAreaId($order->area_id);
  342. // 是否处于临时停车的状态
  343. if ((int)$order->status === Order::STATUS_PAUSE_BIKE) {
  344. $order->pause_bike_time_length += Carbon::now()->diffInMinutes(Carbon::parse($order->pause_bike_time));
  345. }
  346. $order->status = Order::STATUS_CLOSE_BIKE;
  347. $order->end_use_bike_time = now();
  348. $order->end_use_bike_location = [
  349. 'latitude' => $lat,
  350. 'longitude' => $lng,
  351. ];
  352. //计算用车时间
  353. $order->use_bike_time_length = ceil($second / 60);
  354. // 计算价格
  355. if (bccomp($setting->starting_price_time, $order->use_bike_time_length) >= 0) {
  356. // 起步价时间内只收起步价
  357. $time_money = $setting->starting_price;
  358. } else {
  359. $time_money = $setting->per_money * ceil(bcsub($order->use_bike_time_length, $setting->starting_price_time) / $setting->per_minute);
  360. $time_money = bcadd($time_money, $setting->starting_price, 2);
  361. }
  362. // $time_money = $setting->per_money * ceil($order->use_bike_time_length / $setting->per_minute);
  363. // 计算是否在免费分钟内
  364. if ($setting->is_minute_free == AreaSetting::FREE_MINUTE_OK && $setting->many_minute_free >= $order->use_bike_time_length) {
  365. if ($setting->minute_free_times == 0) {
  366. $time_money = 0;
  367. if ($order->preferential_type == 0) {
  368. $order->preferential_type = arr2str([Order::PREFERENTIAL_FREE_MINUTE], '-');
  369. } else {
  370. $arr = str2arr($order->preferential_type, '-');
  371. $order->preferential_type = arr2str(array_merge($arr, [Order::PREFERENTIAL_FREE_MINUTE]), '-');
  372. }
  373. } else {
  374. $key = sprintf(CacheMap::FREE_MINUTE_USER_ID, $order->user_id);
  375. app()->redis->sadd($key, $order->id);
  376. app()->redis->expireat($key, Carbon::tomorrow()->timestamp);
  377. if (app()->redis->scard($key) <= $setting->minute_free_times) {
  378. $time_money = 0;
  379. if ($order->preferential_type == 0) {
  380. $order->preferential_type = arr2str([Order::PREFERENTIAL_FREE_MINUTE], '-');
  381. } else {
  382. $arr = str2arr($order->preferential_type, '-');
  383. $order->preferential_type = arr2str(array_merge($arr, [Order::PREFERENTIAL_FREE_MINUTE]), '-');
  384. }
  385. }
  386. }
  387. }
  388. if ($time_money < 0) {
  389. $time_money = 0;
  390. }
  391. //计算骑行距离
  392. $order->use_bike_distance_length = bcdiv($location['mileage'], 1000, 2);
  393. // 根据起始点计算
  394. // $order->use_bike_distance_length = getDistance($order->start_use_bike_location['longitude'], $order->start_use_bike_location['latitude'], $order->end_use_bike_location['longitude'], $order->end_use_bike_location['latitude']);
  395. // 判断车是否在停车的区域
  396. $order->dispatch_money = 0;
  397. if ($areaSettingRepository->byAreaIdGetIsWholeAreaHuanche($order->area_id) === AreaSetting::WHOLE_AREA_HUANCHE_NO) {
  398. $BikeHandler = new BikeHandler();
  399. $is_huanche = $BikeHandler->byLocationCheckIsInStopParking($location['lat'], $location['lng'], $order->area_id, $lat, $lng);
  400. $bikeModel->is_in_parking = Bike::IN_PARKING_YES;
  401. if (!$is_huanche['status']) {
  402. // 不在还车点
  403. $dispatch_money = $BikeHandler->byDistanceGetDistanceMoney($is_huanche['distance'], $setting);
  404. $order->dispatch_money = $dispatch_money;
  405. $bikeModel->is_in_parking = Bike::IN_PARKING_NO;
  406. }
  407. } else {
  408. //全区域内可停
  409. $bikeModel->is_in_parking = Bike::IN_PARKING_YES;
  410. }
  411. $order->time_money = $time_money;
  412. $order->pause_money = 0;
  413. $order->preferential_money = 0;
  414. $order->order_money = bcadd($time_money, $order->dispatch_money, 2);
  415. $order->pay_money = $order->order_money;
  416. $order->pay_type = Order::PAY_STATUS_NO;
  417. $order->total_money = bcadd($time_money, $order->dispatch_money, 2);
  418. $order->save();
  419. $bikeModel->save();
  420. //更新redis状态
  421. (new BikeStatusInfoSyncHandler())->toBikeWaitRideStatus($bike_no, $location['lng'], $location['lat']);
  422. //关锁
  423. (new BaseBikeControl($box_no))::closeLock();
  424. // 记录日志信息
  425. // OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_CLONE_BIKE, $order->bike_id, $this->user->id, $lat, $lng);
  426. return $this->response->item($order, OrderTransformer::class);
  427. } catch (HttpException $exception) {
  428. return $this->errorNoValidation($exception->getMessage(), $exception->getStatusCode());
  429. }
  430. }
  431. /**
  432. * 临时关锁
  433. * User: Mead
  434. */
  435. public function temporaryCloseLock(BikeCloseOrderRequest $request, OrderRepository $orderRepository, BikeRepository $bikeRepository, LocationLogRepository $locationLogRepository)
  436. {
  437. $bike_no = $request->get('bike_no');
  438. $order_no = $request->get('order_no');
  439. $lat = $request->get('lat');
  440. $lng = $request->get('lng');
  441. try {
  442. $user = $this->user;
  443. $order = $orderRepository->byNoGetRideOrder($order_no);
  444. if (!$order) {
  445. return $this->errorBadRequest('请求非法');
  446. }
  447. $user_id = $this->user->id;
  448. $cache_key = "ORDER_TEMPORARY_CLOSE_BIKE_{$user_id}";
  449. if (Cache::has($cache_key)) {
  450. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  451. }
  452. Cache::put($cache_key, 1, Carbon::now()->addSeconds(3));
  453. //获取车的最后位置
  454. $location = $locationLogRepository->byBikeNoGetLastLocation($order->bike_no, CacheMap::IS_OPEN_MONGODB_DUG);
  455. $box_no = $bikeRepository->byNoGetBoxNO($order->bike_no);
  456. // //运动中不能锁车
  457. if ($location['speed']) {
  458. (new BaseBikeControl($box_no))::nowBikeLocation();
  459. // return $this->errorNoValidation('运动中不能关锁', 450);
  460. }
  461. $order->status = Order::STATUS_PAUSE_BIKE;
  462. $order->pause_bike_time = now();
  463. $order->save();
  464. // 关锁
  465. (new BaseBikeControl($box_no))::temporaryCloseLock();
  466. // 记录日志信息
  467. OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_PAUSE_CLONE_BIKE, $order->bike_id, $user->id, $lat, $lng);
  468. //修改rides
  469. (new BikeStatusInfoSyncHandler())->toBikeTemporaryWaitRideStatus($bike_no);
  470. // 延迟分发一个临时停车超过30分 提醒队列
  471. $key = sprintf(CacheMap::BIKE_TEM_WAIT, $bike_no);
  472. app()->redis->setex($key, CacheMap::BIKE_TEM_WAIT_TIME, Carbon::now());
  473. $mobile = $user->mobile;
  474. $start_use_time = $order->start_use_bike_time;
  475. $this->dispatch(new SendSMSBikeTemporaryWaitOverThirtyJob($mobile, $bike_no, $start_use_time, $key, Carbon::now()->addMinutes(30)));
  476. $this->dispatch(new BikeTemporaryWaitOverThirtyAutoCloseOrderJob($mobile, $bike_no, $start_use_time, $key, Carbon::now()->addMinutes(60)));
  477. return $this->response->array([
  478. 'is_close_lock' => true
  479. ]);
  480. } catch (HttpException $exception) {
  481. return $this->errorNoValidation($exception->getMessage(), $exception->getStatusCode());
  482. }
  483. }
  484. /**
  485. * 临时开锁
  486. * User: Mead
  487. */
  488. public function temporaryOpenLock(BikeCloseOrderRequest $request, OrderRepository $orderRepository, BikeRepository $bikeRepository)
  489. {
  490. try {
  491. $bike_no = $request->get('bike_no');
  492. $order_no = $request->get('order_no');
  493. $lat = $request->get('lat');
  494. $lng = $request->get('lng');
  495. $user = $this->user;
  496. $order = $orderRepository->byNoGetRideOrder($order_no);
  497. if (!$order) {
  498. return $this->errorNoValidation('订单不存在或订单已结算');
  499. }
  500. $user_id = $this->user->id;
  501. $cache_key = "ORDER_TEMPORARY_OPEN_BIKE_{$user_id}";
  502. if (Cache::has($cache_key)) {
  503. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  504. }
  505. Cache::put($cache_key, 1, Carbon::now()->addSeconds(3));
  506. $order->status = Order::STATUS_RIDE_BIKE;
  507. $order->pause_bike_time_length += Carbon::now()->diffInMinutes(Carbon::parse($order->pause_bike_time));
  508. $order->save();
  509. // 开锁
  510. $box_no = $bikeRepository->byNoGetBoxNO($bike_no);
  511. (new BaseBikeControl($box_no))::temporaryOpnLock();
  512. // 记录日志信息
  513. OrderBikeOperate::log($order->id, OrderBikeOperate::TYPE_PAUSE_OPEN_BIKE, $order->bike_id, $user->id, $lat, $lng);
  514. (new BikeStatusInfoSyncHandler())->toBikeTemporaryRideStatus($bike_no);
  515. // 删除临时停车时得缓存key
  516. $key = sprintf(CacheMap::BIKE_TEM_WAIT, $bike_no);
  517. app()->redis->del($key);
  518. return $this->response->array([
  519. 'is_open_lock' => true
  520. ]);
  521. } catch (HttpException $exception) {
  522. return $this->errorNoValidation($exception->getMessage(), $exception->getStatusCode());
  523. }
  524. }
  525. /**
  526. * 判断是否在运营范围
  527. * Author: Mead
  528. */
  529. public function checkBikeIsRidingArea(BikeCloseOrderRequest $request, BikeRepository $bikeRepository, OrderRepository $orderRepository, LocationLogRepository $locationLogRepository, AreaRepository $areaRepository)
  530. {
  531. $bike_no = $request->get('bike_no');
  532. $order_no = $request->get('order_no');
  533. $lat = $request->get('lat');
  534. $lng = $request->get('lng');
  535. try {
  536. $order = $orderRepository->byNo($order_no);
  537. if (!$order) return $this->errorBadRequest('非法请求');
  538. $second = Carbon::now()->diffInSeconds(Carbon::parse($order->start_use_bike_time));
  539. if ($second < AreaSetting::CLOSE_BIKE_TIME) {
  540. //小于60秒直接锁车
  541. return [
  542. 'is_close_order' => true,
  543. ];
  544. }
  545. $user_id = $this->user['id'];
  546. $cache_key = "ORDER_CHECK_BIKE_IS_RIDINGAREA_{$user_id}";
  547. if (Cache::has($cache_key)) {
  548. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  549. }
  550. Cache::put($cache_key, 1, Carbon::now()->addSeconds(3));
  551. $box_no = $bikeRepository->byNoGetBoxNO($order->bike_no);
  552. (new BaseBikeControl($box_no))::nowBikeLocation();
  553. $location = $locationLogRepository->byBikeNoGetLastLocation($order->bike_no, CacheMap::IS_OPEN_MONGODB_DUG);
  554. if ($location['lat'] <= 0) {
  555. $location['lat'] = $lat;
  556. $location['lng'] = $lng;
  557. }
  558. $area = $areaRepository->byIdGetModelRedis($order->area_id);
  559. //检查是否有运营区域
  560. if (!$area) {
  561. return $this->errorNoValidation('暂无运营区域!');
  562. }
  563. //检查是否在骑行区域
  564. $is_out_area = $this->isOutArea($location['lat'], $location['lng'], $area);
  565. if (!$is_out_area) {
  566. return $this->errorNoValidation('超出骑行区域,暂不能锁车');
  567. }
  568. $BikeHandler = new BikeHandler();
  569. //检查是否在禁停区
  570. $is_ban_stop_bike = $BikeHandler->byLocationCheckIsInBanStopParking($location['lat'], $location['lng'], $order->area_id, $lat, $lng);
  571. if ($is_ban_stop_bike) {
  572. return $this->errorNoValidation('禁停区域内禁止停车!');
  573. }
  574. return [
  575. 'is_close_order' => false,
  576. ];
  577. } catch (HttpException $exception) {
  578. return $this->errorNoValidation($exception->getMessage());
  579. }
  580. }
  581. /**
  582. * 检查是否在还车点
  583. * @param BikeCloseOrderRequest $request
  584. * @param OrderRepository $orderRepository
  585. * @param ParkingRepository $parkingRepository
  586. * @param AreaSettingRepository $areaSettingRepository
  587. * User: Mead
  588. */
  589. public function checkBikeIsInStopParking(BikeCloseOrderRequest $request, BikeRepository $bikeRepository, OrderRepository $orderRepository, AreaSettingRepository $areaSettingRepository, LocationLogRepository $locationLogRepository, AreaRepository $areaRepository)
  590. {
  591. $bike_no = $request->get('bike_no');
  592. $order_no = $request->get('order_no');
  593. $lat = $request->get('lat');
  594. $lng = $request->get('lng');
  595. try {
  596. $order = $orderRepository->byNo($order_no);
  597. if (!$order) return $this->errorBadRequest('非法请求');
  598. $user_id = $this->user['id'];
  599. $cache_key = "ORDER_CHECK_BIKE_IS_PARK_{$user_id}";
  600. if (Cache::has($cache_key)) {
  601. return $this->errorNoValidation('您提交的太频繁了,请一会再提交!');
  602. }
  603. Cache::put($cache_key, 1, Carbon::now()->addSeconds(3));
  604. $box_no = $bikeRepository->byNoGetBoxNO($order->bike_no);
  605. (new BaseBikeControl($box_no))::nowBikeLocation();
  606. $setting = $areaSettingRepository->byAreaId($order->area_id);
  607. $location = $locationLogRepository->byBikeNoGetLastLocation($order->bike_no, CacheMap::IS_OPEN_MONGODB_DUG);
  608. if ($location['lat'] <= 0) {
  609. $location['lat'] = $lat;
  610. $location['lng'] = $lng;
  611. }
  612. // // 判断是否在运动中
  613. // if ($location['speed']) {
  614. // return $this->errorNoValidation('运动中不能关锁', 450);
  615. // }
  616. $area = $areaRepository->byIdGetModelRedis($order->area_id);
  617. //检查是否有运营区域
  618. if (!$area) {
  619. return $this->errorNoValidation('暂无运营区域!');
  620. }
  621. //检查是否在骑行区域
  622. $is_out_area = $this->isOutArea($location['lat'], $location['lng'], $area);
  623. if (!$is_out_area) {
  624. return $this->errorNoValidation('超出骑行区域,暂不能锁车');
  625. }
  626. $BikeHandler = new BikeHandler();
  627. //检查是否在禁停区
  628. $is_ban_stop_bike = $BikeHandler->byLocationCheckIsInBanStopParking($location['lat'], $location['lng'], $order->area_id, $lat, $lng);
  629. if ($is_ban_stop_bike) {
  630. return $this->errorNoValidation('禁停区域内禁止停车!');
  631. }
  632. //判断是否全区域内可停
  633. if ($areaSettingRepository->byAreaIdGetIsWholeAreaHuanche($order->area_id) === AreaSetting::WHOLE_AREA_HUANCHE_OK) {
  634. return [
  635. 'is_dispatch' => true,
  636. 'is_stop_bike' => true,
  637. 'dispatch_money' => 0,
  638. ];
  639. }
  640. // 判断是否在停车点
  641. $is_huanche = $BikeHandler->byLocationCheckIsInStopParking($location['lat'], $location['lng'], $order->area_id, $lat, $lng);
  642. if (!$is_huanche['status']) {
  643. (new BaseBikeControl($box_no))::nowBikeLocation();
  644. // 不在还车点
  645. $dispatch_money = $BikeHandler->byDistanceGetDistanceMoney($is_huanche['distance'], $setting);
  646. return [
  647. 'is_dispatch' => false,
  648. 'is_stop_bike' => false,
  649. 'dispatch_money' => $dispatch_money,
  650. ];
  651. }
  652. return [
  653. 'is_dispatch' => true,
  654. 'is_stop_bike' => true,
  655. 'dispatch_money' => 0,
  656. ];
  657. } catch (HttpException $exception) {
  658. return $this->errorNoValidation($exception->getMessage());
  659. }
  660. }
  661. /**
  662. * 判断是否在骑行区
  663. * @param $lat
  664. * @param $lng
  665. * @param $box_no
  666. * User: Mead
  667. */
  668. private function isOutArea($lat, $lng, $area)
  669. {
  670. try {
  671. $location = [
  672. 'latitude' => $lat,
  673. 'longitude' => $lng,
  674. ];
  675. $fences = $area['area_fence'];
  676. $centre = $area['area_centre'];
  677. $radius = $area['area_radius'];
  678. $area_fushe_fence = $area['area_fushe_fence'];
  679. // 判断是否在骑行区域
  680. $ConvertHandler = (new ConvertHandler());
  681. $is_out_area = $ConvertHandler->is_point_in_polygon($location, $fences);
  682. return $is_out_area;
  683. } catch (\Exception $exception) {
  684. return $this->errorNoValidation($exception->getMessage());
  685. }
  686. // // 判断是否骑出辐射范围
  687. // $is_out_area_nearby = true;
  688. // if (!$is_out_area) {
  689. // $is_out_area_nearby = $ConvertHandler->is_point_in_polygon($location, $area_fushe_fence);
  690. // }
  691. //
  692. // return [
  693. // 'is_out_area' => $is_out_area,
  694. // 'is_out_area_nearby' => $is_out_area_nearby
  695. // ];
  696. }
  697. }