OrderController.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller\Api\v1;
  4. use App\Controller\AbstractController;
  5. use App\Controller\Business\DistanceBusiness;
  6. use App\Controller\Business\DriverTypeBusiness;
  7. use App\Master\Framework\Library\GeTui\Push;
  8. use App\Model\Arts\CarSeatModel;
  9. use App\Model\Arts\DriverCarModel;
  10. use App\Model\Arts\DriverMessageModel;
  11. use App\Model\Arts\OrderDriverModel;
  12. use App\Model\Arts\OrderModel;
  13. use App\Model\Arts\UserCouponModel;
  14. use App\Model\Arts\UserModel;
  15. use App\Model\Arts\UserWalletModel;
  16. use App\Request\Api\v1\Order\AddMoneyRequest;
  17. use App\Request\Api\v1\Order\CancelRequest;
  18. use App\Request\Api\v1\Order\DetailRequest;
  19. use App\Request\Api\v1\Order\ListRequest;
  20. use App\Request\Api\v1\Order\PageRequest;
  21. use App\Request\Api\v1\Order\SubmitRequest;
  22. use App\Request\Api\v1\Order\TailwindDriverInfoRequest;
  23. use App\Request\Api\v1\Order\TailwindDriverRequest;
  24. use App\Request\Api\v1\Order\TailwindInviteRequest;
  25. use App\Service\QueueService;
  26. use App\Utils\AppResult;
  27. use App\Utils\Control\AuthUser;
  28. use Hyperf\DbConnection\Db;
  29. use Hyperf\Di\Annotation\Inject;
  30. /**
  31. * 用户管理
  32. * UserController
  33. */
  34. class OrderController extends AbstractController
  35. {
  36. // 日志模块名称
  37. const LOG_MODULE = 'v1/OrderController';
  38. #[Inject]
  39. protected QueueService $service;
  40. /**
  41. * 预下单(快车 接送机)
  42. * @param PageRequest $request
  43. * @return string
  44. */
  45. public function page(PageRequest $request)
  46. {
  47. $params = $request->validated();// 获取校验通过的参数
  48. $user = AuthUser::getInstance()->get();
  49. $point = $params['point'];
  50. $people_num = !empty($params['people_num']) ? $params['people_num'] : 1;
  51. $luggage_num = !empty($params['luggage_num']) ? $params['luggage_num'] : 0;
  52. $is_back = !empty($params['is_back']) ? $params['is_back'] : 0;
  53. $is_exclusive = !empty($params['is_exclusive']) ? $params['is_exclusive'] : 0;
  54. $appointment_time = !empty($params['appointment_time']) ? strtotime($params['appointment_time']) : 0;
  55. $appointment_time = $params['type'] == 30 && empty($appointment_time) ? time() : $appointment_time;// 如果是顺风车 且未选择时间,则默认为当前时间
  56. // 计算公里数
  57. $distanceBusiness = new DistanceBusiness();
  58. if (!$distanceBusiness->computeRoutes($point, $appointment_time)) {
  59. return AppResult::error($distanceBusiness->getMessage() ?? '计算距离错误');
  60. }
  61. $mapData = $distanceBusiness->getData();
  62. // 获取计算距离
  63. $distance = $mapData['distance'];// 距离m
  64. $duration = $mapData['duration'];// 时长s
  65. // 计算对应订单类型的价格
  66. $driverTypeBusiness = new DriverTypeBusiness();
  67. if (!$driverTypeBusiness->calculate((int)$params['type'], $distance, $duration, $point, $appointment_time, $is_back, $is_exclusive)) {
  68. return AppResult::error($driverTypeBusiness->getMessage() ?? '计算失败');
  69. }
  70. $res = $driverTypeBusiness->getData();
  71. if ($params['type'] == 50) {
  72. $total_amount = (string)round((float)$res['total_amount']);// 四舍五入
  73. $rmb_total_amount = (string)round((float)bcmul($total_amount, site('dollar_to_rmb'), 2));// 四舍五入
  74. // 跑腿订单
  75. $data = [
  76. 'errand' => [
  77. 'total_amount' => $total_amount,
  78. 'rmb_total_amount' => $rmb_total_amount
  79. ]
  80. ];
  81. } else {
  82. $airport_fee_min_price = site('airport_fee_min_price');// 接送机最低消费
  83. $carSeat = new CarSeatModel();
  84. $list = $carSeat->getList(orderBy: ['weight' => 'desc']);
  85. foreach ($list as $key => $val) {
  86. $basics_amount = bcsub(bcmul((string)$res['basics_amount'], (string)$val['subsidy_multiple'], 2),(string)$res['basics_amount'],2);// 计算车型补贴:公里数价格 * 补贴比例
  87. $total_amount = bcadd((string)$res['total_amount'], $basics_amount, 2);
  88. // 预约费
  89. $platform_subscribe_price = (string)($total_amount >= site('platform_subscribe_min') ? $val['platform_subscribe_price'] : '0');
  90. $total_amount = (string)round((float)bcadd($total_amount,$platform_subscribe_price,2));// 美元 四舍五入
  91. if ($params['type'] == 20 || ($params['type'] == 10 && $res['is_at_airport'] == 1)){
  92. $total_amount = max($total_amount,$airport_fee_min_price);// 接送机最低消费
  93. }
  94. $rmb_total_amount = (string)round((float)bcmul($total_amount, site('dollar_to_rmb'), 2));// 人民币 四舍五入
  95. $list[$key]['image'] = cdn_url($val['image']);
  96. $list[$key]['total_amount'] = $total_amount;
  97. $list[$key]['rmb_total_amount'] = $rmb_total_amount;
  98. $list[$key]['is_show'] = $val['people_num'] >= $people_num && $val['luggage_num'] >= $luggage_num ? 1 : 0;
  99. }
  100. // 往返提示说明
  101. $come_explain = '';
  102. if ($is_back == 1){
  103. $come_explain = $params['type'] == 10 ? site('wf_taxi_explain') : site('wf_airport_explain');
  104. }
  105. $data = [
  106. 'distance' => bcdiv((string)$distance,'1000'),
  107. 'come_explain' => $come_explain,
  108. 'car_seat_list' => $list,
  109. 'tailwind' => [
  110. [
  111. 'image' => cdn_url('/assets/img/shun_che1.png'),
  112. 'total_amount' => (string)round((float)$res['total_amount']),
  113. 'rmb_total_amount' => (string)round((float)bcmul($res['total_amount'], site('dollar_to_rmb'), 2)),
  114. 'name' => '拼车',
  115. 'is_exclusive' => 0,
  116. ],
  117. [
  118. 'image' => cdn_url('/assets/img/shun_che2.png'),
  119. 'total_amount' => (string)round((float)bcadd($res['total_amount'], site('tailwind_exclusive'), 2)),
  120. 'rmb_total_amount' => (string)round((float)bcmul(bcadd($res['total_amount'], site('tailwind_exclusive'), 2),site('dollar_to_rmb'), 2)),
  121. 'name' => '独享',
  122. 'is_exclusive' => 1,
  123. ]
  124. ],
  125. 'substitute' => [
  126. 'total_amount' => (string)round((float)$res['total_amount']),
  127. 'rmb_total_amount' => (string)round((float)bcmul($res['total_amount'], site('dollar_to_rmb'), 2)),
  128. 'image' => cdn_url('/assets/img/dai_toup1.png'),
  129. ]
  130. ];
  131. }
  132. // 汇率
  133. $data['dollar_to_rmb'] = site('dollar_to_rmb');
  134. if ($appointment_time == 0){
  135. // 如果有实时订单则提示是否继续接单
  136. $model = new OrderModel();
  137. if ($model->getDetail(params: ['user_id' => $user['id'],'status_in' => [10,20,30],'appointment_time_zero' => 0])){
  138. return AppResult::response_fast(2,"您有一条正在进行订单,确定要继续下单吗?", $data);
  139. }
  140. }
  141. return AppResult::success('success', $data);
  142. }
  143. /**
  144. * 提交订单(快车 接送机)
  145. * @param SubmitRequest $request
  146. * @return string
  147. */
  148. public function submit(SubmitRequest $request)
  149. {
  150. $params = $request->validated();// 获取校验通过的参数
  151. $user = AuthUser::getInstance()->get();
  152. // 如果是会员 则可设置一口价
  153. if ($user['is_vip'] != 1 && !empty($params['pay_amount'])){
  154. return AppResult::error('暂无设置一口价权限');
  155. }
  156. if ($user['is_vip'] == 1 && !empty($params['pay_amount']) && !in_array($params['type'], [10, 20])){
  157. return AppResult::error('仅支持快车或接送机设置一口价');
  158. }
  159. $point = $params['point'];
  160. $people_num = !empty($params['people_num']) ? $params['people_num'] : 1;// 乘车人数
  161. $luggage_num = !empty($params['luggage_num']) ? $params['luggage_num'] : 0;// 行李数
  162. $is_back = !empty($params['is_back']) ? $params['is_back'] : 0;// 是否往返
  163. $is_exclusive = !empty($params['is_exclusive']) ? $params['is_exclusive'] : 0;// 是否独享
  164. $appointment_time = !empty($params['appointment_time']) ? strtotime($params['appointment_time']) : 0;// 出发时间
  165. $appointment_time = $params['type'] == 30 && empty($appointment_time) ? time() : $appointment_time;// 如果是顺风车 且未选择时间,则默认为当前时间
  166. // 计算公里数
  167. $distanceBusiness = new DistanceBusiness();
  168. if (!$distanceBusiness->computeRoutes($point, $appointment_time)) {
  169. return AppResult::error($distanceBusiness->getMessage() ?? '计算距离错误');
  170. }
  171. $mapData = $distanceBusiness->getData();
  172. // 获取计算距离
  173. $distance = $mapData['distance'];// 距离m
  174. $duration = $mapData['duration'];// 时长s
  175. // 如果是会员 且是快车 或 接送机 则可设置一口价
  176. if ($user['is_vip'] == 1 && !empty($params['pay_amount']) && in_array($params['type'], [10, 20])){
  177. $carSeat = new CarSeatModel();
  178. $info = $carSeat->getDetail(params: ['id' => $params['car_seat_id']]);
  179. if (!($info['people_num'] >= $people_num && $info['luggage_num'] >= $luggage_num)) {
  180. return AppResult::error('当前车型不支持选择的人数行李数');
  181. }
  182. $pay_amount = $total_amount = $params['pay_amount'];
  183. // 预约费 vip预约费已经包含在
  184. $platform_subscribe_price = (string)($total_amount >= site('platform_subscribe_min') ? $info['platform_subscribe_price'] : '0');
  185. }else{
  186. // 计算对应订单类型的价格
  187. $driverTypeBusiness = new DriverTypeBusiness();
  188. if (!$driverTypeBusiness->calculate((int)$params['type'], $distance, $duration, $point, $appointment_time, $is_back, $is_exclusive)) {
  189. return AppResult::error($driverTypeBusiness->getMessage() ?? '计算失败');
  190. }
  191. $res = $driverTypeBusiness->getData();
  192. // 如果是接送机 快车 则根据车型计算价格
  193. if (in_array($params['type'], [10, 20])) {
  194. $carSeat = new CarSeatModel();
  195. $info = $carSeat->getDetail(params: ['id' => $params['car_seat_id']]);
  196. if (!($info['people_num'] >= $people_num && $info['luggage_num'] >= $luggage_num)) {
  197. return AppResult::error('当前车型不支持选择的人数行李数');
  198. }
  199. $basics_amount = bcsub(bcmul((string)$res['basics_amount'], (string)$info['subsidy_multiple'], 2),(string)$res['basics_amount'],2);// 计算车型补贴:公里数价格 * 补贴比例
  200. $total_amount = bcadd((string)$res['total_amount'], $basics_amount, 2);
  201. // 预约费
  202. $platform_subscribe_price = (string)($total_amount >= site('platform_subscribe_min') ? $info['platform_subscribe_price'] : '0');
  203. $total_amount = bcadd($total_amount,$platform_subscribe_price,2);
  204. if ($params['type'] == 20 || ($params['type'] == 10 && $res['is_at_airport'] == 1)){
  205. $airport_fee_min_price = site('airport_fee_min_price');// 接送机最低消费
  206. $total_amount = max($total_amount,$airport_fee_min_price);
  207. }
  208. }
  209. // 如果是 代驾 顺风车 跑腿 则无需单独增加价格
  210. if (in_array($params['type'], [30, 40, 50])) {
  211. $total_amount = $res['total_amount'];
  212. }
  213. if (!isset($total_amount)) {
  214. return AppResult::error('操作异常');
  215. }
  216. $pay_amount = $total_amount = (string)round((float)$total_amount);// 支付金额 四舍五入
  217. // 优惠券核销
  218. if (!empty($params['coupon_id'])){
  219. $order_coupons = (new UserCouponModel())->getOrderCoupon($user['id'],(int)$params['type'],(int)$params['coupon_id'],$total_amount);
  220. if ($total_amount >= $order_coupons['min_money']){
  221. $pay_amount = bcsub($total_amount,$order_coupons['money'],2);
  222. $discount_amount = $order_coupons['money'];
  223. }
  224. }
  225. }
  226. // 在线支付需校验金额
  227. if ($params['pay_type'] == 1) {
  228. $money = UserWalletModel::getOne($user['id'], 'money');
  229. if ($money < $pay_amount) {
  230. return AppResult::error('余额不足');
  231. }
  232. }
  233. $data = [
  234. 'type' => $params['type'],
  235. 'flight_type' => $params['flight_type'] ?? 0,
  236. 'distance' => (int)$distance,
  237. 'duration' => (int)$duration,
  238. 'total_amount' => $total_amount,
  239. 'dollar_to_rmb' => site('dollar_to_rmb'),
  240. 'pay_amount' => $pay_amount,
  241. 'discount_amount' => $discount_amount ?? 0,
  242. 'pay_type' => $params['pay_type'],
  243. 'is_pay' => $params['pay_type'] == 1 ? 1 : 0,
  244. 'coupon_id' => $params['coupon_id'] ?? 0,
  245. 'car_seat_id' => $params['car_seat_id'] ?? 0,
  246. 'name' => $params['name'] ?? $user['nickname'],
  247. 'phone' => $params['phone'] ?? "{$user['area_code']} {$user['mobile']}",
  248. 'people_num' => $people_num,
  249. 'luggage_num' => $luggage_num,
  250. 'start_lng' => $point[0]['lng'],
  251. 'start_lat' => $point[0]['lat'],
  252. 'end_lng' => end($point)['lng'],
  253. 'end_lat' => end($point)['lat'],
  254. 'is_back' => $is_back,
  255. 'is_exclusive' => $is_exclusive,
  256. 'is_child' => $params['is_child'] ?? 0,
  257. 'appointment_time' => $appointment_time,
  258. 'flight_no' => $params['flight_no'] ?? '',
  259. 'basics_amount' => $res['basics_amount'] ?? '0.00',
  260. 'city_subsidy_price' => $res['city_subsidy_price'] ?? '0.00',
  261. 'peak_price' => $res['peak_price'] ?? '0.00',
  262. 'night_price' => $res['night_price'] ?? '0.00',
  263. 'exclusive_price' => $res['exclusive_price'] ?? '0.00',
  264. 'platform_subscribe_price' => $platform_subscribe_price ?? '0.00',
  265. 'remark' => $params['remark'] ?? '',
  266. 'freight_name' => $params['freight_name'] ?? '',
  267. 'image' => $params['image'] ?? '',
  268. ];
  269. Db::beginTransaction();
  270. // 在线支付需要提前扣费
  271. if ($params['pay_type'] == 1) {
  272. $type_name = DriverTypeBusiness::type[$params['type']] ?? '';
  273. $wallet = new UserWalletModel();
  274. if (!$wallet->change($user['id'], (float)$data['pay_amount'], "{$type_name}订单消费")) {
  275. Db::rollBack();
  276. return AppResult::error($wallet->getMessage());
  277. }
  278. }
  279. // 使用优惠券
  280. if (!empty($params['coupon_id'])) {
  281. $coupon = UserCouponModel::query()
  282. ->where('id',$params['coupon_id'])
  283. ->where('type',$params['type'])
  284. ->where('is_use',0)
  285. ->where('status',1)
  286. ->where('valid_at', '>', time())
  287. ->update(['is_use' => 1, 'use_at' => time()]);
  288. if (!$coupon) {
  289. Db::rollBack();
  290. return AppResult::error('优惠券核销失败,请校验优惠券');
  291. }
  292. }
  293. // 增加用户订单数
  294. UserModel::query()->where('id', $user['id'])->increment('order_num');
  295. // 创建订单
  296. $model = new OrderModel();
  297. if (!$model->createOrder($user['id'], $data, $point)) {
  298. Db::rollBack();
  299. return AppResult::error($model->getMessage());
  300. }
  301. Db::commit();
  302. // 加入提醒司机接单队列
  303. if (!empty($model->getData()['order_no'])){
  304. $this->service->pushOrder([
  305. 'order_no' => $model->getData()['order_no']
  306. ]);
  307. }
  308. // 加入超时订单取消队列
  309. if (in_array($params['type'],[10,20,30,40,50])){
  310. $delay = $appointment_time > 0 ? 600 : 300;// 延迟时间 单位秒
  311. if ($params['type'] == 30){
  312. $delay = $appointment_time - time();
  313. $delay = max($delay, 600);
  314. }
  315. $this->service->cancelOrder([
  316. 'order_no' => $model->getData()['order_no']
  317. ],$delay);
  318. }
  319. return AppResult::success($model->getMessage(), $model->getData());
  320. }
  321. /**
  322. * 订单列表
  323. * @param ListRequest $request
  324. * @return string
  325. */
  326. public function list(ListRequest $request)
  327. {
  328. $params = $request->validated();
  329. $user = AuthUser::getInstance()->get();
  330. $params['user_id'] = $user['id'];
  331. // 列表筛选
  332. switch ($params['status_type']) {
  333. case 1:
  334. $params['status_in'] = [10, 20];
  335. $params['appointment_time_not'] = 1;
  336. break;
  337. case 2:
  338. $params['status_in'] = [10, 20, 30];
  339. break;
  340. case 3:
  341. $params['status_in'] = [40];
  342. }
  343. $model = new OrderModel();
  344. $model->setSelect([
  345. 'id', 'user_id', 'driver_id', 'type', 'order_no', 'driver_order_id', 'total_amount', 'pay_amount', 'appointment_time', 'create_time',
  346. 'driver_status', 'status'
  347. ]);
  348. $list = $model->getList(
  349. params : $params,
  350. orderBy: ['id' => 'desc'],
  351. with : [
  352. 'points' => function ($query) {
  353. $query->select('id', 'order_id', 'type', 'lng', 'lat', 'address', 'postal_code', 'is_in_city', 'is_at_airport')->where('status', 1)->orderBy('id', 'asc');
  354. }
  355. ]
  356. );
  357. foreach ($list as $key => $val) {
  358. $list[$key]['start_time'] = date('Y-m-d H:i', !empty($val['appointment_time']) ? $val['appointment_time'] : $val['create_time']);
  359. $list[$key]['create_time'] = strtotime($list[$key]['start_time']);
  360. }
  361. return AppResult::success(result: $list);
  362. }
  363. /**
  364. * 订单详情
  365. * @param DetailRequest $request
  366. * @return string
  367. */
  368. public function detail(DetailRequest $request)
  369. {
  370. $params = $request->validated();// 获取校验通过的参数
  371. $user = AuthUser::getInstance()->get();
  372. $model = new OrderModel();
  373. $model->setSelect([
  374. 'id', 'user_id', 'driver_id', 'type', 'flight_type', 'order_no', 'driver_order_id', 'total_amount', 'pay_amount', 'is_pay', 'raise_amount',
  375. 'pay_type', 'car_seat_id', 'name', 'phone', 'people_num', 'luggage_num', 'remark', 'distance', 'duration', 'is_back', 'is_exclusive', 'is_child',
  376. 'appointment_time', 'flight_no', 'car_no', 'car_color', 'car_brand', 'take_time', 'driver_status', 'status', 'create_time', 'start_driver_time', 'end_driver_time'
  377. ]);
  378. $info = $model->getDetail(
  379. params: ['order_no' => $params['order_no'], 'user_id' => $user['id']],
  380. with : [
  381. 'driver' => function ($query) {
  382. $query->select('id', 'name', 'area_code', 'mobile', 'avatar');
  383. },
  384. 'points' => function ($query) {
  385. $query->select('id', 'order_id', 'type', 'lng', 'lat', 'address', 'postal_code', 'is_in_city', 'is_at_airport')->where('status', 1)->orderBy('id', 'asc');
  386. }
  387. ]
  388. );
  389. if (!$info) {
  390. return AppResult::error('订单不存在');
  391. }
  392. $car_img = DriverCarModel::query()->where('driver_id',$info['driver']['id'] ?? 0)->value('car_img');
  393. $info['end_driver_time'] = $info['driver_status'] == 2 ? time_hour(!empty($info['end_driver_time']) ? $info['end_driver_time'] : ($info['end_driver_time'] + $info['duration'])) : 0;
  394. $info['driver_name'] = $info['driver']['name'] ?? '';
  395. $info['driver_avatar'] = $car_img ?? '';
  396. $info['driver_mobile'] = ($info['driver']['area_code'] ?? '') . ' ' . ($info['driver']['mobile'] ?? '');
  397. $info['service_telephone'] = site('service_telephone');// 客服电话
  398. unset($info['driver']);
  399. return AppResult::success('success', $info);
  400. }
  401. /**
  402. * 加价
  403. * @param AddMoneyRequest $request
  404. * @return string
  405. */
  406. public function add_money(AddMoneyRequest $request)
  407. {
  408. $params = $request->validated();// 获取校验通过的参数
  409. $user = AuthUser::getInstance()->get();
  410. $model = new OrderModel();
  411. $info = $model->getDetail(
  412. params: ['order_no' => $params['order_no'], 'user_id' => $user['id'], 'status' => 10],
  413. );
  414. if (!$info) {
  415. return AppResult::error('订单不存在或已被接单');
  416. }
  417. // 在线支付需校验金额
  418. if ($info['pay_type'] == 1) {
  419. $money = UserWalletModel::getOne($user['id'], 'money');
  420. if ($money < $params['amount']) {
  421. return AppResult::error('余额不足');
  422. }
  423. }
  424. Db::beginTransaction();
  425. // 在线支付需要提前扣费
  426. if ($info['pay_type'] == 1) {
  427. $type_name = DriverTypeBusiness::type[$info['type']] ?? '';
  428. $wallet = new UserWalletModel();
  429. if (!$wallet->change($user['id'], (float)$params['amount'], "{$type_name}订单加价消费")) {
  430. Db::rollBack();
  431. return AppResult::error($wallet->getMessage());
  432. }
  433. }
  434. if (!$model->addMoney($info['id'], (float)$params['amount'])) {
  435. Db::rollBack();
  436. return AppResult::error($model->getMessage());
  437. }
  438. Db::commit();
  439. return AppResult::success($model->getMessage());
  440. }
  441. /**
  442. * 取消订单
  443. * @param DetailRequest $request
  444. * @return string
  445. */
  446. public function cancel(CancelRequest $request)
  447. {
  448. $params = $request->validated();// 获取校验通过的参数
  449. $user = AuthUser::getInstance()->get();
  450. Db::beginTransaction();
  451. $model = new OrderModel();
  452. if (!$model->cancel(order_no: $params['order_no'], user_id: $user['id'],reason: $params['reason'])) {
  453. Db::rollBack();
  454. return AppResult::error($model->getMessage());
  455. }
  456. Db::commit();
  457. return AppResult::success($model->getMessage());
  458. }
  459. /**
  460. * 司机发布顺风车
  461. * @param TailwindDriverRequest $request
  462. * @return string
  463. */
  464. public function tailwind_driver(TailwindDriverRequest $request)
  465. {
  466. $params = $request->validated();// 获取校验通过的参数
  467. $user = AuthUser::getInstance()->get();
  468. $start_lng = $params['point'][0]['lng'] ?? '';
  469. $start_lat = $params['point'][0]['lat'] ?? '';
  470. $end_lng = $params['point'][1]['lng'] ?? '0.00';
  471. $end_lat = $params['point'][1]['lat'] ?? '0.00';
  472. unset($params['point']);
  473. $params['status'] = 1;
  474. $params['start_time_gt'] = time();
  475. $model = new OrderDriverModel();
  476. $model->setSelect([
  477. 'id', 'driver_id', 'car_no', 'remark', 'start_time', 'people_num', 'people_at_num', 'start_address', 'start_lng', 'start_lat',
  478. 'end_address', 'end_lng', 'end_lat', 'status', 'create_time',
  479. Db::raw("(st_distance(point ({$start_lng}, {$start_lat}),point(start_lng,start_lat))*111195) as start_distance"),
  480. Db::raw("(st_distance(point ({$end_lng}, {$end_lat}),point(end_lng,end_lat))*111195) as end_distance"),
  481. ]);
  482. $list = $model->getList(
  483. params : $params,
  484. orderBy: ['start_time'=>'asc','start_distance' => 'asc', 'end_distance' => 'asc'],
  485. with : [
  486. 'order' => function ($query) use ($user) {
  487. $query->select('id', 'driver_order_id', 'user_id', 'driver_id', 'status')->where('user_id', $user['id'] ?? 0)->whereIn('status', [10, 20, 30, 40]);
  488. },
  489. 'driver' => function ($query) {
  490. $query->select('id', 'name', 'avatar');
  491. },
  492. 'car' => function ($query) {
  493. $query->select('driver_id', 'car_color', 'car_brand');
  494. }
  495. ]
  496. );
  497. foreach ($list as $key => $val) {
  498. // 起点终点总距离差
  499. $total_distance = bcadd((string)$val['start_distance'], (string)($val['end_distance'] ?? 0), 2);
  500. $list[$key]['total_distance'] = $total_distance;
  501. // 起点距离
  502. $stat_distance = 0;
  503. if (isset($val['start_distance'])) {
  504. if ($val['start_distance'] >= 1000) {
  505. $stat_distance = round(($val['start_distance'] / 1000), 2) . 'km';
  506. } else {
  507. $stat_distance = round($val['start_distance'], 1) . 'm';
  508. }
  509. }
  510. $list[$key]['start_distance'] = $stat_distance;
  511. // 终点距离
  512. $end_distance = 0;
  513. if (isset($val['end_distance'])) {
  514. if ($val['end_distance'] >= 1000) {
  515. $end_distance = round(($val['end_distance'] / 1000), 2) . 'km';
  516. } else {
  517. $end_distance = round($val['end_distance'], 1) . 'm';
  518. }
  519. }
  520. $list[$key]['end_distance'] = $end_distance;
  521. $list[$key]['start_time'] = time_hour($val['start_time']);
  522. $list[$key]['is_subscribe'] = !empty($val['order']) ? 1 : 0;
  523. $list[$key]['car_color'] = $val['car']['car_color'] ?? '';
  524. $list[$key]['car_brand'] = $val['car']['car_brand'] ?? '';
  525. $list[$key]['driver_name'] = $val['driver']['name'] ?? '';
  526. $list[$key]['driver_avatar'] = !empty($val['driver']['avatar']) ? cdn_url($val['driver']['avatar']) : '';
  527. unset($list[$key]['car'], $list[$key]['driver']);
  528. }
  529. return AppResult::success('success', $list);
  530. }
  531. /**
  532. * 司机发布顺风车详情
  533. * @param TailwindDriverInfoRequest $request
  534. * @return string
  535. */
  536. public function tailwind_driver_info(TailwindDriverInfoRequest $request)
  537. {
  538. $params = $request->validated();// 获取校验通过的参数
  539. $user = AuthUser::getInstance()->get();
  540. $params['status'] = 1;
  541. $model = new OrderDriverModel();
  542. $model->setSelect([
  543. 'id', 'driver_id', 'car_no', 'remark', 'start_time', 'people_num', 'people_at_num', 'start_address', 'start_lng', 'start_lat',
  544. 'end_address', 'end_lng', 'end_lat', 'status', 'create_time'
  545. ]);
  546. $info = $model->getDetail(
  547. params : $params,
  548. with : [
  549. 'order' => function ($query) use ($user) {
  550. $query->select('id', 'driver_order_id', 'user_id', 'driver_id', 'status')->where('user_id', $user['id'] ?? 0)->whereIn('status', [10, 20, 30, 40]);
  551. },
  552. 'driver' => function ($query) {
  553. $query->select('id', 'name', 'avatar');
  554. },
  555. 'car' => function ($query) {
  556. $query->select('driver_id', 'car_color', 'car_brand');
  557. }
  558. ]
  559. );
  560. $info['start_time'] = time_hour($info['start_time']);
  561. $info['is_subscribe'] = !empty($info['order']) ? 1 : 0;
  562. $info['car_color'] = $info['car']['car_color'] ?? '';
  563. $info['car_brand'] = $info['car']['car_brand'] ?? '';
  564. $info['driver_name'] = $info['driver']['name'] ?? '';
  565. $info['driver_avatar'] = !empty($info['driver']['avatar']) ? cdn_url($info['driver']['avatar']) : '';
  566. unset($info['car'], $info['driver']);
  567. return AppResult::success('success', $info);
  568. }
  569. /**
  570. * 邀请司机接单
  571. * @param TailwindInviteRequest $request
  572. * @return string
  573. */
  574. public function tailwind_invite(TailwindInviteRequest $request)
  575. {
  576. $params = $request->validated();// 获取校验通过的参数
  577. $user = AuthUser::getInstance()->get();
  578. $model = new OrderModel();
  579. $info = $model->getDetail(
  580. params: ['order_no' => $params['order_no'], 'user_id' => $user['id'], 'status' => 10],
  581. );
  582. if (!$info) {
  583. return AppResult::error('订单不存在或已被接单');
  584. }
  585. $driver_order = OrderDriverModel::query()->where('id', $params['driver_order_id'])->where('status',1)->first();
  586. if (!$driver_order){
  587. return AppResult::error('当前拼车路线不存在或行程已出发');
  588. }
  589. if (!$model->tailwind_invite($info['id'], $params['driver_order_id'])) {
  590. return AppResult::error($model->getMessage());
  591. }
  592. // 发送系统消息
  593. DriverMessageModel::add([
  594. 'driver_id' => $driver_order['driver_id'],
  595. 'type' => 1,
  596. 'name' => "顺风车有新订单",
  597. 'content' => "用户邀请您同行,请前往顺风车行程中查看详情",
  598. 'value' => $params['order_no']
  599. ]);
  600. return AppResult::success($model->getMessage());
  601. }
  602. }