OrderService.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. <?php
  2. namespace App\Services\Client;
  3. use App\Models\AgentConfig;
  4. use App\Models\AgentInfo;
  5. use App\Models\CoachConfig;
  6. use App\Models\CoachSchedule;
  7. use App\Models\CoachUser;
  8. use App\Models\Coupon;
  9. use App\Models\MemberUser;
  10. use App\Models\Order;
  11. use App\Models\OrderGrabRecord;
  12. use App\Models\OrderRecord;
  13. use App\Models\Project;
  14. use App\Models\SysConfig;
  15. use App\Models\User;
  16. use App\Models\Wallet;
  17. use App\Models\WalletRefundRecord;
  18. use Exception;
  19. use Illuminate\Support\Facades\Auth;
  20. use Illuminate\Support\Facades\DB;
  21. use Illuminate\Support\Facades\Log;
  22. class OrderService
  23. {
  24. protected AgentService $agentService;
  25. protected ProjectService $projectService;
  26. public function __construct(
  27. AgentService $agentService,
  28. ProjectService $projectService
  29. ) {
  30. $this->agentService = $agentService;
  31. $this->projectService = $projectService;
  32. }
  33. /**
  34. * 订单初始化
  35. */
  36. public function initialize($userId, $data)
  37. {
  38. $user = MemberUser::find($userId);
  39. if (! $user) {
  40. throw new Exception('用户不存在');
  41. }
  42. if ($user->state != 'enable') {
  43. throw new Exception('用户状态异常');
  44. }
  45. // 查询用户钱包
  46. $wallet = $user->wallet;
  47. // 查询默认地址
  48. $address = $user->address;
  49. $areaCode = $address ? $address->area_code : $data['area_code'];
  50. // 查询技师数据
  51. $coach = CoachUser::where('state', 'enable')
  52. ->whereHas('info', function ($query) {
  53. $query->where('state', 'approved');
  54. })
  55. ->whereHas('real', function ($query) {
  56. $query->where('state', 'approved');
  57. })
  58. ->whereHas('qual', function ($query) {
  59. $query->where('state', 'approved');
  60. })
  61. ->with(['info:id,nickname,avatar,gender'])
  62. ->find($data['coach_id']);
  63. if (! $coach) {
  64. throw new Exception('技师不存在');
  65. }
  66. // 查询技师排班
  67. // $schedule = CoachSchedule::where('coach_id', $coachId)
  68. // ->where('date', date('Y-m-d'))
  69. // ->first();
  70. // // 查询用户优惠券
  71. // $coupons = Coupon::where('user_id', $userId)
  72. // ->where('state', 'enable')
  73. // ->where('expire_time', '>', now())
  74. // ->get();
  75. // 获取项目详情
  76. $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
  77. // 计算订单金额
  78. $amounts = $this->calculateOrderAmount($userId, $address?->id, $data['coach_id'], $data['project_id'], $project?->agent_id);
  79. return [
  80. 'wallet' => $wallet,
  81. 'coach' => $coach,
  82. 'project' => $project,
  83. 'address' => $address,
  84. // 'schedule' => $schedule,
  85. 'amounts' => $amounts,
  86. // 'coupons' => $coupons
  87. ];
  88. }
  89. /**
  90. * 创建订单
  91. */
  92. public function createOrder($userId, array $data)
  93. {
  94. try {
  95. return DB::transaction(function () use ($userId, $data) {
  96. // 1. 参数校验
  97. $user = MemberUser::where('id', $userId)
  98. ->where('state', 'enable')
  99. ->firstOrFail();
  100. $address = $user->addresses()
  101. ->where('id', $data['address_id'])
  102. ->firstOrFail();
  103. // 查询技师及其认证状态
  104. $coach = CoachUser::where('id', $data['coach_id'])
  105. ->where('state', 'enable')
  106. ->whereHas('info', function ($query) {
  107. $query->where('state', 'approved');
  108. })
  109. ->whereHas('qual', function ($query) {
  110. $query->where('state', 'approved');
  111. })
  112. ->whereHas('real', function ($query) {
  113. $query->where('state', 'approved');
  114. })
  115. ->firstOrFail();
  116. $project = Project::where('id', $data['project_id'])
  117. ->where('state', 'enable')
  118. ->firstOrFail();
  119. // 2. 创建订单
  120. $orderType = isset($data['order_id']) ? 'add_time' : 'normal';
  121. // 计算订单金额
  122. $amounts = $this->calculateOrderAmount(
  123. $userId,
  124. $data['address_id'],
  125. $data['coach_id'],
  126. $data['project_id'],
  127. $project->agent_id,
  128. $data['use_balance'] ?? false
  129. );
  130. $order = new Order;
  131. $order->user_id = $userId;
  132. $order->project_id = $data['project_id'];
  133. $order->coach_id = $data['coach_id'];
  134. $order->type = $orderType;
  135. $order->state = 'wait_pay';
  136. $order->source = 'platform';
  137. $order->total_amount = $amounts['total_amount'];
  138. $order->balance_amount = $amounts['balance_amount'];
  139. $order->pay_amount = $amounts['pay_amount'];
  140. $order->project_amount = $amounts['project_amount'];
  141. $order->traffic_amount = $orderType == 'add_time' ? 0 : $amounts['delivery_fee'];
  142. $order->payment_type = ($data['use_balance'] && $amounts['pay_amount'] == 0) ? 'balance' : null;
  143. $order->service_time = $data['service_time'];
  144. // 从用户地址获取位置信息
  145. $order->longitude = $address->longitude; // 经度
  146. $order->latitude = $address->latitude; // 纬度
  147. $order->location = $address->location; // 定位地址
  148. $order->address = $address->address; // 详细地址
  149. $order->area_code = $address->area_code; // 行政区划代码
  150. $order->save();
  151. // 3. 创建订单记录
  152. OrderRecord::create([
  153. 'order_id' => $order->id,
  154. 'object_id' => $userId,
  155. 'object_type' => MemberUser::class,
  156. 'state' => $orderType == 'add_time' ? 'add_time' : 'create',
  157. 'remark' => $orderType == 'add_time' ? '加钟订单' : '创建订单',
  158. ]);
  159. // 4. 余额支付处理
  160. if ($order->payment_type == 'balance') {
  161. $order->state = 'wait_receive';
  162. $order->save();
  163. OrderRecord::create([
  164. 'order_id' => $order->id,
  165. 'object_id' => $userId,
  166. 'object_type' => MemberUser::class,
  167. 'state' => 'pay',
  168. 'remark' => '余额支付',
  169. ]);
  170. // 创建技师排班
  171. // CoachSchedule::create([
  172. // 'coach_id' => $data['coach_id'],
  173. // 'date' => date('Y-m-d'),
  174. // 'state' => 'busy',
  175. // ]);
  176. // TODO: 发送抢单通知
  177. }
  178. return [
  179. 'order_id' => $order->id,
  180. 'payment_type' => $order->payment_type,
  181. ];
  182. });
  183. } catch (Exception $e) {
  184. Log::error('创建订单失败:', [
  185. 'message' => $e->getMessage(),
  186. 'user_id' => $userId,
  187. 'data' => $data,
  188. ]);
  189. throw $e;
  190. }
  191. }
  192. /**
  193. * 结束订单
  194. */
  195. public function finishOrder($userId, $orderId)
  196. {
  197. return DB::transaction(function () use ($userId, $orderId) {
  198. $order = Order::where('user_id', $userId)
  199. ->where('id', $orderId)
  200. ->first();
  201. if (! $order) {
  202. throw new Exception('订单不存在');
  203. }
  204. // 添加订单结束记录
  205. OrderRecord::create([
  206. 'order_id' => $orderId,
  207. 'user_id' => $userId,
  208. 'state' => 'finish',
  209. 'remark' => '服务完成',
  210. ]);
  211. // 修改订单状态
  212. $order->state = 'finished';
  213. $order->save();
  214. return ['message' => '订单已完成'];
  215. });
  216. }
  217. /**
  218. * 确认技师离开
  219. */
  220. public function confirmLeave($userId, $orderId)
  221. {
  222. return DB::transaction(function () use ($userId, $orderId) {
  223. $order = Order::where('user_id', $userId)
  224. ->where('id', $orderId)
  225. ->first();
  226. if (! $order) {
  227. throw new Exception('订单不存在');
  228. }
  229. // 添加订单撤离记录
  230. OrderRecord::create([
  231. 'order_id' => $orderId,
  232. 'user_id' => $userId,
  233. 'state' => 'leave',
  234. 'remark' => '技师已离开',
  235. ]);
  236. // 修改订单状态
  237. $order->state = 'allow_leave';
  238. $order->save();
  239. return ['message' => '已确认技师离开'];
  240. });
  241. }
  242. /**
  243. * 取消订单
  244. */
  245. public function cancelOrder($userId, $orderId)
  246. {
  247. return DB::transaction(function () use ($userId, $orderId) {
  248. $order = Order::where('user_id', $userId)
  249. ->where('id', $orderId)
  250. ->first();
  251. if (! $order) {
  252. throw new Exception('订单不存在');
  253. }
  254. // 添加订单取消记录
  255. OrderRecord::create([
  256. 'order_id' => $orderId,
  257. 'user_id' => $userId,
  258. 'state' => 'cancel',
  259. 'remark' => '用户取消订单',
  260. ]);
  261. // 修改订单状态
  262. $order->state = 'cancelled';
  263. $order->save();
  264. return ['message' => '订单已取消'];
  265. });
  266. }
  267. /**
  268. * 获取订单列表
  269. */
  270. public function getOrderList()
  271. {
  272. $userId = Auth::id();
  273. return Order::where('user_id', $userId)
  274. ->with([
  275. 'project:id,title,cover,price',
  276. 'coach:id,name,avatar',
  277. 'agent:id,company_name',
  278. 'address:id,address',
  279. ])
  280. ->orderBy('created_at', 'desc')
  281. ->paginate(10);
  282. }
  283. /**
  284. * 获取订单详情
  285. */
  286. public function getOrderDetail($orderId)
  287. {
  288. $userId = Auth::id();
  289. return Order::where('id', $orderId)
  290. ->where('user_id', $userId)
  291. ->with([
  292. 'project:id,title,cover,price,duration',
  293. 'coach:id,name,avatar,mobile',
  294. 'agent:id,company_name',
  295. 'address:id,address,latitude,longitude',
  296. 'records' => function ($query) {
  297. $query->orderBy('created_at', 'asc');
  298. },
  299. ])
  300. ->firstOrFail();
  301. }
  302. /**
  303. * 订单退款
  304. */
  305. public function refundOrder($orderId)
  306. {
  307. $userId = Auth::id();
  308. return DB::transaction(function () use ($orderId, $userId) {
  309. // 查询并锁定订单
  310. $order = Order::where('id', $orderId)
  311. ->where('user_id', $userId)
  312. ->where('state', 'pending')
  313. ->lockForUpdate()
  314. ->firstOrFail();
  315. // 更新订单状态
  316. $order->state = 'refunded';
  317. $order->save();
  318. // 添加订单记录
  319. OrderRecord::create([
  320. 'order_id' => $orderId,
  321. 'object_id' => $userId,
  322. 'object_type' => 'user',
  323. 'state' => 'refund',
  324. 'remark' => '订单退款',
  325. ]);
  326. // 创建退款记录
  327. WalletRefundRecord::create([
  328. 'order_id' => $orderId,
  329. 'user_id' => $userId,
  330. 'amount' => $order->total_amount,
  331. 'state' => 'success',
  332. ]);
  333. return ['message' => '退款成功'];
  334. });
  335. }
  336. /**
  337. * 获取代理商配置
  338. */
  339. public function getAgentConfig($agentId)
  340. {
  341. $agent = AgentInfo::where('id', $agentId)
  342. ->where('state', 'enable')
  343. ->firstOrFail();
  344. // $config = AgentConfig::where('agent_id', $agentId)->firstOrFail();
  345. return [
  346. // 'min_distance' => $config->min_distance,
  347. // 'min_fee' => $config->min_fee,
  348. // 'per_km_fee' => $config->per_km_fee
  349. ];
  350. }
  351. /**
  352. * 获取技师配置
  353. */
  354. public function getCoachConfig($coachId)
  355. {
  356. $coach = CoachUser::where('id', $coachId)
  357. ->where('state', 'enable')
  358. ->where('auth_state', 'passed')
  359. ->firstOrFail();
  360. // $config = CoachConfig::where('coach_id', $coachId)->firstOrFail();
  361. return [
  362. // 'delivery_fee_type' => $config->delivery_fee_type,
  363. // 'charge_delivery_fee' => $config->charge_delivery_fee
  364. ];
  365. }
  366. /**
  367. * 计算路费金额
  368. */
  369. public function calculateDeliveryFee($coachId, $projectId, $agentId, $distance)
  370. {
  371. // 查询技师数据
  372. $coach = CoachUser::where('state', 'enable')
  373. ->whereHas('info', function ($query) {
  374. $query->where('state', 'approved');
  375. })
  376. ->whereHas('real', function ($query) {
  377. $query->where('state', 'approved');
  378. })
  379. ->whereHas('qual', function ($query) {
  380. $query->where('state', 'approved');
  381. })
  382. ->find($coachId);
  383. if (! $coach) {
  384. throw new Exception('技师不存在');
  385. }
  386. // 查询技师项目
  387. $coachProject = $coach->projects()
  388. ->where('state', 'enable')
  389. ->where('project_id', $projectId)
  390. ->first();
  391. if (! $coachProject) {
  392. throw new Exception('项目不存在');
  393. }
  394. // 技师免收路费
  395. if ($coachProject->traffic_fee_type == 'free') {
  396. return 0;
  397. }
  398. // 查询代理商
  399. $agent = AgentInfo::where('state', 'enable')->find($agentId);
  400. if ($agent) {
  401. $agentProject = $agent->projects()
  402. ->where('state', 'enable')
  403. ->where('project_id', $projectId)
  404. ->first();
  405. if (! $agentProject) {
  406. throw new Exception('代理商项目不存在');
  407. }
  408. $config = $agent->projectConfig;
  409. } else {
  410. // 系统配置
  411. $config = SysConfig::where('key', 'delivery_fee')->firstOrFail();
  412. dd('暂停处理');
  413. }
  414. $fee = 0;
  415. if ($distance <= $config->min_distance) {
  416. $fee = $config->min_fee;
  417. } else {
  418. $extraDistance = $distance - $config->min_distance;
  419. $fee = $config->min_fee + ($extraDistance * $config->per_km_fee);
  420. }
  421. return $coachProject->delivery_fee_type == 'round_trip' ? $fee * 2 : $fee;
  422. }
  423. /**
  424. * 计算订单金额
  425. */
  426. public function calculateOrderAmount($userId, $addressId, $coachId, $projectId, $agentId, $useBalance = false)
  427. {
  428. // 参数校验
  429. $user = MemberUser::find($userId);
  430. if (! $user) {
  431. throw new Exception('用户不存在');
  432. }
  433. if ($user->state != 'enable') {
  434. throw new Exception('用户状态异常');
  435. }
  436. // 查询地址
  437. $address = $user->address()->find($addressId);
  438. // 查询技师数据
  439. $coach = CoachUser::where('state', 'enable')
  440. ->whereHas('info', function ($query) {
  441. $query->where('state', 'approved');
  442. })
  443. ->whereHas('real', function ($query) {
  444. $query->where('state', 'approved');
  445. })
  446. ->whereHas('qual', function ($query) {
  447. $query->where('state', 'approved');
  448. })
  449. ->with(['info:id,nickname,avatar,gender'])
  450. ->find($coachId);
  451. if (! $coach) {
  452. throw new Exception('技师不存在');
  453. }
  454. // 查询技师项目
  455. $coachProject = $coach->projects()
  456. ->where('state', 'enable')
  457. ->where('project_id', $projectId)
  458. ->first();
  459. if (! $coachProject) {
  460. throw new Exception('项目不存在');
  461. }
  462. // 查询项目
  463. $project = $coachProject->basicInfo;
  464. if (! $project) {
  465. throw new Exception('项目不存在');
  466. }
  467. if ($project->state != 'enable') {
  468. throw new Exception('项目状态异常');
  469. }
  470. // 查询代理商
  471. $agent = AgentInfo::where('state', 'enable')->find($agentId);
  472. if ($agent) {
  473. $agentProject = $agent->projects()
  474. ->where('state', 'enable')
  475. ->where('project_id', $projectId)
  476. ->first();
  477. if (! $agentProject) {
  478. throw new Exception('代理商项目不存在');
  479. }
  480. $project->price = $agentProject->price;
  481. $project->duration = $agentProject->duration;
  482. $project->distance = $address->distance;
  483. }
  484. // 计算金额
  485. $projectAmount = $project->price;
  486. $deliveryFee = $this->calculateDeliveryFee($coachId, $projectId, $agentId, $address?->distance);
  487. // $tipAmount = request()->has('order_id') ? Order::find(request()->input('order_id'))->tip_amount : 0;
  488. $couponAmount = 0;
  489. if (request()->has('coupon_id')) {
  490. // $coupon = Coupon::where('id', request()->input('coupon_id'))
  491. // ->where('state', 'enable')
  492. // ->firstOrFail();
  493. // $couponAmount = $coupon->amount;
  494. }
  495. $totalAmount = $projectAmount + $deliveryFee - $couponAmount;
  496. $balanceAmount = 0;
  497. $payAmount = $totalAmount;
  498. if ($useBalance) {
  499. $wallet = Wallet::where('user_id', $userId)->first();
  500. if ($wallet && $wallet->balance >= $totalAmount) {
  501. $balanceAmount = $totalAmount;
  502. $payAmount = 0;
  503. } elseif ($wallet) {
  504. $balanceAmount = $wallet->balance;
  505. $payAmount = $totalAmount - $balanceAmount;
  506. }
  507. }
  508. return [
  509. 'total_amount' => $totalAmount,
  510. 'balance_amount' => $balanceAmount,
  511. 'pay_amount' => $payAmount,
  512. 'coupon_amount' => $couponAmount,
  513. // 'tip_amount' => $tipAmount,
  514. 'project_amount' => $projectAmount,
  515. 'delivery_fee' => $deliveryFee,
  516. ];
  517. }
  518. /**
  519. * 加钟
  520. */
  521. public function addTime($userId, $orderId)
  522. {
  523. $order = Order::where('id', $orderId)
  524. ->where('user_id', $userId)
  525. ->firstOrFail();
  526. return $this->createOrder($userId, $order->project_id, $order->address_id, $order->coach_id, 0, $orderId);
  527. }
  528. /**
  529. * 指定技师
  530. */
  531. public function assignCoach($userId, $orderId, $coachId)
  532. {
  533. return DB::transaction(function () use ($userId, $orderId, $coachId) {
  534. // 参数校验
  535. $user = MemberUser::where('id', $userId)
  536. ->where('state', 'enable')
  537. ->firstOrFail();
  538. $order = Order::where('id', $orderId)
  539. ->where('user_id', $userId)
  540. ->whereIn('state', [0, 1, 6])
  541. ->firstOrFail();
  542. $coach = CoachUser::where('id', $coachId)
  543. ->where('state', 'enable')
  544. ->where('auth_state', 'passed')
  545. ->firstOrFail();
  546. // $schedule = CoachSchedule::where('coach_id', $coachId)
  547. // ->where('date', date('Y-m-d'))
  548. // ->where('state', 'free')
  549. // ->firstOrFail();
  550. // 修改订单
  551. $order->coach_id = $coachId;
  552. if ($order->state == 'created') {
  553. $amounts = $this->calculateOrderAmount($userId, $order->address_id, $coachId, $order->project_id, $order->agent_id, $order->payment_type == 'balance');
  554. $order->total_amount = $amounts['total_amount'];
  555. $order->balance_amount = $amounts['balance_amount'];
  556. $order->pay_amount = $amounts['pay_amount'];
  557. $order->coupon_amount = $amounts['coupon_amount'];
  558. // $order->tip_amount = $amounts['tip_amount'];
  559. $order->project_amount = $amounts['project_amount'];
  560. $order->delivery_fee = $amounts['delivery_fee'];
  561. if ($order->payment_type == 'balance') {
  562. $order->state = 'paid';
  563. }
  564. }
  565. if ($order->state == 'paid') {
  566. $order->state = 'assigned';
  567. }
  568. $order->save();
  569. // 创建订单历史
  570. OrderRecord::create([
  571. 'order_id' => $order->id,
  572. 'type' => 'assigned',
  573. 'user_id' => $userId,
  574. 'coach_id' => $coachId,
  575. ]);
  576. OrderRecord::create([
  577. 'order_id' => $order->id,
  578. 'type' => 'accepted',
  579. 'user_id' => $userId,
  580. 'coach_id' => $coachId,
  581. 'remark' => '抢单成功',
  582. ]);
  583. // 更新抢单池
  584. OrderGrabRecord::where('order_id', $orderId)
  585. ->update(['state' => 'success', 'coach_id' => $coachId]);
  586. // 更新排班
  587. // $schedule->state = 'busy';
  588. // $schedule->save();
  589. return true;
  590. });
  591. }
  592. }