|
@@ -5,9 +5,7 @@ namespace App\Services\Client;
|
|
|
use App\Models\AgentConfig;
|
|
|
use App\Models\AgentInfo;
|
|
|
use App\Models\CoachConfig;
|
|
|
-use App\Models\CoachSchedule;
|
|
|
use App\Models\CoachUser;
|
|
|
-use App\Models\Coupon;
|
|
|
use App\Models\MemberUser;
|
|
|
use App\Models\Order;
|
|
|
use App\Models\OrderGrabRecord;
|
|
@@ -40,68 +38,66 @@ class OrderService
|
|
|
*/
|
|
|
public function initialize(int $userId, array $data): array
|
|
|
{
|
|
|
- $user = MemberUser::find($userId);
|
|
|
+ try {
|
|
|
+ // 参数验证
|
|
|
+ abort_if(empty($data), 400, '订单初始化参数不能为空');
|
|
|
|
|
|
- if (! $user) {
|
|
|
- throw new Exception('用户不存在');
|
|
|
- }
|
|
|
+ DB::beginTransaction();
|
|
|
+ try {
|
|
|
+ $user = MemberUser::find($userId);
|
|
|
+ abort_if(! $user || $user->state != 'enable', 400, '用户状态异常');
|
|
|
|
|
|
- if ($user->state != 'enable') {
|
|
|
- throw new Exception('用户状态异常');
|
|
|
- }
|
|
|
+ // 查询用户钱包
|
|
|
+ $wallet = $user->wallet;
|
|
|
|
|
|
- // 查询用户钱包
|
|
|
- $wallet = $user->wallet;
|
|
|
-
|
|
|
- // 查询默认地址
|
|
|
- $address = $user->address;
|
|
|
-
|
|
|
- $areaCode = $address ? $address->area_code : $data['area_code'];
|
|
|
-
|
|
|
- // 查询技师数据
|
|
|
- $coach = CoachUser::where('state', 'enable')
|
|
|
- ->whereHas('info', function ($query) {
|
|
|
- $query->where('state', 'approved');
|
|
|
- })
|
|
|
- ->whereHas('real', function ($query) {
|
|
|
- $query->where('state', 'approved');
|
|
|
- })
|
|
|
- ->whereHas('qual', function ($query) {
|
|
|
- $query->where('state', 'approved');
|
|
|
- })
|
|
|
- ->with(['info:id,nickname,avatar,gender'])
|
|
|
- ->find($data['coach_id']);
|
|
|
-
|
|
|
- if (! $coach) {
|
|
|
- throw new Exception('技师不存在');
|
|
|
- }
|
|
|
+ // 查询默认地址
|
|
|
+ $address = $user->address;
|
|
|
|
|
|
- // 查询技师排班
|
|
|
- // $schedule = CoachSchedule::where('coach_id', $coachId)
|
|
|
- // ->where('date', date('Y-m-d'))
|
|
|
- // ->first();
|
|
|
+ $areaCode = $address ? $address->area_code : $data['area_code'];
|
|
|
|
|
|
- // // 查询用户优惠券
|
|
|
- // $coupons = Coupon::where('user_id', $userId)
|
|
|
- // ->where('state', 'enable')
|
|
|
- // ->where('expire_time', '>', now())
|
|
|
- // ->get();
|
|
|
+ // 查询技师数据
|
|
|
+ $coach = $this->validateCoach($data['coach_id']);
|
|
|
|
|
|
- // 获取项目详情
|
|
|
- $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
|
|
|
+ // 查询技师排班
|
|
|
+ // $schedule = CoachSchedule::where('coach_id', $coachId)
|
|
|
+ // ->where('date', date('Y-m-d'))
|
|
|
+ // ->first();
|
|
|
|
|
|
- // 计算订单金额
|
|
|
- $amounts = $this->calculateOrderAmount($userId, $address?->id, $data['coach_id'], $data['project_id'], $project?->agent_id);
|
|
|
+ // // 查询用户优惠券
|
|
|
+ // $coupons = Coupon::where('user_id', $userId)
|
|
|
+ // ->where('state', 'enable')
|
|
|
+ // ->where('expire_time', '>', now())
|
|
|
+ // ->get();
|
|
|
|
|
|
- return [
|
|
|
- 'wallet' => $wallet,
|
|
|
- 'coach' => $coach,
|
|
|
- 'project' => $project,
|
|
|
- 'address' => $address,
|
|
|
- // 'schedule' => $schedule,
|
|
|
- 'amounts' => $amounts,
|
|
|
- // 'coupons' => $coupons
|
|
|
- ];
|
|
|
+ // 获取项目详情
|
|
|
+ $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
|
|
|
+
|
|
|
+ // 计算订单金额
|
|
|
+ $amounts = $this->calculateOrderAmount($userId, $address?->id, $data['coach_id'], $data['project_id'], $project?->agent_id);
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'wallet' => $wallet,
|
|
|
+ 'coach' => $coach,
|
|
|
+ 'project' => $project,
|
|
|
+ 'address' => $address,
|
|
|
+ // 'schedule' => $schedule,
|
|
|
+ 'amounts' => $amounts,
|
|
|
+ // 'coupons' => $coupons
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('订单初始化失败', [
|
|
|
+ 'userId' => $userId,
|
|
|
+ 'data' => $data,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -123,10 +119,25 @@ class OrderService
|
|
|
// 2. 创建订单
|
|
|
$orderType = isset($data['order_id']) ? 'add_time' : 'normal';
|
|
|
|
|
|
+ // 关键操作:验证必要参数
|
|
|
+ abort_if(empty($data['project_id']), 400, '项目ID不能为空');
|
|
|
+ abort_if(empty($data['service_time']), 400, '服务时间不能为空');
|
|
|
+ abort_if($orderType == 'normal' && empty($data['coach_id']), 400, '技师ID不能为空');
|
|
|
+ abort_if($orderType == 'normal' && empty($data['address_id']), 400, '地址ID不能为空');
|
|
|
+
|
|
|
if ($orderType == 'normal') {
|
|
|
+ // 普通订单验证技师
|
|
|
$coach = $this->validateCoach($data['coach_id']);
|
|
|
} else {
|
|
|
+ // 加钟订单验证
|
|
|
$originalOrder = $this->getOriginalOrder($user, $data['order_id']);
|
|
|
+
|
|
|
+ // 关键操作:验证原订单技师状态
|
|
|
+ $coach = $this->validateCoach($originalOrder->coach_id);
|
|
|
+
|
|
|
+ // 关键操作:验证原订单状态
|
|
|
+ abort_if(! in_array($originalOrder->state, ['service_ing', 'service_end']), 400, '原订单状态不允许加钟');
|
|
|
+
|
|
|
$data = $this->prepareAddTimeData($originalOrder, $data);
|
|
|
}
|
|
|
|
|
@@ -137,6 +148,15 @@ class OrderService
|
|
|
// 计算订单金额
|
|
|
$amounts = $this->calculateOrderAmount($userId, $address->id, $data['coach_id'], $data['project_id'], $project->agent_id, $data['use_balance'] ?? false);
|
|
|
|
|
|
+ // 关键操作:验证订单金额
|
|
|
+ abort_if($amounts['total_amount'] <= 0, 400, '订单金额异常');
|
|
|
+
|
|
|
+ // 关键操作:验证余额支付
|
|
|
+ if ($amounts['payment_type'] == 'balance') {
|
|
|
+ $wallet = $user->wallet;
|
|
|
+ abort_if($wallet->available_balance < $amounts['balance_amount'], 400, '可用余额不足');
|
|
|
+ }
|
|
|
+
|
|
|
// 创建订单记录
|
|
|
$order = $this->createOrderRecord($userId, $data, $orderType, $address, $amounts);
|
|
|
|
|
@@ -161,7 +181,7 @@ class OrderService
|
|
|
}
|
|
|
|
|
|
// 提取方法:验证技师
|
|
|
- private function validateCoach($coachId): CoachUser
|
|
|
+ public function validateCoach(int $coachId): CoachUser
|
|
|
{
|
|
|
$coach = CoachUser::where('id', $coachId)
|
|
|
->where('state', 'enable')
|
|
@@ -263,53 +283,99 @@ class OrderService
|
|
|
{
|
|
|
return DB::transaction(function () use ($userId, $orderId) {
|
|
|
try {
|
|
|
- $user = MemberUser::where('id', $userId)->firstOrFail();
|
|
|
- $order = $user->orders()->find($orderId);
|
|
|
+ // 1. 验证用户和订单
|
|
|
+ $order = $this->validateOrderForCancel($userId, $orderId);
|
|
|
|
|
|
- if (! $order) {
|
|
|
- throw new Exception('订单不存在');
|
|
|
- }
|
|
|
+ // 2. 处理退款
|
|
|
+ $this->handleCancelRefund($order);
|
|
|
|
|
|
- // 判断订单状态
|
|
|
- if ($order->state == 'wait_receive') { // 已接单
|
|
|
- // 扣除20%费用
|
|
|
- $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.2;
|
|
|
- $this->handleRefund($user, $order, $deductAmount, false);
|
|
|
- } elseif ($order->state == 'on_the_way') { // 已出发
|
|
|
- // 扣除50%费用并扣除路费
|
|
|
- $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.5;
|
|
|
- $this->handleRefund($user, $order, $deductAmount, true);
|
|
|
- } elseif ($order->state == 'wait_pay') {
|
|
|
- // 待支付状态直接取消,无需退款
|
|
|
- } else {
|
|
|
- throw new Exception('当前订单状态不允许取消');
|
|
|
- }
|
|
|
-
|
|
|
- // 添加订单取消记录
|
|
|
- OrderRecord::create([
|
|
|
- 'order_id' => $orderId,
|
|
|
- 'object_id' => $userId,
|
|
|
- 'object_type' => MemberUser::class,
|
|
|
- 'state' => 'cancel',
|
|
|
- 'remark' => '用户取消订单',
|
|
|
- ]);
|
|
|
-
|
|
|
- // 修改订单状态
|
|
|
- $order->state = 'cancel';
|
|
|
- $order->save();
|
|
|
+ // 3. 完成订单取消
|
|
|
+ $this->completeCancel($order, $userId);
|
|
|
|
|
|
return ['message' => '订单已取消'];
|
|
|
+
|
|
|
} catch (Exception $e) {
|
|
|
- Log::error('取消订单失败:', [
|
|
|
- 'message' => $e->getMessage(),
|
|
|
- 'user_id' => $userId,
|
|
|
- 'order_id' => $orderId,
|
|
|
- ]);
|
|
|
+ $this->logCancelOrderError($e, $userId, $orderId);
|
|
|
throw $e;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 验证订单取消条件
|
|
|
+ */
|
|
|
+ private function validateOrderForCancel(int $userId, int $orderId): Order
|
|
|
+ {
|
|
|
+ // 复用之前的用户验证逻辑
|
|
|
+ $user = MemberUser::where('id', $userId)
|
|
|
+ ->where('state', 'enable')
|
|
|
+ ->firstOrFail();
|
|
|
+
|
|
|
+ // 验证订单状态
|
|
|
+ $order = Order::where('user_id', $userId)
|
|
|
+ ->where('id', $orderId)
|
|
|
+ ->whereIn('state', ['wait_pay', 'wait_receive', 'on_the_way'])
|
|
|
+ ->lockForUpdate()
|
|
|
+ ->firstOrFail();
|
|
|
+
|
|
|
+ return $order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理订单取消退款
|
|
|
+ */
|
|
|
+ private function handleCancelRefund(Order $order): void
|
|
|
+ {
|
|
|
+ $user = $order->user;
|
|
|
+
|
|
|
+ switch ($order->state) {
|
|
|
+ case 'wait_receive': // 已接单
|
|
|
+ // 扣除20%费用
|
|
|
+ $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.2;
|
|
|
+ $this->handleRefund($user, $order, $deductAmount, false);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'on_the_way': // 已出发
|
|
|
+ // 扣除50%费用并扣除路费
|
|
|
+ $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.5;
|
|
|
+ $this->handleRefund($user, $order, $deductAmount, true);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'wait_pay':
|
|
|
+ // 待支付状态直接取消,无需退款
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ abort(400, '当前订单状态不允许取消');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 完成订单取消
|
|
|
+ */
|
|
|
+ private function completeCancel(Order $order, int $userId): void
|
|
|
+ {
|
|
|
+ // 添加订单取消记录
|
|
|
+ OrderRecord::create([
|
|
|
+ 'order_id' => $order->id,
|
|
|
+ 'object_id' => $userId,
|
|
|
+ 'object_type' => MemberUser::class,
|
|
|
+ 'state' => 'cancel',
|
|
|
+ 'remark' => '用户取消订单',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 修改订单状态
|
|
|
+ $order->state = 'cancel';
|
|
|
+ $order->cancel_time = now(); // 添加取消时间
|
|
|
+ $order->save();
|
|
|
+
|
|
|
+ // 如果有技师,可能需要通知技师订单已取消
|
|
|
+ if ($order->coach_id) {
|
|
|
+ // TODO: 发送通知给技师
|
|
|
+ // event(new OrderCancelledEvent($order));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 处理退款
|
|
|
*/
|
|
@@ -342,12 +408,15 @@ class OrderService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 提取方法:创建退款记录
|
|
|
+ /**
|
|
|
+ * 创建退款记录
|
|
|
+ */
|
|
|
private function createRefundRecords($user, $order, $amount, $type = 'balance'): void
|
|
|
{
|
|
|
$refundMethod = $type;
|
|
|
$remark = $type == 'balance' ? '订单取消退还余额' : '订单取消退还支付金额';
|
|
|
|
|
|
+ // 创建退款记录
|
|
|
$refundRecord = $user->wallet->refundRecords()->create([
|
|
|
'refund_method' => $refundMethod,
|
|
|
'total_refund_amount' => $order->payment_amount + $order->balance_amount,
|
|
@@ -358,6 +427,7 @@ class OrderService
|
|
|
'order_id' => $order->id,
|
|
|
]);
|
|
|
|
|
|
+ // 创建交易记录
|
|
|
$user->wallet->transRecords()->create([
|
|
|
'amount' => $amount,
|
|
|
'owner_type' => get_class($refundRecord),
|
|
@@ -373,11 +443,26 @@ class OrderService
|
|
|
'state' => 'success',
|
|
|
]);
|
|
|
|
|
|
+ // 更新钱包余额
|
|
|
$user->wallet->increment('total_balance', $amount);
|
|
|
$user->wallet->increment('available_balance', $amount);
|
|
|
$user->wallet->save();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 记录订单取消错误日志
|
|
|
+ */
|
|
|
+ private function logCancelOrderError(Exception $e, int $userId, int $orderId): void
|
|
|
+ {
|
|
|
+ // 复用之前的日志记录方法
|
|
|
+ Log::error('取消订单失败:', [
|
|
|
+ 'message' => $e->getMessage(),
|
|
|
+ 'user_id' => $userId,
|
|
|
+ 'order_id' => $orderId,
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 结束订单
|
|
|
*/
|
|
@@ -385,41 +470,95 @@ class OrderService
|
|
|
{
|
|
|
return DB::transaction(function () use ($userId, $orderId) {
|
|
|
try {
|
|
|
- // 关键操作:参数校验
|
|
|
- $order = Order::where('user_id', $userId)
|
|
|
- ->where('id', $orderId)
|
|
|
- ->where('state', 'service_ing') // 订单状态必须是服务中
|
|
|
- ->firstOrFail();
|
|
|
+ // 1. 验证用户和订单
|
|
|
+ $order = $this->validateOrderForFinish($userId, $orderId);
|
|
|
|
|
|
- if (! $order) {
|
|
|
- throw new Exception('订单不能结束');
|
|
|
- }
|
|
|
+ // 2. 验证技师状态
|
|
|
+ $this->validateCoach($order->coach_id);
|
|
|
|
|
|
- // 关键操作:创建订单历史记录
|
|
|
- OrderRecord::create([
|
|
|
- 'order_id' => $orderId,
|
|
|
- 'object_id' => $userId,
|
|
|
- 'object_type' => MemberUser::class,
|
|
|
- 'state' => 'finish',
|
|
|
- 'remark' => '服务完成',
|
|
|
- ]);
|
|
|
+ // 3. 验证服务时长
|
|
|
+ $this->validateServiceDuration($order);
|
|
|
|
|
|
- // 关键操作:修改订单状态为服务结束
|
|
|
- $order->state = 'service_end';
|
|
|
- $order->save();
|
|
|
+ // 4. 完成订单
|
|
|
+ $this->completeOrder($order, $userId);
|
|
|
|
|
|
return ['message' => '订单已完成'];
|
|
|
+
|
|
|
} catch (Exception $e) {
|
|
|
- Log::error('结束订单失败:', [
|
|
|
- 'message' => $e->getMessage(),
|
|
|
- 'user_id' => $userId,
|
|
|
- 'order_id' => $orderId,
|
|
|
- ]);
|
|
|
+ $this->logFinishOrderError($e, $userId, $orderId);
|
|
|
throw $e;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 验证订单完成条件
|
|
|
+ */
|
|
|
+ private function validateOrderForFinish(int $userId, int $orderId): Order
|
|
|
+ {
|
|
|
+ // 验证用户状态
|
|
|
+ $user = MemberUser::where('id', $userId)
|
|
|
+ ->where('state', 'enable')
|
|
|
+ ->firstOrFail();
|
|
|
+
|
|
|
+ // 验证订单状态
|
|
|
+ $order = Order::where('user_id', $userId)
|
|
|
+ ->where('id', $orderId)
|
|
|
+ ->where('state', 'service_ing')
|
|
|
+ ->lockForUpdate()
|
|
|
+ ->firstOrFail();
|
|
|
+
|
|
|
+ return $order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证服务时长
|
|
|
+ */
|
|
|
+ private function validateServiceDuration(Order $order): void
|
|
|
+ {
|
|
|
+ // 计算服务时长
|
|
|
+ $serviceStartTime = $order->service_start_time ?? $order->created_at;
|
|
|
+ $serviceDuration = now()->diffInMinutes($serviceStartTime);
|
|
|
+
|
|
|
+ // 获取项目要求的最短服务时长
|
|
|
+ $minDuration = $order->project->duration ?? 0;
|
|
|
+
|
|
|
+ abort_if($serviceDuration < $minDuration, 400, "服务时长不足{$minDuration}分钟");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 完成订单
|
|
|
+ */
|
|
|
+ private function completeOrder(Order $order, int $userId): void
|
|
|
+ {
|
|
|
+ // 1. 创建订单记录
|
|
|
+ OrderRecord::create([
|
|
|
+ 'order_id' => $order->id,
|
|
|
+ 'object_id' => $userId,
|
|
|
+ 'object_type' => MemberUser::class,
|
|
|
+ 'state' => 'finish',
|
|
|
+ 'remark' => '服务完成',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 2. 更新订单状态
|
|
|
+ $order->state = 'service_end';
|
|
|
+ $order->finish_time = now();
|
|
|
+ $order->save();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录订单完成错误日志
|
|
|
+ */
|
|
|
+ private function logFinishOrderError(Exception $e, int $userId, int $orderId): void
|
|
|
+ {
|
|
|
+ Log::error('结束订单失败:', [
|
|
|
+ 'message' => $e->getMessage(),
|
|
|
+ 'user_id' => $userId,
|
|
|
+ 'order_id' => $orderId,
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 确认技师离开
|
|
|
*/
|
|
@@ -450,7 +589,7 @@ class OrderService
|
|
|
$order->state = 'leave';
|
|
|
$order->save();
|
|
|
|
|
|
- return ['message' => '已确认技师离开'];
|
|
|
+ return ['message' => '已确技师离开'];
|
|
|
} catch (Exception $e) {
|
|
|
Log::error('确认技师离开失败:', [
|
|
|
'message' => $e->getMessage(),
|
|
@@ -699,11 +838,7 @@ class OrderService
|
|
|
abort_if(! $user || $user->state != 'enable', 404, '用户不存在或状态异常');
|
|
|
|
|
|
// 2. 查询技师项目
|
|
|
- $coach = CoachUser::where('state', 'enable')
|
|
|
- ->whereHas('info', fn ($q) => $q->where('state', 'approved'))
|
|
|
- ->whereHas('real', fn ($q) => $q->where('state', 'approved'))
|
|
|
- ->whereHas('qual', fn ($q) => $q->where('state', 'approved'))
|
|
|
- ->find($coachId);
|
|
|
+ $coach = $this->validateCoach($coachId);
|
|
|
|
|
|
abort_if(! $coach, 404, '技师不存在或状态异常');
|
|
|
|