BikeController.php 37 KB

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