|
@@ -14,7 +14,6 @@ use App\Enums\TechnicianStatus;
|
|
|
use App\Enums\TransactionType;
|
|
|
use App\Enums\UserStatus;
|
|
|
use App\Models\AgentInfo;
|
|
|
-use App\Models\CoachSchedule;
|
|
|
use App\Models\CoachUser;
|
|
|
use App\Models\MemberAddress;
|
|
|
use App\Models\MemberUser;
|
|
@@ -29,6 +28,7 @@ use App\Services\Client\Traits\HandlesPayments;
|
|
|
use App\Services\Client\Traits\ValidatesServiceTime;
|
|
|
use Carbon\Carbon;
|
|
|
use Exception;
|
|
|
+use Illuminate\Database\Eloquent\Collection;
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
@@ -255,8 +255,21 @@ readonly class OrderService
|
|
|
);
|
|
|
|
|
|
// 加钟订单使用原订单地址和技师ID
|
|
|
- if ($data['order_id']) {
|
|
|
- $order = Order::find($data['order_id']);
|
|
|
+ if (!empty($data['order_id'])) {
|
|
|
+ $order = Order::findOrFail($data['order_id']);
|
|
|
+
|
|
|
+ // 验证订单状态
|
|
|
+ abort_if(
|
|
|
+ !in_array($order->state, [
|
|
|
+ OrderStatus::PAID->value,
|
|
|
+ OrderStatus::ACCEPTED->value,
|
|
|
+ OrderStatus::SERVICE->value,
|
|
|
+ OrderStatus::SERVICE_ING->value,
|
|
|
+ ]),
|
|
|
+ 422,
|
|
|
+ '原订单状态不允许加钟'
|
|
|
+ );
|
|
|
+
|
|
|
$data['address_id'] = $order->address_id;
|
|
|
$data['coach_id'] = $order->coach_id;
|
|
|
$data['service_time'] = $order->service_end_time;
|
|
@@ -326,33 +339,10 @@ readonly class OrderService
|
|
|
->where('state', ProjectStatus::OPEN->value)
|
|
|
->firstOrFail();
|
|
|
|
|
|
- // 验证服务时间
|
|
|
- if (! empty($data['service_time'])) {
|
|
|
- $serviceTime = Carbon::parse($data['service_time']);
|
|
|
- abort_if(
|
|
|
- $serviceTime->isPast(),
|
|
|
- 400,
|
|
|
- '服务时间不能早于当前时间'
|
|
|
- );
|
|
|
-
|
|
|
- // 如果指定了技师,验证服务时间是否可用
|
|
|
- if (! empty($data['coach_id'])) {
|
|
|
- $this->validateServiceTime($data['coach_id'], $data['service_time']);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 验证技师状态(如果有)
|
|
|
- if (! empty($data['coach_id'])) {
|
|
|
- $this->validateCoach($data['coach_id']);
|
|
|
+ // 验证服务时间和技师状态
|
|
|
+ if (! empty($data['service_time']) && ! empty($data['coach_id'])) {
|
|
|
+ $this->validateServiceTimeParams($data['coach_id'], $data['service_time']);
|
|
|
}
|
|
|
-
|
|
|
- // TODO: 验证代理商状态(如果有)
|
|
|
- // 验证代理商状态(如果有)
|
|
|
- // if (! empty($data['agent_id'])) {
|
|
|
- // $agent = AgentInfo::where('id', $data['agent_id'])
|
|
|
- // ->where('state', AgentStatus::OPEN->value)
|
|
|
- // ->firstOrFail();
|
|
|
- // }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -460,7 +450,7 @@ readonly class OrderService
|
|
|
*/
|
|
|
private function notifyOrderCreated(Order $order): void
|
|
|
{
|
|
|
- // TODO: 实现订单创建通知逻辑
|
|
|
+ // TODO: 实现订单创建通������
|
|
|
// 1. 通知用户
|
|
|
// event(new OrderCreatedEvent($order));
|
|
|
|
|
@@ -510,32 +500,20 @@ readonly class OrderService
|
|
|
&& $orderType !== OrderType::GRAB;
|
|
|
}
|
|
|
|
|
|
- // 提取方法:验证技师
|
|
|
- public function validateCoach(int $coachId): CoachUser
|
|
|
+ /**
|
|
|
+ * 验证技师状态
|
|
|
+ */
|
|
|
+ private function validateCoach(int $coachId): CoachUser
|
|
|
{
|
|
|
- // 查询技师基本信息
|
|
|
$coach = CoachUser::query()
|
|
|
- ->with(['info', 'qual', 'real'])
|
|
|
+ ->with(['info'])
|
|
|
->where('id', $coachId)
|
|
|
+ ->where('state', TechnicianStatus::ACTIVE->value)
|
|
|
->first();
|
|
|
|
|
|
- // 验证技师是否存在
|
|
|
- abort_if(! $coach, 400, '技师不存在');
|
|
|
-
|
|
|
- // 验证技师状态
|
|
|
- abort_if($coach->state !== TechnicianStatus::ACTIVE->value, 400, '技师未激活');
|
|
|
-
|
|
|
- // 验证基本信息认证
|
|
|
+ abort_if(! $coach, 400, '技师不存在或未激活');
|
|
|
abort_if(! $coach->info || $coach->info->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
- 400, '技师基本信息未认证');
|
|
|
-
|
|
|
- // 验证资质认证
|
|
|
- abort_if(! $coach->qual || $coach->qual->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
- 400, '技师资质未认证');
|
|
|
-
|
|
|
- // 验证实名认证
|
|
|
- abort_if(! $coach->real || $coach->real->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
- 400, '技师实名未认证');
|
|
|
+ 400, '技师未通过认证');
|
|
|
|
|
|
return $coach;
|
|
|
}
|
|
@@ -784,7 +762,7 @@ readonly class OrderService
|
|
|
// 1. 验证用户和订单
|
|
|
$order = $this->validateOrderForFinish($userId, $orderId);
|
|
|
|
|
|
- // 2. 验证技师状态
|
|
|
+ // 2. 验证技师状���
|
|
|
$coach = $this->validateCoach($order->coach_id);
|
|
|
|
|
|
// 4. 完成订单
|
|
@@ -991,7 +969,7 @@ readonly class OrderService
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 格式化订单列表项
|
|
|
+ * �������化订��列表项
|
|
|
*/
|
|
|
private function formatOrderListItem(Order $order): array
|
|
|
{
|
|
@@ -1278,7 +1256,7 @@ readonly class OrderService
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // 计算实际可退金额(总金额减去违约金)
|
|
|
+ // 计算实际可��金额(总金额减去违约金)
|
|
|
$totalRefund = bcsub($order->total_amount, $deductAmount, 2);
|
|
|
|
|
|
// 优先退还用户使用的余额部分
|
|
@@ -1598,7 +1576,7 @@ readonly class OrderService
|
|
|
* @param float $distance 距离(公里)
|
|
|
* @param array{
|
|
|
* min_distance: float, // 最小计费距离(公里)
|
|
|
- * min_fee: float, // 最小路费(元)
|
|
|
+ * min_fee: float, // ���小路费(元)
|
|
|
* per_km_fee: float, // 每公里费用(元)
|
|
|
* max_distance: float, // 最大服务距离(公里)
|
|
|
* max_fee: float, // 最大路费(元)
|
|
@@ -1782,7 +1760,7 @@ readonly class OrderService
|
|
|
->where('state', ProjectStatus::OPEN->value)
|
|
|
->first();
|
|
|
|
|
|
- abort_if(! $project, 404, '项目不存在或状态异常');
|
|
|
+ abort_if(! $project, 404, '项目不���在或���态异常');
|
|
|
|
|
|
// 如果有代理商,获取代理商价格
|
|
|
if ($agentId) {
|
|
@@ -1800,7 +1778,7 @@ readonly class OrderService
|
|
|
*/
|
|
|
private function getAgentProjectPrice(int $agentId, int $projectId): ?object
|
|
|
{
|
|
|
- return DB::table('agent_projects')
|
|
|
+ return DB::table('agent_project')
|
|
|
->where('agent_id', $agentId)
|
|
|
->where('project_id', $projectId)
|
|
|
->where('state', 'enable')
|
|
@@ -1898,7 +1876,7 @@ readonly class OrderService
|
|
|
}
|
|
|
|
|
|
// 如果有有效距离,返回最小值;否则返回0
|
|
|
- return !empty($distances) ? min($distances) : 0.0;
|
|
|
+ return ! empty($distances) ? min($distances) : 0.0;
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
Log::error('计算距离失败', [
|
|
@@ -1908,6 +1886,7 @@ readonly class OrderService
|
|
|
'lat' => $lat,
|
|
|
'lng' => $lng,
|
|
|
]);
|
|
|
+
|
|
|
return 0.0;
|
|
|
}
|
|
|
}
|
|
@@ -1974,7 +1953,7 @@ readonly class OrderService
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
- // 查询订单信息
|
|
|
+ // 查询��单信息
|
|
|
$order = Order::where('id', $orderId)
|
|
|
->whereIn('state', [OrderStatus::CREATED->value])
|
|
|
->firstOrFail();
|
|
@@ -2354,81 +2333,6 @@ readonly class OrderService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 验证服务时间参数
|
|
|
- */
|
|
|
- private function validateServiceTimeParams(int $coachId, string $serviceTime): void
|
|
|
- {
|
|
|
- // 验证技师状态是否正常
|
|
|
- $coach = CoachUser::where('id', $coachId)
|
|
|
- ->where('state', TechnicianStatus::ACTIVE->value)
|
|
|
- ->where('auth_state', TechnicianAuthStatus::PASSED->value)
|
|
|
- ->firstOrFail();
|
|
|
-
|
|
|
- // 验证时间格式并检查是否早于当前时间
|
|
|
- $serviceDateTime = Carbon::parse($serviceTime);
|
|
|
- abort_if(
|
|
|
- $serviceDateTime->isPast(),
|
|
|
- 400,
|
|
|
- '服务时间不能早于当前时间'
|
|
|
- );
|
|
|
-
|
|
|
- // 验证预约提前时间(至少提前指定小时)
|
|
|
- $minAdvanceHours = config('business.min_advance_hours', 2);
|
|
|
- abort_if(
|
|
|
- $serviceDateTime->diffInHours(now()) < $minAdvanceHours,
|
|
|
- 400,
|
|
|
- "需要至少提前{$minAdvanceHours}小时预约"
|
|
|
- );
|
|
|
-
|
|
|
- // 验证预约时间范围(最多提前指定天数)
|
|
|
- $maxAdvanceDays = config('business.max_advance_days', 7);
|
|
|
- abort_if(
|
|
|
- $serviceDateTime->diffInDays(now()) > $maxAdvanceDays,
|
|
|
- 400,
|
|
|
- "最多只能提前{$maxAdvanceDays}天预约"
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 验证是否在工作时间内
|
|
|
- */
|
|
|
- private function validateWorkingHours(string $serviceTime, array $workSchedule): void
|
|
|
- {
|
|
|
- $serviceDateTime = Carbon::parse($serviceTime);
|
|
|
-
|
|
|
- // 检查是否为工作日
|
|
|
- $dayOfWeek = $serviceDateTime->dayOfWeek;
|
|
|
- abort_if(
|
|
|
- ! in_array($dayOfWeek, $workSchedule['work_days']),
|
|
|
- 400,
|
|
|
- '该时间不在技师工作日内'
|
|
|
- );
|
|
|
-
|
|
|
- // 检查是否为特殊休息日
|
|
|
- $dateStr = $serviceDateTime->format('Y-m-d');
|
|
|
- abort_if(
|
|
|
- in_array($dateStr, $workSchedule['rest_dates']),
|
|
|
- 400,
|
|
|
- '技师该日期休息'
|
|
|
- );
|
|
|
-
|
|
|
- // 检查是否在工作时间范围内
|
|
|
- $timeStr = $serviceDateTime->format('H:i');
|
|
|
- $startTime = Carbon::parse($workSchedule['work_hours']['start']);
|
|
|
- $endTime = Carbon::parse($workSchedule['work_hours']['end']);
|
|
|
-
|
|
|
- abort_if(
|
|
|
- $timeStr < $startTime->format('H:i') || $timeStr > $endTime->format('H:i'),
|
|
|
- 400,
|
|
|
- sprintf(
|
|
|
- '服务时间需在%s-%s之间',
|
|
|
- $startTime->format('H:i'),
|
|
|
- $endTime->format('H:i')
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 检查订单时间冲突
|
|
|
*/
|
|
@@ -2551,43 +2455,6 @@ readonly class OrderService
|
|
|
return $order;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 创建评价记录
|
|
|
- */
|
|
|
- // private function createEvaluation(Order $order, array $data): OrderEvaluation
|
|
|
- // {
|
|
|
- // // 创建评价记录
|
|
|
- // $evaluation = $order->evaluation()->create([
|
|
|
- // 'user_id' => $order->user_id,
|
|
|
- // 'coach_id' => $order->coach_id,
|
|
|
- // 'project_id' => $order->project_id,
|
|
|
- // 'score' => $data['score'],
|
|
|
- // 'content' => $data['content'],
|
|
|
- // 'images' => $data['images'] ?? [],
|
|
|
- // 'state' => 'normal',
|
|
|
- // ]);
|
|
|
-
|
|
|
- // // 创建评价标签
|
|
|
- // if (! empty($data['tags'])) {
|
|
|
- // // TODO: 创建评价标签
|
|
|
- // $this->createEvaluationTags($evaluation, $data['tags']);
|
|
|
- // }
|
|
|
-
|
|
|
- // return $evaluation;
|
|
|
- // }
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建评价标签
|
|
|
- */
|
|
|
- // private function createEvaluationTags(OrderEvaluation $evaluation, array $tags): void
|
|
|
- // {
|
|
|
- // $tagIds = EvaluationTag::whereIn('id', $tags)
|
|
|
- // ->where('state', 'enable')
|
|
|
- // ->pluck('id');
|
|
|
-
|
|
|
- // $evaluation->tags()->attach($tagIds);
|
|
|
- // }
|
|
|
-
|
|
|
/**
|
|
|
* 更新技师评分
|
|
|
*/
|
|
@@ -2667,36 +2534,30 @@ readonly class OrderService
|
|
|
// // Cache::tags(['coach_evaluations'])->forget("coach:{$order->coach_id}");
|
|
|
// }
|
|
|
|
|
|
- /**
|
|
|
- * 获取技师工作时间安排
|
|
|
- */
|
|
|
- private function getCoachWorkSchedule(int $coachId): array
|
|
|
+ private function getNearbyCoaches(float $lat, float $lng, float $radius = 5.0): Collection
|
|
|
+ {
|
|
|
+ // 获取在线技师列表
|
|
|
+ return CoachUser::query()
|
|
|
+ ->with(['info'])
|
|
|
+ ->where('state', TechnicianStatus::ACTIVE->value)
|
|
|
+ ->whereHas('info', fn ($q) => $q->where('state', TechnicianAuthStatus::PASSED->value))
|
|
|
+ ->get();
|
|
|
+ }
|
|
|
+
|
|
|
+ private function validateCoachForGrab(int $coachId): CoachUser
|
|
|
{
|
|
|
- // 查询技师排班信息
|
|
|
- $schedule = CoachSchedule::where('coach_id', $coachId)
|
|
|
- ->where('state', 1)
|
|
|
+ $coach = CoachUser::query()
|
|
|
+ ->with(['info'])
|
|
|
+ ->where('id', $coachId)
|
|
|
+ ->where('state', TechnicianStatus::ACTIVE->value)
|
|
|
->first();
|
|
|
|
|
|
- // 如果没有排班信息,返回默认工作时间
|
|
|
- if (! $schedule) {
|
|
|
- return [
|
|
|
- 'work_days' => range(1, 7), // 默认每天工作
|
|
|
- 'work_hours' => [
|
|
|
- 'start' => '09:00', // 默认早9点开始
|
|
|
- 'end' => '21:00', // 默认晚9点结束
|
|
|
- ],
|
|
|
- 'rest_dates' => [], // 默认无休息日
|
|
|
- ];
|
|
|
- }
|
|
|
+ abort_if(! $coach, 400, '技师不存在或未激活');
|
|
|
|
|
|
- // 返回技师的实际排班信息
|
|
|
- return [
|
|
|
- 'work_days' => json_decode($schedule->work_days, true),
|
|
|
- 'work_hours' => [
|
|
|
- 'start' => $schedule->work_start_time,
|
|
|
- 'end' => $schedule->work_end_time,
|
|
|
- ],
|
|
|
- 'rest_dates' => json_decode($schedule->rest_dates, true) ?? [],
|
|
|
- ];
|
|
|
+ // 验证技师认证状态
|
|
|
+ abort_if(! $coach->info || $coach->info->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
+ 400, '技师未通过认证');
|
|
|
+
|
|
|
+ return $coach;
|
|
|
}
|
|
|
}
|