BikeController.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. <?php
  2. namespace App\Http\Controllers\Admin;
  3. use App\Handlers\Aes128Handler;
  4. use App\Handlers\BikeControl;
  5. use App\Handlers\BikeStatusInfoSyncHandler;
  6. use App\Http\Requests\RemarkRequest;
  7. use App\Http\Requests\BikeRequest;
  8. use App\Http\Resources\BikeResource;
  9. use App\Http\Resources\OrderRentResource;
  10. use App\Http\Resources\OrderResource;
  11. use App\Imports\BikesImport;
  12. use App\Models\AdminUser;
  13. use App\Models\AdminUserArea;
  14. use App\Models\Area;
  15. use App\Models\Bike;
  16. use App\Models\BoxBinding;
  17. use App\Models\LocationsLog;
  18. use App\Models\Order;
  19. use App\Models\OrderRent;
  20. use App\Utils\Admin;
  21. use App\Filters\BikeFilter;
  22. use App\Utils\GaodeMaps;
  23. use Carbon\Carbon;
  24. use Illuminate\Http\Request;
  25. use App\Http\Controllers\Controller;
  26. use Illuminate\Support\Facades\DB;
  27. use Illuminate\Support\Facades\Log;
  28. use Intervention\Image\Facades\Image;
  29. use Maatwebsite\Excel\Facades\Excel;
  30. use SimpleSoftwareIO\QrCode\Facades\QrCode;
  31. /**
  32. * Class BikeController
  33. * @package App\Http\Controllers\Admin
  34. */
  35. class BikeController extends Controller
  36. {
  37. /**
  38. * index
  39. *
  40. * @param Request $request
  41. * @param BikeFilter $filter
  42. * @return \Illuminate\Http\JsonResponse
  43. * @author Fx
  44. *
  45. */
  46. public function index(Request $request, BikeFilter $filter)
  47. {
  48. //
  49. $admin_id = Admin::user()->id;
  50. $bike = Bike::query()->filter($filter)->orderByDesc('id');
  51. if (!Admin::isAdministrator()) {
  52. $area_ids = AdminUser::getAreaIdsByAdminId($admin_id);
  53. $area_id = AdminUserArea::query()->where('admin_id', $admin_id)->pluck('area_id')->toArray();
  54. if (count($area_ids) !== 0) {
  55. $area_idss = array_merge($area_ids, $area_id);
  56. $bike = $bike->where(function ($q) use ($area_idss) {
  57. $q->whereIn('put_area_id', $area_idss);
  58. });
  59. } else {
  60. $bike = $bike->where(function ($q) use ($area_id) {
  61. $q->whereIn('put_area_id', $area_id);
  62. });
  63. }
  64. }
  65. $bike = $request->get('all') ? $bike->get() : $bike->paginate();
  66. return $this->ok(BikeResource::collection($bike));
  67. }
  68. /**
  69. * maps 渲染地图车
  70. *
  71. * @param Request $request
  72. * @param BikeFilter $filter
  73. * @return \Illuminate\Http\JsonResponse
  74. * @author Fx
  75. *
  76. */
  77. public function maps(Request $request, BikeFilter $filter)
  78. {
  79. //
  80. $admin_id = Admin::user()->id;
  81. $bike = Bike::query()->filter($filter)->orderByDesc('id');
  82. if (!Admin::isAdministrator()) {
  83. $area_ids = AdminUser::getAreaIdsByAdminId($admin_id);
  84. if (count($area_ids) !== 0) {
  85. $bike = $bike->whereIn('put_area_id', $area_ids)->orWhere('put_area_id', 0);
  86. } else {
  87. $area = AdminUserArea::query()->where('admin_id', $admin_id)->first();
  88. $area_id = $area->area_id ?? 0;
  89. $bike = $bike->where('put_area_id', $area_id)->orWhere('put_area_id', 0);
  90. }
  91. }
  92. $bike = $bike->get();
  93. $data = [];
  94. foreach ($bike as $v) {
  95. // 此样式和前端海量点style数据相对应
  96. $style = 1;// 样式1 未使用 正常
  97. if ($v->is_low_battery_power == Bike::BATTERY_POWER_LOW) {
  98. $style = 0; // 样式0 低电量
  99. } elseif ($v->is_riding == Bike::RIDING_YES) {
  100. $style = 2; // 样式2 骑行中
  101. }
  102. if (app()->redis->hexists(BikeStatusInfoSyncHandler::REDIS_RIDE_BIKE_WORKER_ORDERS_TAG, $v->bike_no)) {
  103. $style = 3; // 样式3 运维骑行中
  104. };
  105. $data[] = [
  106. 'lnglat' => $v->last_location ? [json_decode($v->last_location)->lng, json_decode($v->last_location)->lat] : [116.397546, 39.909153],
  107. 'name' => $v->bike_no,
  108. 'id' => $v->id,
  109. 'style' => $style
  110. ];
  111. }
  112. return $this->ok($data);
  113. }
  114. /**
  115. * Show the form for creating a new resource.
  116. *
  117. * @return \Illuminate\Http\Response
  118. */
  119. public function create()
  120. {
  121. //
  122. }
  123. /**
  124. * store 添加车辆
  125. *
  126. * @param BikeRequest $request
  127. * @param Bike $bike
  128. * @return \Illuminate\Http\JsonResponse
  129. * @author Fx
  130. *
  131. */
  132. public function store(BikeRequest $request, Bike $bike)
  133. {
  134. //
  135. $inputs = $request->validated();
  136. $inputs['put_time'] = date('Y-m-d H:i:s', strtotime($inputs['put_time']));
  137. $box_no = $inputs['box_no'];
  138. $box = BoxBinding::query()->where('box_no', $box_no)->first();
  139. if (empty($box)) return $this->error('找不到此设备信息,请联系管理员');
  140. if ($box->is_binding == BoxBinding::BINDING_YES) return $this->error('此设备已经绑定过');
  141. // 蓝牙信息'TBIT_WA205-7HBLE';
  142. $blu_key = config('systemConfig.blu_key');
  143. try {
  144. $blu_ase_key = Aes128Handler::genKey($blu_key, $box_no);
  145. } catch (\Exception $e) {
  146. return $this->error($e->getMessage());
  147. }
  148. $inputs['blu_key'] = $blu_key;
  149. $inputs['blu_ase_key'] = $blu_ase_key;
  150. try {
  151. DB::beginTransaction();
  152. $bike->create($inputs);
  153. $box->is_binding = BoxBinding::BINDING_YES;
  154. $box->save();
  155. DB::commit();
  156. return $this->ok(BikeResource::make($bike));
  157. } catch (\Exception $e) {
  158. DB::rollBack();
  159. Log::error($e->getMessage());
  160. return $this->error('添加失败,请联系管理员');
  161. }
  162. }
  163. /**
  164. * Display the specified resource.
  165. *
  166. * @param int $id
  167. * @return \Illuminate\Http\Response
  168. */
  169. public function show($id)
  170. {
  171. //
  172. }
  173. /**
  174. * Show the form for editing the specified resource.
  175. *
  176. * @param int $id
  177. * @return \Illuminate\Http\Response
  178. */
  179. public function edit(Bike $bike)
  180. {
  181. return $this->ok(BikeResource::make($bike));
  182. //
  183. }
  184. /**
  185. * update 更新车辆
  186. *
  187. * @param BikeRequest $request
  188. * @param Bike $bike
  189. * @return \Illuminate\Http\JsonResponse
  190. * @author Fx
  191. *
  192. */
  193. public function update(BikeRequest $request, Bike $bike)
  194. {
  195. //
  196. $inputs = $request->validated();
  197. $inputs['put_time'] = date('Y-m-d H:i:s', strtotime($inputs['put_time']));
  198. // 更新redis
  199. if ((int)$inputs['put_status'] === 0) {
  200. (new BikeStatusInfoSyncHandler())->toBikeOffLineStatus($bike->bike_no);
  201. } elseif ((int)$inputs['put_status'] === 1) {
  202. $lastLocation = LocationsLog::getNewestLocationByBikeNo($bike->bike_no);
  203. (new BikeStatusInfoSyncHandler())->toBikeOnLineStatus($bike->bike_no, $lastLocation['lng'] ?? 0, $lastLocation['lat'] ?? 0);
  204. }
  205. if ($bike->box_no != $inputs['box_no']) {
  206. $box_no = $inputs['box_no'];
  207. $box = BoxBinding::query()->where('box_no', $box_no)->first();
  208. if (empty($box)) return $this->error('找不到此设备信息,请联系管理员');
  209. if ($box->is_binding == BoxBinding::BINDING_YES) return $this->error('此设备已经绑定过');
  210. // 蓝牙信息'TBIT_WA205-7HBLE';
  211. $blu_key = config('systemConfig.blu_key');
  212. try {
  213. $blu_ase_key = Aes128Handler::genKey($blu_key, $box_no);
  214. } catch (\Exception $e) {
  215. return $this->error($e->getMessage());
  216. }
  217. $inputs['blu_key'] = $blu_key;
  218. $inputs['blu_ase_key'] = $blu_ase_key;
  219. try {
  220. DB::beginTransaction();
  221. $bike->update($inputs);
  222. $box->is_binding = BoxBinding::BINDING_YES;
  223. $box->save();
  224. DB::commit();
  225. return $this->ok(BikeResource::make($bike));
  226. } catch (\Exception $e) {
  227. DB::rollBack();
  228. Log::error($e->getMessage());
  229. return $this->error('添加失败,请联系管理员');
  230. }
  231. }
  232. $bike->update($inputs);
  233. return $this->ok(BikeResource::make($bike));
  234. }
  235. public function updateRemark(RemarkRequest $request, $id)
  236. {
  237. //
  238. $bike = Bike::find($id);
  239. $inputs = $request->validated();
  240. // Log::info($id);
  241. $bike->update($inputs);
  242. return $this->ok(BikeResource::make($bike));
  243. }
  244. /**
  245. * Remove the specified resource from storage.
  246. *
  247. * @param int $id
  248. * @return \Illuminate\Http\Response
  249. */
  250. public function destroy(Bike $bike)
  251. {
  252. //
  253. $bike->delete();
  254. return $this->noContent();
  255. }
  256. /**
  257. * bikesUpdate 批量投放更新
  258. *
  259. * @param Request $request
  260. * @return \Illuminate\Http\JsonResponse
  261. * @author Fx
  262. *
  263. */
  264. public function bikesUpdate(Request $request)
  265. {
  266. $ids = $request->get('ids');
  267. $bike_nos = $request->get('bike_nos');
  268. $updated = $request->get('updated');
  269. //参数校验
  270. if (empty($ids)) return $this->error('请选择车辆');
  271. if (empty($updated['put_time'])) return $this->error('参数错误');
  272. if (empty($updated['put_area_id'])) return $this->error('参数错误');
  273. if ($updated['put_status'] != '0' && $updated['put_status'] != '1') return $this->error('参数错误');
  274. $updates['put_time'] = date('Y-m-d H:i:s', strtotime($updated['put_time']));
  275. $updates['put_area_id'] = $updated['put_area_id'];
  276. $updates['put_status'] = $updated['put_status'];
  277. $bool = Bike::query()->whereIn('id', $ids)->update($updates);
  278. // Log::info($bool);
  279. if ($bool) {
  280. // 更新redis
  281. if (!empty($bike_nos)) {
  282. if ($updated['put_status'] == 0) {
  283. foreach ($bike_nos as $v) {
  284. (new BikeStatusInfoSyncHandler())->toBikeOffLineStatus($v);
  285. }
  286. } elseif ($updated['put_status'] == 1) {
  287. foreach ($bike_nos as $v) {
  288. $lastLocation = LocationsLog::getNewestLocationByBikeNo($v);
  289. (new BikeStatusInfoSyncHandler())->toBikeOnLineStatus($v, $lastLocation['lng'] ?? 0, $lastLocation['lat'] ?? 0);
  290. }
  291. }
  292. }
  293. return $this->ok('操作成功');
  294. } else {
  295. return $this->error('操作失败');
  296. }
  297. }
  298. /**
  299. * import 导入excle
  300. *
  301. * @param Request $request
  302. * @return \Illuminate\Http\JsonResponse
  303. * @author Fx
  304. *
  305. */
  306. public function import(Request $request)
  307. {
  308. try {
  309. Excel::import(new BikesImport, $request->file('bikes'));
  310. return $this->ok('导入成功');
  311. } catch (\Exception $e) {
  312. Log::error($e->getMessage());
  313. return $this->error('导入失败');
  314. }
  315. }
  316. /**
  317. * download 下载模板
  318. *
  319. * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
  320. * @author Fx
  321. *
  322. */
  323. public function download()
  324. {
  325. return response()->download(public_path('example_bike.xlsx'));
  326. }
  327. /**
  328. * QrCode 返回单个车辆二维码
  329. *
  330. * @param Request $request
  331. * @return \Illuminate\Http\JsonResponse
  332. * @author Fx
  333. *
  334. */
  335. public function QrCode(Request $request)
  336. {
  337. $bike_no = $request->get('bike_no') ?? '';
  338. if (empty($bike_no)) return $this->error('参数错误');
  339. $filename = storage_path('app/public/qcode/') . $bike_no . 'appcode.png';
  340. if (!file_exists($filename)) {
  341. $filename = $this->wechatQrCode($bike_no);
  342. $path = config('app.url') . '/storage/qcode/' . $filename;
  343. return $this->ok($path);
  344. }
  345. $path = config('app.url') . '/storage/qcode/' . $bike_no . 'appcode.png';
  346. return $this->ok($path);
  347. }
  348. /**
  349. * qrCodesDownload 批量下载二维码
  350. *
  351. * @param Request $request
  352. * @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
  353. * @author Fx
  354. *
  355. */
  356. public function qrCodesDownload(Request $request)
  357. {
  358. $bike_nos = $request->get('bike_nos') ?? '';
  359. if (empty($bike_nos)) return $this->error('参数错误');
  360. $files = [];
  361. foreach ($bike_nos as $v) {
  362. $file = storage_path('app/public/qcode/') . $v . 'appcode.png';
  363. if (!file_exists($file)) {
  364. $filename = $this->wechatQrCode($v);
  365. $files[$filename] = $file;
  366. } else {
  367. $filename = $v . 'appcode.png';
  368. $files[$filename] = $file;
  369. }
  370. }
  371. $zip_file = 'invoices.zip';
  372. $zip = new \ZipArchive();
  373. $zip->open($zip_file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
  374. $path = storage_path('invoices');
  375. // 这里边全是真实全绝对路径 不需要跳过目录
  376. foreach ($files as $name => $file) {
  377. // 我们要跳过所有子目录
  378. // if (!$file->isDir()) {
  379. // $filePath = $file->getRealPath();
  380. //
  381. // // 用 substr/strlen 获取文件扩展名
  382. // $relativePath = 'invoices/' . substr($filePath, strlen($path) + 1);
  383. //
  384. // $zip->addFile($filePath, $relativePath);
  385. // }
  386. //$file 为真实文件路径 $name为文件名 如果不写$name生成的压缩文件 将会包含目录
  387. $zip->addFile($file, $name);
  388. }
  389. $zip->close();
  390. return response()->download($zip_file);
  391. }
  392. /**
  393. * analysisPosition 解析车辆位置
  394. *
  395. * @param Request $request
  396. * @return \Illuminate\Http\JsonResponse
  397. * @author Fx
  398. *
  399. */
  400. public function analysisPosition(Request $request)
  401. {
  402. $bike_id = $request->get('bike_id');
  403. $bike = Bike::find($bike_id);
  404. if (empty($bike)) return $this->ok(['position' => '']);
  405. $last_location = $bike->last_location;
  406. $lngLat = $last_location ? [json_decode($last_location)->lng, json_decode($last_location)->lat] : [116.397546, 39.909153];
  407. $position = GaodeMaps::getAddress($lngLat);
  408. //最近订单 取最
  409. $order = Order::query()->where('bike_id', $bike->id)->orderByDesc('id')->with('users')->first();
  410. $orderRent = OrderRent::query()->where('bike_id', $bike->id)->orderByDesc('id')->with('users')->first();
  411. $data2 = [];
  412. $data1 = [];
  413. if (!empty($order) && !empty($orderRent)) {
  414. if ($orderRent->start_use_bike_time > $order->start_use_bike_time) {
  415. // $user = User::query()->find($v->user_id);
  416. $data1['bike_no'] = $orderRent->bike_no;
  417. $data1['username'] = $orderRent->users->nickname ?? '';
  418. $data1['truename'] = $orderRent->users->truename ?? '';
  419. $data1['mobile'] = $orderRent->users->mobile ?? '';
  420. $data1['order_status'] = OrderRent::$statusMaps[$orderRent->status];
  421. $data1['start_use_bike_time'] = $orderRent->start_use_bike_time;
  422. $data1['end_use_bike_time'] = $orderRent->end_use_bike_time;
  423. $data1['start_use_bike_time_app'] = date('m-d H:i', strtotime($orderRent->start_use_bike_time));
  424. $data1['end_use_bike_time_app'] = $orderRent->end_use_bike_time ? date('m-d H:i', strtotime($orderRent->end_use_bike_time)) : '';
  425. } else {
  426. $data1['bike_no'] = $order->bike_no;
  427. $data1['username'] = $order->users->nickname ?? '';
  428. $data1['truename'] = $order->users->truename ?? '';
  429. $data1['mobile'] = $order->users->mobile ?? '';
  430. $data1['order_status'] = Order::$statusMaps[$order->status];
  431. $data1['start_use_bike_time'] = $order->start_use_bike_time;
  432. $data1['end_use_bike_time'] = $order->end_use_bike_time;
  433. $data1['start_use_bike_time_app'] = date('m-d H:i', strtotime($order->start_use_bike_time));
  434. $data1['end_use_bike_time_app'] = $order->end_use_bike_time ? date('m-d H:i', strtotime($order->end_use_bike_time)) : '';
  435. }
  436. $data2[] = $data1;
  437. } else if (!empty($order)) {
  438. $data1['bike_no'] = $order->bike_no;
  439. $data1['username'] = $order->users->nickname ?? '';
  440. $data1['truename'] = $order->users->truename ?? '';
  441. $data1['mobile'] = $order->users->mobile ?? '';
  442. $data1['order_status'] = Order::$statusMaps[$order->status];
  443. $data1['start_use_bike_time'] = $order->start_use_bike_time;
  444. $data1['end_use_bike_time'] = $order->end_use_bike_time;
  445. $data1['start_use_bike_time_app'] = date('m-d H:i', strtotime($order->start_use_bike_time));
  446. $data1['end_use_bike_time_app'] = $order->end_use_bike_time ? date('m-d H:i', strtotime($order->end_use_bike_time)) : '';
  447. $data2[] = $data1;
  448. } else if (!empty($orderRent)) {
  449. $data1['bike_no'] = $orderRent->bike_no;
  450. $data1['username'] = $orderRent->users->nickname ?? '';
  451. $data1['truename'] = $orderRent->users->truename ?? '';
  452. $data1['mobile'] = $orderRent->users->mobile ?? '';
  453. $data1['order_status'] = OrderRent::$statusMaps[$orderRent->status];
  454. $data1['start_use_bike_time'] = $orderRent->start_use_bike_time;
  455. $data1['end_use_bike_time'] = $orderRent->end_use_bike_time;
  456. $data1['start_use_bike_time_app'] = date('m-d H:i', strtotime($orderRent->start_use_bike_time));
  457. $data1['end_use_bike_time_app'] = $orderRent->end_use_bike_time ? date('m-d H:i', strtotime($orderRent->end_use_bike_time)) : '';
  458. $data2[] = $data1;
  459. }
  460. $workInfo = [];
  461. if (app()->redis->hexists(BikeStatusInfoSyncHandler::REDIS_RIDE_BIKE_WORKER_ORDERS_TAG, $bike->bike_no)) {
  462. $work_id = app()->redis->hget(BikeStatusInfoSyncHandler::REDIS_RIDE_BIKE_WORKER_ORDERS_TAG, $bike->bike_no);
  463. $work = AdminUser::find($work_id);
  464. $workInfo = [
  465. 'name' => $work->name,
  466. 'phone' => $work->phone,
  467. ];
  468. }
  469. return $this->ok(['position' => $position, 'bike' => BikeResource::make($bike), 'lately_order' => $data1, 'workInfo' => $workInfo]);
  470. }
  471. /**
  472. * wechatQrCode 本地插件生成二维码
  473. *
  474. * @param $bike_no
  475. * @return string
  476. * @author Fx
  477. *
  478. */
  479. private function wechatQrCode($bike_no)
  480. {
  481. $filename = $bike_no . 'appcode.png';
  482. QrCode::format('png')->size(600)->generate(config('wechat.code_url') . $bike_no, storage_path('app/public/qcode/') . $filename);
  483. $file = storage_path('app/public/qcode/') . $filename;
  484. // 修改指定图片的大小
  485. $img = Image::canvas(700, 700, '#fff');
  486. $img->insert($file, 'top-left', 50, 20);
  487. $fontPath = public_path('fonts/msyhbd.ttc');
  488. $img->text($bike_no, 350, 610, function ($font) use ($fontPath) {
  489. $font->file($fontPath);
  490. $font->size(60);
  491. $font->color('#000');
  492. $font->align('center');
  493. $font->valign('top');
  494. });
  495. // 将处理后的图片重新保存到其他路径
  496. $img->save($file);
  497. return $filename;
  498. }
  499. /**
  500. * statistics 简要统计信息
  501. *
  502. * @param BikeFilter $filter
  503. * @return \Illuminate\Http\JsonResponse
  504. * @author Fx
  505. *
  506. */
  507. public function statistics(BikeFilter $filter)
  508. {
  509. $riding_yes_num = Bike::query()->filter($filter)->where('is_riding', Bike::RIDING_YES); //使用中的数量
  510. $riding_no_num = Bike::query()->filter($filter)->where('is_riding', Bike::RIDING_NO); //未使用的数量
  511. $low_power_num = Bike::query()->filter($filter)->where('is_low_battery_power', Bike::BATTERY_POWER_LOW); //低电量的数量
  512. $admin_id = Admin::user()->id;
  513. if (!Admin::isAdministrator()) {
  514. $area_ids = AdminUser::getAreaIdsByAdminId($admin_id);
  515. if (count($area_ids) !== 0) {
  516. $riding_yes_num = $riding_yes_num->whereIn('put_area_id', $area_ids);
  517. $riding_no_num = $riding_no_num->whereIn('put_area_id', $area_ids);
  518. $low_power_num = $low_power_num->whereIn('put_area_id', $area_ids);
  519. } else {
  520. $area = AdminUserArea::query()->where('admin_id', $admin_id)->first();
  521. $area_id = $area->area_id ?? 0;
  522. $riding_yes_num = $riding_yes_num->where('put_area_id', $area_id);
  523. $riding_no_num = $riding_no_num->where('put_area_id', $area_id);
  524. $low_power_num = $low_power_num->where('put_area_id', $area_id);
  525. }
  526. }
  527. $riding_yes_num = $riding_yes_num->count('id');
  528. $riding_no_num = $riding_no_num->count('id');
  529. $low_power_num = $low_power_num->count('id');
  530. $worker_riding_num = app()->redis->hkeys(BikeStatusInfoSyncHandler::REDIS_RIDE_BIKE_WORKER_ORDERS_TAG);
  531. $data = [
  532. 'riding_yes_num' => $riding_yes_num,
  533. 'riding_no_num' => $riding_no_num,
  534. 'low_power_num' => $low_power_num,
  535. 'worker_riding_num' => Bike::query()->filter($filter)->whereIn('bike_no', $worker_riding_num)->count('id')
  536. ];
  537. return $this->ok($data);
  538. }
  539. /**
  540. * getTrajectory 车辆轨迹 普通订单
  541. *
  542. * @param Request $request
  543. * @return \Illuminate\Http\JsonResponse
  544. * @author Fx
  545. *
  546. */
  547. public function getTrajectory(Request $request)
  548. {
  549. $bike_no = $request->get('bike_no') ?? '';
  550. $time_between = $request->get('time_between') ?? [];
  551. if (empty($bike_no) || empty($time_between)) return $this->error('参数错误');
  552. $locationLog = LocationsLog::where('bike_no', $bike_no)
  553. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  554. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  555. ->where('is_rent', LocationsLog::RENT_NO)
  556. ->whereBetween('latitude', [3, 53])->whereBetween('longitude', [73, 136])->orderBy('created_at', 'asc')
  557. ->get(['longitude', 'latitude', 'created_at']);
  558. $order = Order::where('bike_no', $bike_no)
  559. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  560. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  561. ->get(['end_use_bike_location', 'start_use_bike_location', 'created_at']);
  562. $orders = Order::where('bike_no', $bike_no)
  563. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  564. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  565. ->get();
  566. $data = [];
  567. $dataTime = [];
  568. $marks = [];
  569. if (empty($locationLog)) return $this->ok($data);
  570. if (empty($order)) return $this->ok($data);
  571. foreach ($order as $v) {
  572. $marks[] = [json_decode($v->start_use_bike_location)->longitude, json_decode($v->start_use_bike_location)->latitude];
  573. $marks[] = [json_decode($v->end_use_bike_location)->longitude, json_decode($v->end_use_bike_location)->latitude];
  574. }
  575. foreach ($locationLog as $v) {
  576. $data[] = [$v->longitude, $v->latitude];
  577. $dataTime[] = date('Y-m-d H:i:s', strtotime($v->created_at));
  578. }
  579. if (empty($data)) return $this->ok($data);
  580. $res = [
  581. 'locations' => $data,
  582. 'locations_time' => $dataTime,
  583. 'marks' => $marks,
  584. 'start_location' => $data[0],
  585. 'end_location' => end($data),
  586. 'orders' => OrderResource::collection($orders)
  587. ];
  588. return $this->ok($res);
  589. }
  590. /**
  591. * getOrderRentTrajectory 车辆轨迹 日租订单
  592. *
  593. * @param Request $request
  594. * @return \Illuminate\Http\JsonResponse
  595. * @author Fx
  596. *
  597. */
  598. public function getOrderRentTrajectory(Request $request)
  599. {
  600. $bike_no = $request->get('bike_no') ?? '';
  601. $time_between = $request->get('time_between') ?? [];
  602. if (empty($bike_no) || empty($time_between)) return $this->error('参数错误');
  603. $locationLog = LocationsLog::where('bike_no', $bike_no)
  604. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  605. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  606. ->where('is_rent', LocationsLog::RENT_YES)
  607. ->whereBetween('latitude', [3, 53])->whereBetween('longitude', [73, 136])->orderBy('created_at', 'asc')
  608. ->get(['longitude', 'latitude', 'created_at']);
  609. $order = OrderRent::where('bike_no', $bike_no)
  610. ->where('status', '!=', OrderRent::STATUS_WAIT_PAY_RENT_MONEY)
  611. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  612. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  613. ->get(['end_use_bike_location', 'start_use_bike_location', 'created_at']);
  614. $orders = OrderRent::where('bike_no', $bike_no)
  615. ->where('status', '!=', OrderRent::STATUS_WAIT_PAY_RENT_MONEY)
  616. ->where('created_at', '>=', Carbon::parse($time_between[0])->toDateTimeString())
  617. ->where('created_at', '<=', Carbon::parse($time_between[1])->toDateTimeString())
  618. ->get();
  619. $data = [];
  620. $dataTime = [];
  621. $marks = [];
  622. if (empty($locationLog)) return $this->ok($data);
  623. if (empty($order)) return $this->ok($data);
  624. foreach ($order as $v) {
  625. $marks[] = [json_decode($v->start_use_bike_location)->longitude, json_decode($v->start_use_bike_location)->latitude];
  626. $marks[] = [json_decode($v->end_use_bike_location)->longitude, json_decode($v->end_use_bike_location)->latitude];
  627. }
  628. foreach ($locationLog as $v) {
  629. $data[] = [$v->longitude, $v->latitude];
  630. $dataTime[] = date('Y-m-d H:i:s', strtotime($v->created_at));
  631. }
  632. if (empty($data)) return $this->ok($data);
  633. $res = [
  634. 'locations' => $data,
  635. 'locations_time' => $dataTime,
  636. 'marks' => $marks,
  637. 'start_location' => $data[0],
  638. 'end_location' => end($data),
  639. 'orders' => OrderRentResource::collection($orders)
  640. ];
  641. return $this->ok($res);
  642. }
  643. /**
  644. * mulBikesContro 车辆批量操作
  645. *
  646. * @param Request $request
  647. * @return \Illuminate\Http\JsonResponse
  648. * @author Fx
  649. *
  650. */
  651. public function mulBikesContro(Request $request)
  652. {
  653. $admin_id = Admin::user()->id;
  654. $bikeIds = $request->get('bikeIds') ?? [];
  655. $type = $request->get('type') ?? '';
  656. if (empty($bikeIds) || empty($type)) return $this->error('参数错误');
  657. try {
  658. $bikes = Bike::query()->whereIn('id', $bikeIds)->get(['id', 'box_no', 'bike_no', 'last_location', 'put_status']);
  659. if (count($bikes) == 0) return $this->error('找不到车辆相关信息,请检查参数');
  660. switch ($type) {
  661. case 'bell': //寻铃
  662. foreach ($bikes as $v) {
  663. if (!empty($v->box_no)) {
  664. BikeControl::bellBike($v->box_no);
  665. }
  666. }
  667. break;
  668. case 'openLock': // 开锁
  669. foreach ($bikes as $v) {
  670. if (!empty($v->box_no)) {
  671. BikeControl::openLock($v->box_no);
  672. (new BikeStatusInfoSyncHandler())->toBikeRideStatus(BikeStatusInfoSyncHandler::ROLE_SERVER, $v->bike_no, ['id' => $admin_id, 'area_id' => $v->put_area_id, 'bike_id' => $v->id]);
  673. }
  674. }
  675. break;
  676. case 'closeLock': // 关电车锁
  677. foreach ($bikes as $v) {
  678. if (!empty($v->box_no)) {
  679. BikeControl::openLock($v->box_no);
  680. $location = json_decode($v->last_location);
  681. // 此处取mysql得位置信息
  682. (new BikeStatusInfoSyncHandler())->toBikeWaitRideStatus($v->bike_no, $location->lng, $location->lat, $v->put_status);
  683. }
  684. }
  685. break;
  686. case 'openBatteryLock': //开电池锁
  687. foreach ($bikes as $v) {
  688. if (!empty($v->box_no)) {
  689. BikeControl::openBatteryLock($v->box_no);
  690. }
  691. }
  692. break;
  693. case 'rebootBox': //重启中控
  694. foreach ($bikes as $v) {
  695. if (!empty($v->box_no)) {
  696. BikeControl::rebootBox($v->box_no);
  697. }
  698. }
  699. break;
  700. case 'nowBikeLocation': //立即定位
  701. foreach ($bikes as $v) {
  702. if (!empty($v->box_no)) {
  703. BikeControl::nowBikeLocation($v->box_no);
  704. }
  705. }
  706. break;
  707. case 'nowBikeBatteryMSG': //立即上传电池信息
  708. foreach ($bikes as $v) {
  709. if (!empty($v->box_no)) {
  710. BikeControl::nowBikeBatteryMSG($v->box_no);
  711. }
  712. }
  713. break;
  714. case 'outLoseElectric': //失能
  715. foreach ($bikes as $v) {
  716. if (!empty($v->box_no)) {
  717. BikeControl::outAreaLoseElectric($v->box_no);
  718. }
  719. }
  720. break;
  721. case 'outGetElectric': //获能
  722. foreach ($bikes as $v) {
  723. if (!empty($v->box_no)) {
  724. BikeControl::outAreaGetElectric($v->box_no);
  725. }
  726. }
  727. break;
  728. case 'repair': //恢复正常
  729. foreach ($bikes as $v) {
  730. if (!empty($v->id)) {
  731. $update = [
  732. 'is_low_battery_power' => Bike::BATTERY_POWER_OK, // 电量正常
  733. 'is_link' => Bike::LINK_OFFLINE, // 离线
  734. 'is_in_parking' => Bike::IN_PARKING_YES, // 在停车区
  735. 'is_trouble' => Bike::TROUBLE_NO, // 无故障
  736. 'battery_power' => 0, // 电量设为0
  737. ];
  738. Bike::where('id', $v->id)->update($update);
  739. }
  740. }
  741. break;
  742. default :
  743. return $this->error('参数错误');
  744. }
  745. return $this->ok('操作成功');
  746. } catch (\Exception $e) {
  747. Log::error($e->getMessage());
  748. return $this->error('出现错误,请联系管理员');
  749. }
  750. }
  751. /**
  752. * bikeStatusValueLabel 返回车辆状态码
  753. *
  754. * @return \Illuminate\Http\JsonResponse
  755. * @author Fx
  756. *
  757. */
  758. public function bikeStatusValueLabel()
  759. {
  760. $bikeStateMaps = Bike::$bikeStatesMaps;
  761. $bikeStates = [];
  762. foreach ($bikeStateMaps as $k => $v) {
  763. $bikeStates[] = ['id' => $k, 'name' => $v];
  764. }
  765. return $this->ok($bikeStates);
  766. }
  767. }