OrderService.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <?php
  2. namespace App\Services\Client;
  3. use App\Models\Order;
  4. use App\Models\OrderRecord;
  5. use App\Models\WalletRefundRecord;
  6. use App\Models\User;
  7. use App\Models\MemberAddress;
  8. use App\Models\CoachUser;
  9. use App\Models\Project;
  10. use App\Models\AgentInfo;
  11. use App\Models\AgentConfig;
  12. use App\Models\CoachConfig;
  13. use App\Models\Wallet;
  14. use App\Models\Coupon;
  15. use App\Models\CoachSchedule;
  16. use App\Models\MemberUser;
  17. use App\Models\OrderHistory;
  18. use App\Models\OrderGrabRecord;
  19. use Illuminate\Support\Facades\Auth;
  20. use Illuminate\Support\Facades\DB;
  21. use Exception;
  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, $coachId, $areaCode, $projectId)
  37. {
  38. // 查询用户钱包
  39. $wallet = Wallet::where('user_id', $userId)->first();
  40. // 查询默认地址
  41. $address = MemberAddress::where('user_id', $userId)
  42. ->where('is_default', 1)
  43. ->first();
  44. if ($address) {
  45. $areaCode = $address->area_code;
  46. }
  47. // 查询技师数据
  48. $coach = CoachUser::where('id', $coachId)
  49. ->where('state', 'enable')
  50. ->where('auth_state', 'passed')
  51. ->first();
  52. // 查询技师排班
  53. // $schedule = CoachSchedule::where('coach_id', $coachId)
  54. // ->where('date', date('Y-m-d'))
  55. // ->first();
  56. // // 查询用户优惠券
  57. // $coupons = Coupon::where('user_id', $userId)
  58. // ->where('state', 'enable')
  59. // ->where('expire_time', '>', now())
  60. // ->get();
  61. // 获取代理商
  62. $agent = $this->agentService->getAgent($areaCode);
  63. // 获取项目详情
  64. $project = $this->projectService->getProjectDetail($projectId, $agent->id);
  65. // 计算订单金额
  66. $amounts = $this->calculateOrderAmount($userId, $address->id, $coachId, $projectId, $agent->id);
  67. return [
  68. 'wallet' => $wallet,
  69. 'coach' => $coach,
  70. 'project' => $project,
  71. 'address' => $address,
  72. // 'schedule' => $schedule,
  73. 'amounts' => $amounts,
  74. // 'coupons' => $coupons
  75. ];
  76. }
  77. /**
  78. * 结束订单
  79. */
  80. public function finishOrder($userId, $orderId)
  81. {
  82. return DB::transaction(function() use ($userId, $orderId) {
  83. $order = Order::where('user_id', $userId)
  84. ->where('id', $orderId)
  85. ->first();
  86. if (!$order) {
  87. throw new Exception('订单不存在');
  88. }
  89. // 添加订单结束记录
  90. OrderRecord::create([
  91. 'order_id' => $orderId,
  92. 'user_id' => $userId,
  93. 'state' => 'finish',
  94. 'remark' => '服务完成'
  95. ]);
  96. // 修改订单状态
  97. $order->status = 'finished';
  98. $order->save();
  99. return ['message' => '订单已完成'];
  100. });
  101. }
  102. /**
  103. * 确认技师离开
  104. */
  105. public function confirmLeave($userId, $orderId)
  106. {
  107. return DB::transaction(function() use ($userId, $orderId) {
  108. $order = Order::where('user_id', $userId)
  109. ->where('id', $orderId)
  110. ->first();
  111. if (!$order) {
  112. throw new Exception('订单不存在');
  113. }
  114. // 添加订单撤离记录
  115. OrderRecord::create([
  116. 'order_id' => $orderId,
  117. 'user_id' => $userId,
  118. 'state' => 'leave',
  119. 'remark' => '技师已离开'
  120. ]);
  121. // 修改订单状态
  122. $order->status = 'allow_leave';
  123. $order->save();
  124. return ['message' => '已确认技师离开'];
  125. });
  126. }
  127. /**
  128. * 取消订单
  129. */
  130. public function cancelOrder($userId, $orderId)
  131. {
  132. return DB::transaction(function() use ($userId, $orderId) {
  133. $order = Order::where('user_id', $userId)
  134. ->where('id', $orderId)
  135. ->first();
  136. if (!$order) {
  137. throw new Exception('订单不存在');
  138. }
  139. // 添加订单取消记录
  140. OrderRecord::create([
  141. 'order_id' => $orderId,
  142. 'user_id' => $userId,
  143. 'state' => 'cancel',
  144. 'remark' => '用户取消订单'
  145. ]);
  146. // 修改订单状态
  147. $order->status = 'cancelled';
  148. $order->save();
  149. return ['message' => '订单已取消'];
  150. });
  151. }
  152. /**
  153. * 获取订单列表
  154. */
  155. public function getOrderList()
  156. {
  157. $userId = Auth::id();
  158. return Order::where('user_id', $userId)
  159. ->with([
  160. 'project:id,title,cover,price',
  161. 'coach:id,name,avatar',
  162. 'agent:id,company_name',
  163. 'address:id,address'
  164. ])
  165. ->orderBy('created_at', 'desc')
  166. ->paginate(10);
  167. }
  168. /**
  169. * 获取订单详情
  170. */
  171. public function getOrderDetail($orderId)
  172. {
  173. $userId = Auth::id();
  174. return Order::where('id', $orderId)
  175. ->where('user_id', $userId)
  176. ->with([
  177. 'project:id,title,cover,price,duration',
  178. 'coach:id,name,avatar,mobile',
  179. 'agent:id,company_name',
  180. 'address:id,address,latitude,longitude',
  181. 'records' => function($query) {
  182. $query->orderBy('created_at', 'asc');
  183. }
  184. ])
  185. ->firstOrFail();
  186. }
  187. /**
  188. * 订单退款
  189. */
  190. public function refundOrder($orderId)
  191. {
  192. $userId = Auth::id();
  193. return DB::transaction(function() use ($orderId, $userId) {
  194. // 查询并锁定订单
  195. $order = Order::where('id', $orderId)
  196. ->where('user_id', $userId)
  197. ->where('state', 'pending')
  198. ->lockForUpdate()
  199. ->firstOrFail();
  200. // 更新订单状态
  201. $order->state = 'refunded';
  202. $order->save();
  203. // 添加订单记录
  204. OrderRecord::create([
  205. 'order_id' => $orderId,
  206. 'object_id' => $userId,
  207. 'object_type' => 'user',
  208. 'state' => 'refund',
  209. 'remark' => '订单退款'
  210. ]);
  211. // 创建退款记录
  212. WalletRefundRecord::create([
  213. 'order_id' => $orderId,
  214. 'user_id' => $userId,
  215. 'amount' => $order->total_amount,
  216. 'state' => 'success'
  217. ]);
  218. return ['message' => '退款成功'];
  219. });
  220. }
  221. /**
  222. * 获取代理商配置
  223. */
  224. public function getAgentConfig($agentId)
  225. {
  226. $agent = AgentInfo::where('id', $agentId)
  227. ->where('state', 'enable')
  228. ->firstOrFail();
  229. // $config = AgentConfig::where('agent_id', $agentId)->firstOrFail();
  230. return [
  231. // 'min_distance' => $config->min_distance,
  232. // 'min_fee' => $config->min_fee,
  233. // 'per_km_fee' => $config->per_km_fee
  234. ];
  235. }
  236. /**
  237. * 获取技师配置
  238. */
  239. public function getCoachConfig($coachId)
  240. {
  241. $coach = CoachUser::where('id', $coachId)
  242. ->where('state', 'enable')
  243. ->where('auth_state', 'passed')
  244. ->firstOrFail();
  245. // $config = CoachConfig::where('coach_id', $coachId)->firstOrFail();
  246. return [
  247. // 'delivery_fee_type' => $config->delivery_fee_type,
  248. // 'charge_delivery_fee' => $config->charge_delivery_fee
  249. ];
  250. }
  251. /**
  252. * 计算路费金额
  253. */
  254. public function calculateDeliveryFee($coachId, $agentId, $distance)
  255. {
  256. $coach = CoachUser::where('id', $coachId)
  257. ->where('state', 'enable')
  258. ->where('auth_state', 'passed')
  259. ->firstOrFail();
  260. $coachConfig = CoachConfig::where('coach_id', $coachId)->firstOrFail();
  261. if (!$coachConfig->charge_delivery_fee) {
  262. return 0;
  263. }
  264. $agentConfig = AgentConfig::where('agent_id', $agentId)->firstOrFail();
  265. $fee = 0;
  266. if ($distance <= $agentConfig->min_distance) {
  267. $fee = $agentConfig->min_fee;
  268. } else {
  269. $extraDistance = $distance - $agentConfig->min_distance;
  270. $fee = $agentConfig->min_fee + ($extraDistance * $agentConfig->per_km_fee);
  271. }
  272. return $coachConfig->delivery_fee_type == 'round_trip' ? $fee * 2 : $fee;
  273. }
  274. /**
  275. * 计算订单金额
  276. */
  277. public function calculateOrderAmount($userId, $addressId, $coachId, $projectId, $agentId, $useBalance = false)
  278. {
  279. // 参数校验
  280. $user = MemberUser::where('id', $userId)
  281. ->where('status', 1)
  282. ->firstOrFail();
  283. $address = MemberAddress::where('id', $addressId)
  284. ->where('user_id', $userId)
  285. ->firstOrFail();
  286. $coach = CoachUser::where('id', $coachId)
  287. ->where('state', 'enable')
  288. ->where('auth_state', 'passed')
  289. ->firstOrFail();
  290. $project = Project::where('id', $projectId)
  291. ->where('state', 'enable')
  292. ->firstOrFail();
  293. // 计算金额
  294. $projectAmount = $project->price;
  295. $deliveryFee = $this->calculateDeliveryFee($coachId, $agentId, $address->distance);
  296. $tipAmount = request()->has('order_id') ? Order::find(request()->input('order_id'))->tip_amount : 0;
  297. $couponAmount = 0;
  298. if (request()->has('coupon_id')) {
  299. // $coupon = Coupon::where('id', request()->input('coupon_id'))
  300. // ->where('state', 'enable')
  301. // ->firstOrFail();
  302. // $couponAmount = $coupon->amount;
  303. }
  304. $totalAmount = $projectAmount + $deliveryFee + $tipAmount - $couponAmount;
  305. $balanceAmount = 0;
  306. $payAmount = $totalAmount;
  307. if ($useBalance) {
  308. $wallet = Wallet::where('user_id', $userId)->first();
  309. if ($wallet && $wallet->balance >= $totalAmount) {
  310. $balanceAmount = $totalAmount;
  311. $payAmount = 0;
  312. } else if ($wallet) {
  313. $balanceAmount = $wallet->balance;
  314. $payAmount = $totalAmount - $balanceAmount;
  315. }
  316. }
  317. return [
  318. 'total_amount' => $totalAmount,
  319. 'balance_amount' => $balanceAmount,
  320. 'pay_amount' => $payAmount,
  321. 'coupon_amount' => $couponAmount,
  322. 'tip_amount' => $tipAmount,
  323. 'project_amount' => $projectAmount,
  324. 'delivery_fee' => $deliveryFee
  325. ];
  326. }
  327. /**
  328. * 创建订单
  329. */
  330. public function createOrder($userId, $projectId, $addressId, $coachId, $useBalance, $orderId = null)
  331. {
  332. return DB::transaction(function() use ($userId, $projectId, $addressId, $coachId, $useBalance, $orderId) {
  333. // 参数校验
  334. $user = MemberUser::where('id', $userId)
  335. ->where('status', 1)
  336. ->firstOrFail();
  337. $address = MemberAddress::where('id', $addressId)
  338. ->where('user_id', $userId)
  339. ->firstOrFail();
  340. if (!$orderId) {
  341. $coach = CoachUser::where('id', $coachId)
  342. ->where('state', 'enable')
  343. ->where('auth_state', 'passed')
  344. ->firstOrFail();
  345. }
  346. $project = Project::where('id', $projectId)
  347. ->where('state', 'enable')
  348. ->firstOrFail();
  349. // 创建订单
  350. $orderType = $orderId ? 'add_time' : 'normal';
  351. $amounts = $this->calculateOrderAmount($userId, $addressId, $coachId, $projectId, $project->agent_id, $useBalance);
  352. $order = new Order();
  353. $order->user_id = $userId;
  354. $order->project_id = $projectId;
  355. $order->address_id = $addressId;
  356. $order->coach_id = $coachId;
  357. $order->order_type = $orderType == 'normal' ? 0 : 1;
  358. $order->status = 0;
  359. $order->total_amount = $amounts['total_amount'];
  360. $order->balance_amount = $amounts['balance_amount'];
  361. $order->pay_amount = $amounts['pay_amount'];
  362. $order->coupon_amount = $amounts['coupon_amount'];
  363. $order->tip_amount = $amounts['tip_amount'];
  364. $order->project_amount = $amounts['project_amount'];
  365. $order->delivery_fee = $orderType == 'add_time' ? 0 : $amounts['delivery_fee'];
  366. $order->payment_type = $useBalance && $amounts['pay_amount'] == 0 ? 0 : 1;
  367. $order->save();
  368. // 创建订单历史
  369. if ($orderType == 'add_time') {
  370. OrderRecord::create([
  371. 'order_id' => $order->id,
  372. 'type' => 'add_time',
  373. 'user_id' => $userId
  374. ]);
  375. } else {
  376. OrderRecord::create([
  377. 'order_id' => $order->id,
  378. 'type' => 'create',
  379. 'user_id' => $userId
  380. ]);
  381. }
  382. // 余额支付处理
  383. if ($order->payment_type == 'balance') {
  384. $order->status = 'paid';
  385. $order->save();
  386. OrderRecord::create([
  387. 'order_id' => $order->id,
  388. 'type' => 'pay',
  389. 'user_id' => $userId
  390. ]);
  391. // CoachSchedule::create([
  392. // 'coach_id' => $coachId,
  393. // 'date' => date('Y-m-d'),
  394. // 'status' => 'busy'
  395. // ]);
  396. return $order->id;
  397. }
  398. // 微信支付处理
  399. // TODO: 调用支付接口
  400. return $order->id;
  401. });
  402. }
  403. /**
  404. * 加钟
  405. */
  406. public function addTime($userId, $orderId)
  407. {
  408. $order = Order::where('id', $orderId)
  409. ->where('user_id', $userId)
  410. ->firstOrFail();
  411. return $this->createOrder($userId, $order->project_id, $order->address_id, $order->coach_id, 0, $orderId);
  412. }
  413. /**
  414. * 指定技师
  415. */
  416. public function assignCoach($userId, $orderId, $coachId)
  417. {
  418. return DB::transaction(function() use ($userId, $orderId, $coachId) {
  419. // 参数校验
  420. $user = MemberUser::where('id', $userId)
  421. ->where('status', 'enable')
  422. ->firstOrFail();
  423. $order = Order::where('id', $orderId)
  424. ->where('user_id', $userId)
  425. ->whereIn('status', [0, 1, 6])
  426. ->firstOrFail();
  427. $coach = CoachUser::where('id', $coachId)
  428. ->where('state', 'enable')
  429. ->where('auth_state', 'passed')
  430. ->firstOrFail();
  431. // $schedule = CoachSchedule::where('coach_id', $coachId)
  432. // ->where('date', date('Y-m-d'))
  433. // ->where('status', 'free')
  434. // ->firstOrFail();
  435. // 修改订单
  436. $order->coach_id = $coachId;
  437. if ($order->status == 'created') {
  438. $amounts = $this->calculateOrderAmount($userId, $order->address_id, $coachId, $order->project_id, $order->agent_id, $order->payment_type == 'balance');
  439. $order->total_amount = $amounts['total_amount'];
  440. $order->balance_amount = $amounts['balance_amount'];
  441. $order->pay_amount = $amounts['pay_amount'];
  442. $order->coupon_amount = $amounts['coupon_amount'];
  443. $order->tip_amount = $amounts['tip_amount'];
  444. $order->project_amount = $amounts['project_amount'];
  445. $order->delivery_fee = $amounts['delivery_fee'];
  446. if ($order->payment_type == 'balance') {
  447. $order->status = 'paid';
  448. }
  449. }
  450. if ($order->status == 'paid') {
  451. $order->status = 'assigned';
  452. }
  453. $order->save();
  454. // 创建订单历史
  455. OrderRecord::create([
  456. 'order_id' => $order->id,
  457. 'type' => 'assigned',
  458. 'user_id' => $userId,
  459. 'coach_id' => $coachId
  460. ]);
  461. OrderRecord::create([
  462. 'order_id' => $order->id,
  463. 'type' => 'accepted',
  464. 'user_id' => $userId,
  465. 'coach_id' => $coachId,
  466. 'remark' => '抢单成功'
  467. ]);
  468. // 更新抢单池
  469. OrderGrabRecord::where('order_id', $orderId)
  470. ->update(['status' => 'success', 'coach_id' => $coachId]);
  471. // 更新排班
  472. // $schedule->status = 'busy';
  473. // $schedule->save();
  474. return true;
  475. });
  476. }
  477. }