WorkOrderController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. <?php
  2. namespace App\Http\Controllers\App;
  3. use App\Filters\WorkOrderFilter;
  4. use App\Http\Resources\App\WorkOrderResource;
  5. use App\Http\Resources\WarningLogResource;
  6. use App\Jobs\VerifyChargeWorkOrderJob;
  7. use App\Jobs\VerifyWatchWorkOrderJob;
  8. use App\Models\AdminMerchant;
  9. use App\Models\AdminUser;
  10. use App\Models\Bike;
  11. use App\Models\LocationsLog;
  12. use App\Models\WarningLog;
  13. use App\Models\WorkOrder;
  14. use App\Utils\Admin;
  15. use App\Utils\GaodeMaps;
  16. use App\Utils\RedisKeys;
  17. use Carbon\Carbon;
  18. use Illuminate\Http\Request;
  19. use App\Http\Controllers\Controller;
  20. use Illuminate\Support\Facades\DB;
  21. use Illuminate\Support\Facades\Log;
  22. use Illuminate\Support\Facades\Redis;
  23. class WorkOrderController extends AppBaseController
  24. {
  25. /**
  26. * workOrderList 工单列表
  27. *
  28. * @param WorkOrderFilter $workOrderFilter
  29. * @return \Illuminate\Http\JsonResponse
  30. * @author Fx
  31. *
  32. */
  33. public function workOrderList(WorkOrderFilter $workOrderFilter)
  34. {
  35. $area_ids = self::$areaIds;
  36. $workOrderList = WorkOrder::query()
  37. ->where(AdminMerchant::getMerchantWhere())
  38. ->whereIn('area_id', $area_ids)
  39. ->filter($workOrderFilter)
  40. ->orderBy('planned')
  41. ->orderByDesc('id')
  42. ->paginate();
  43. return $this->ok(WorkOrderResource::collection($workOrderList));
  44. }
  45. /**
  46. * myWorkOrder 我的工单列表
  47. *
  48. * @param WorkOrderFilter $workOrderFilter
  49. * @return \Illuminate\Http\JsonResponse
  50. * @author Fx
  51. *
  52. */
  53. public function myWorkOrder(WorkOrderFilter $workOrderFilter)
  54. {
  55. $admin_id = Admin::user()->id;
  56. $area_ids = self::$areaIds;
  57. $workOrderList = WorkOrder::query()
  58. ->where(AdminMerchant::getMerchantWhere())
  59. ->whereIn('area_id', $area_ids)
  60. ->filter($workOrderFilter)
  61. ->where('worker_id', $admin_id)
  62. ->orderByDesc('id')
  63. ->paginate();
  64. return $this->ok(WorkOrderResource::collection($workOrderList));
  65. }
  66. /**
  67. * workOrderDetail 工单详情
  68. *
  69. * @param Request $request
  70. * @return \Illuminate\Http\JsonResponse
  71. * @author Fx
  72. *
  73. */
  74. public function workOrderDetail(Request $request)
  75. {
  76. $work_order_id = $request->get('work_order_id') ?? '';
  77. if (empty($work_order_id)) return $this->error('参数错误');
  78. $worker_order = WorkOrder::query()->where(AdminMerchant::getMerchantWhere())->where('id',$work_order_id)->first();
  79. if (empty($worker_order)) return $this->error('找不到该工单信息,请联系管理员');
  80. // 创建者
  81. $created_at = $worker_order->created_at;
  82. $created_man = WorkOrder::$sourceMaps[$worker_order->source];
  83. // 进度
  84. $planneds = WorkOrder::$plannedMaps[$worker_order->planned];
  85. $planned = $worker_order->planned;
  86. // 接单着
  87. $received_name = $worker_order->worker->name ?? '';
  88. $received_at = $worker_order->fix_start_time ?? '';
  89. $position = LocationsLog::getNewestLocationByBikeNo($worker_order->bike_no);
  90. $reason = $worker_order->reason ?? '';
  91. $type = $worker_order->type;
  92. // if($type === WorkOrder::TYPE_TROUBLE){
  93. $str = '';
  94. if (is_serialized($reason)) {
  95. $reasonArr = unserialize($reason);
  96. foreach ($reasonArr as $k => $v) {
  97. if ($v == 1) {
  98. $str .= WorkOrder::$warningMaps[$k] . "、";
  99. }
  100. }
  101. $str = rtrim($str, '、');
  102. $reason = $str;
  103. }
  104. // }
  105. $data = [
  106. 'reason' => empty($reason) ? $worker_order->type_name : $reason,
  107. 'planned' => $planned,
  108. 'planneds' => $planneds,
  109. 'bike_no' => $worker_order->bike_no,
  110. 'location' => GaodeMaps::getAddress([$position['lng'], $position['lat']]),
  111. 'created_at' => Carbon::parse($created_at)->format('Y-m-d H:i:s'),
  112. 'times' => $worker_order->times,
  113. 'created_man' => $created_man
  114. ];
  115. switch ($planned) {
  116. case WorkOrder::PLANNED_STATUS_MEET:
  117. $data += [
  118. 'received_name' => '',
  119. 'received_at' => '',
  120. 'work_over_time' => '',
  121. 'work_over_name' => '',
  122. ];
  123. break;
  124. case WorkOrder::PLANNED_STATUS_WORK:
  125. $data += [
  126. 'received_name' => $received_name,
  127. 'received_at' => Carbon::parse($received_at)->format('Y-m-d H:i:s'),
  128. 'work_over_time' => '',
  129. 'work_over_name' => '',
  130. ];
  131. break;
  132. case WorkOrder::PLANNED_STATUS_WORKED:
  133. $data += [
  134. 'received_name' => $received_name,
  135. 'received_at' => Carbon::parse($received_at)->format('Y-m-d H:i:s'),
  136. 'work_over_time' => Carbon::parse($worker_order->fix_end_time)->format('Y-m-d H:i:s'),
  137. 'work_over_name' => $worker_order->workerOver->name ?? '系统',
  138. ];
  139. break;
  140. case WorkOrder::PLANNED_STATUS_OVER:
  141. $data += [
  142. 'received_name' => $received_name,
  143. 'received_at' => Carbon::parse($received_at)->format('Y-m-d H:i:s'),
  144. 'work_over_time' => Carbon::parse($worker_order->fix_end_time)->format('Y-m-d H:i:s'),
  145. 'work_over_name' => $worker_order->workerOver->name ?? '系统',
  146. ];
  147. break;
  148. default :
  149. $data += [
  150. 'received_name' => '',
  151. 'received_at' => '',
  152. 'work_over_time' => '',
  153. 'work_over_name' => '',
  154. ];
  155. }
  156. return $this->ok($data);
  157. }
  158. /**
  159. * workOrderStatistics 工单统计
  160. *
  161. * @param WorkOrderFilter $filter
  162. * @return \Illuminate\Http\JsonResponse
  163. * @author Fx
  164. *
  165. */
  166. public function workOrderStatistics(WorkOrderFilter $filter)
  167. {
  168. $area_ids = self::$areaIds;
  169. $type_num = WorkOrder::query()
  170. ->where(AdminMerchant::getMerchantWhere())
  171. ->whereIn('area_id', $area_ids)
  172. ->where('status', WorkOrder::STATUS_NO)
  173. ->filter($filter)
  174. ->groupBy(['type'])
  175. ->select('type', DB::raw('COUNT(id) as type_num'))
  176. ->get()
  177. ->toArray();
  178. $data = [
  179. 'charge_num' => 0,
  180. 'watch_num' => 0,
  181. 'power_failure_num' => 0,
  182. 'alert_num' => 0,
  183. 'steal_num' => 0,
  184. 'headman_num' => 0,
  185. 'trouble_num' => 0,
  186. 'help_num' => 0,
  187. 'other_num' => 0,
  188. 'offline_num' => 0,
  189. 'planned_meet_num' => 0, // 未认领
  190. 'planned_work_num' => 0, // 处理中
  191. 'planned_worked_num' => 0, // 已处理
  192. 'planned_over_num' => 0,// 完结
  193. ];
  194. foreach ($type_num as $v) {
  195. switch ($v['type']) {
  196. case WorkOrder::TYPE_CHARGE:
  197. $data['charge_num'] = $v['type_num'];
  198. break;
  199. case WorkOrder::TYPE_WATCH:
  200. $data['watch_num'] = $v['type_num'];
  201. break;
  202. case WorkOrder::TYPE_POWER_FAILURE:
  203. $data['power_failure_num'] = $v['type_num'];
  204. break;
  205. case WorkOrder::TYPE_ALERT:
  206. $data['alert_num'] = $v['type_num'];
  207. break;
  208. case WorkOrder::TYPE_STEAL:
  209. $data['steal_num'] = $v['type_num'];
  210. break;
  211. case WorkOrder::TYPE_HEADMAN:
  212. $data['headman_num'] = $v['type_num'];
  213. break;
  214. case WorkOrder::TYPE_TROUBLE:
  215. $data['trouble_num'] = $v['type_num'];
  216. break;
  217. case WorkOrder::TYPE_HELP:
  218. $data['help_num'] = $v['type_num'];
  219. break;
  220. case WorkOrder::TYPE_OFFLINE:
  221. $data['offline_num'] = $v['type_num'];
  222. break;
  223. case WorkOrder::TYPE_OTHER:
  224. $data['other_num'] = $v['type_num'];
  225. break;
  226. default;
  227. }
  228. }
  229. $planned_num = WorkOrder::query()
  230. ->where(AdminMerchant::getMerchantWhere())
  231. ->filter($filter)
  232. ->groupBy('planned')
  233. ->select('planned', DB::raw('COUNT(id) as planned_num'))
  234. ->get()
  235. ->toArray();
  236. foreach ($planned_num as $v) {
  237. switch ($v['planned']) {
  238. case WorkOrder::PLANNED_STATUS_MEET:
  239. $data['planned_meet_num'] = $v['planned_num'];
  240. break;
  241. case WorkOrder::PLANNED_STATUS_WORK:
  242. $data['planned_work_num'] = $v['planned_num'];
  243. break;
  244. case WorkOrder::PLANNED_STATUS_WORKED:
  245. $data['planned_worked_num'] = $v['planned_num'];
  246. break;
  247. case WorkOrder::PLANNED_STATUS_OVER:
  248. $data['planned_over_num'] = $v['planned_num'];
  249. break;
  250. default;
  251. }
  252. }
  253. return $this->ok($data);
  254. }
  255. /**
  256. * workOrderType 筛选项工单类型
  257. *
  258. * @return \Illuminate\Http\JsonResponse
  259. * @author Fx
  260. *
  261. */
  262. public function workOrderType()
  263. {
  264. $types = WorkOrder::$typeMaps;
  265. $data = [];
  266. foreach ($types as $k => $v) {
  267. $dataItem['key'] = $k;
  268. $dataItem['value'] = $v;
  269. $data[] = $dataItem;
  270. }
  271. return $this->ok($types);
  272. }
  273. /**
  274. * receiveWorkOrder 接单
  275. *
  276. * @param Request $request
  277. * @return \Illuminate\Http\JsonResponse
  278. * @author Fx
  279. *
  280. */
  281. public function receiveWorkOrder(Request $request)
  282. {
  283. $work_order_id = $request->get('work_order_id') ?? '';
  284. if (empty($work_order_id)) return $this->error('参数错误');
  285. $worker_order = WorkOrder::query()->where('id',$work_order_id)->where(AdminMerchant::getMerchantWhere())->first();
  286. if (empty($worker_order)) return $this->error('找不到该工单信息,请联系管理员');
  287. // 加锁防止重复
  288. $lock = $this->makeReceiveWorkLock($work_order_id);
  289. if (!$lock) return $this->error('该工单已被认领');
  290. if ($worker_order->planned > WorkOrder::PLANNED_STATUS_MEET) return $this->error('该工单已被认领');
  291. $worker_id = Admin::user()->id;
  292. $worker_order->planned = WorkOrder::PLANNED_STATUS_WORK;
  293. $worker_order->fix_start_time = Carbon::now();
  294. $worker_order->worker_id = $worker_id;
  295. $bool = $worker_order->save();
  296. if ($bool) {
  297. return $this->ok('接单成功');
  298. } else {
  299. return $this->error('出现错误,请联系管理员');
  300. }
  301. }
  302. /**
  303. * makeReceiveWorkLock 接单锁 防止重复接单
  304. *
  305. * @param $work_order_id
  306. * @return bool
  307. * @author Fx
  308. *
  309. */
  310. public function makeReceiveWorkLock($work_order_id)
  311. {
  312. $key = sprintf(RedisKeys::LOCK_RECEIVE_WORK, $work_order_id);
  313. $res = app()->redis->incr($key);
  314. app()->redis->Expire($key, RedisKeys::LOCK_EXPIRE_RECEIVE_WORK);
  315. return $res > 1 ? false : true;
  316. }
  317. /**
  318. * updateWorkOrderStatus 更新工单状态已处理
  319. *
  320. * @param Request $request
  321. * @return \Illuminate\Http\JsonResponse
  322. * @author Fx
  323. *
  324. */
  325. public function updateWorkOrderStatus(Request $request)
  326. {
  327. $work_order_id = $request->get('work_order_id') ?? '';
  328. if (empty($work_order_id)) return $this->error('参数错误');
  329. //验证工单存在
  330. $work_order = WorkOrder::query()->where(AdminMerchant::getMerchantWhere())->find($work_order_id);
  331. if (empty($work_order)) return $this->error('找不到该工单信息,请联系管理员');
  332. $work_order_type = $work_order->type;
  333. $worker_id = $work_order->worker_id;
  334. // 验证是否有权限操作此工单
  335. $worker_type = Admin::user()->type;
  336. $admin_id = Admin::user()->id;
  337. if ($worker_type != AdminUser::TYPE_ADMIN) {
  338. if ($admin_id != $worker_id) {
  339. return $this->error('无权限操作此工单');
  340. }
  341. }
  342. if ($work_order->planned == WorkOrder::PLANNED_STATUS_MEET) return $this->error('该工单暂未认领');
  343. if ($work_order->planned == WorkOrder::PLANNED_STATUS_WORKED) return $this->error('该工单为已处理状态不可重复提交');
  344. if ($work_order->planned == WorkOrder::PLANNED_STATUS_OVER) return $this->error('该工单为已完成状态不可重复提交');
  345. // 判断工单类型 进行验证是否真正完成
  346. switch ($work_order_type) {
  347. case WorkOrder::TYPE_WATCH: // 查看类型工单
  348. $res = $this->updateWorkOrder($work_order);
  349. if ($res) {
  350. // 延时队列 验证是否完成工单
  351. VerifyWatchWorkOrderJob::dispatch($work_order->work_no)->delay(Carbon::now()->addMinutes(config('queue.delay.verifyWatchWorkOrder')));
  352. return $this->ok('操作成功');
  353. } else {
  354. return $this->error('更新失败,请重新尝试');
  355. }
  356. break;
  357. case WorkOrder::TYPE_CHARGE: // 充电类型工单
  358. $res = $this->updateWorkOrder($work_order);
  359. if ($res) {
  360. // 延时队列 验证是否完成工单
  361. VerifyChargeWorkOrderJob::dispatch($work_order->work_no)->delay(Carbon::now()->addMinutes(config('queue.delay.verifyChargeWorkOrder')));
  362. return $this->ok('操作成功');
  363. } else {
  364. return $this->error('更新失败,请重新尝试');
  365. }
  366. break;
  367. case WorkOrder::TYPE_TROUBLE: // 故障工单
  368. // 需要修改车辆故障表 以及更新车辆状态
  369. $bike_id = $work_order->bike_id;
  370. $bike = Bike::find($bike_id);
  371. if(!empty($bike)){
  372. $bike->is_trouble = Bike::TROUBLE_NO;
  373. $bike->save();
  374. }else{
  375. return $this->error('找不到车辆相关信息');
  376. }
  377. $res = $this->updateWorkOrderOver($work_order);
  378. if ($res) {
  379. return $this->ok('操作成功');
  380. } else {
  381. return $this->error('更新失败,请重新尝试');
  382. }
  383. break;
  384. default: // 其他工单了类型暂时不验证 直接赋值成功
  385. $res = $this->updateWorkOrderOver($work_order);
  386. if ($res) {
  387. return $this->ok('操作成功');
  388. } else {
  389. return $this->error('更新失败,请重新尝试');
  390. }
  391. }
  392. }
  393. /**
  394. * updateWorkOrder 更新工单状态为已处理
  395. *
  396. * @param $work_order
  397. * @return mixed
  398. * @author Fx
  399. *
  400. */
  401. private function updateWorkOrder($work_order)
  402. {
  403. $work_order->planned = WorkOrder::PLANNED_STATUS_WORKED;
  404. $work_order->work_over_id = Admin::user()->id;
  405. $bool = $work_order->save();
  406. return $bool;
  407. }
  408. /**
  409. * updateWorkOrderOver 更新工单状态为已完成
  410. *
  411. * @param $work_order
  412. * @return mixed
  413. * @author Fx
  414. *
  415. */
  416. private function updateWorkOrderOver($work_order)
  417. {
  418. $work_order->planned = WorkOrder::PLANNED_STATUS_OVER;
  419. $work_order->status = WorkOrder::STATUS_OK;
  420. $work_order->work_over_id = Admin::user()->id;
  421. $work_order->fix_end_time = Carbon::now();
  422. $bool = $work_order->save();
  423. $bike_id = $work_order->bike_id;
  424. $bike = Bike::find($bike_id);
  425. if(!empty($bike)){
  426. $bike->is_trouble = Bike::TROUBLE_NO;
  427. $bike->save();
  428. }
  429. return $bool;
  430. }
  431. /**
  432. * upgradeGroupWorkOrder 升级为组长工单
  433. *
  434. * @param Request $request
  435. * @return \Illuminate\Http\JsonResponse
  436. * @author Fx
  437. *
  438. */
  439. public function upgradeGroupWorkOrder(Request $request)
  440. {
  441. $work_order_id = $request->get('work_order_id') ?? '';
  442. $remark = $request->get('remark') ?? '';
  443. if (empty($work_order_id) || empty($remark)) return $this->error('参数错误');
  444. //验证工单存在
  445. $work_order = WorkOrder::query()->where(AdminMerchant::getMerchantWhere())->find($work_order_id);
  446. if (empty($work_order)) return $this->error('找不到该工单信息,请联系管理员');
  447. $admin_id = Admin::user()->id;
  448. $worker_id = $work_order->worker_id;
  449. if ($work_order->planned == WorkOrder::PLANNED_STATUS_MEET) return $this->error('该工单暂未认领不能升级');
  450. if ($admin_id != $worker_id) {
  451. return $this->error('无权限操作此工单');
  452. }
  453. try {
  454. $work_order->type = WorkOrder::TYPE_HEADMAN;
  455. $work_order->type_name = WorkOrder::$typeMaps[WorkOrder::TYPE_HEADMAN];
  456. $work_order->remark = $remark; // 备注信息
  457. $work_order->save();
  458. return $this->ok('更改成功');
  459. } catch (\Exception $exception) {
  460. Log::error($exception->getMessage());
  461. return $this->error('更改失败,请联系管理员');
  462. }
  463. }
  464. /**
  465. * getWarningByWorkOrderId 根据工单id获取警告日志
  466. *
  467. * @param Request $request
  468. * @return \Illuminate\Http\JsonResponse
  469. * @author Fx
  470. *
  471. */
  472. public function getWarningByWorkOrderId(Request $request)
  473. {
  474. $workOrderId = $request->get('id');
  475. $is_limit_type = $request->get('is_limit_type', 1);
  476. $workOrder = WorkOrder::query()->where(AdminMerchant::getMerchantWhere())->find($workOrderId);
  477. if (empty($workOrder)) return $this->error('找不到工单');
  478. $warning = WarningLog::query()
  479. ->where('bike_no', $workOrder->bike_no);
  480. if ((bool)$is_limit_type) {
  481. // Log::info($is_limit_type);
  482. $warning = $warning
  483. ->where('type', $workOrder->warning_type);
  484. }
  485. $warning = $warning
  486. ->where('created_at', '>', Carbon::parse($workOrder->created_at)->subMinutes(5))
  487. ->orderByDesc('id')
  488. ->limit(300)
  489. ->get();
  490. return $this->ok(WarningLogResource::collection($warning));
  491. }
  492. }