db = Db::connection(); } /** * 创建出差申请(草稿) */ public function createDraft(int $userId, array $data): BusinessTrip { return $this->db->transaction(function () use ($userId, $data) { try { // 计算最早和最晚时间 $itineraries = $data['itineraries'] ?? []; if (empty($itineraries)) { throw new \Exception('请至少添加一条行程'); } $startDate = null; $endDate = null; $destinations = []; foreach ($itineraries as $itinerary) { if ($itinerary['start_time']) { if (!$startDate || $itinerary['start_time'] < $startDate) { $startDate = $itinerary['start_time']; } } if ($itinerary['end_time']) { if (!$endDate || $itinerary['end_time'] > $endDate) { $endDate = $itinerary['end_time']; } } if (!empty($itinerary['destination'])) { $destinations[] = $itinerary['destination']; } } // 创建出差申请 $businessTrip = BusinessTrip::create([ 'user_id' => $userId, 'reason' => $data['reason'] ?? '', 'start_date' => $startDate ? date('Y-m-d', strtotime($startDate)) : null, 'end_date' => $endDate ? date('Y-m-d', strtotime($endDate)) : null, 'destination' => implode(', ', array_unique($destinations)), 'estimated_cost' => $data['estimated_cost'] ?? 0, 'status' => BusinessTrip::STATUS_DRAFT, ]); // 创建行程明细 foreach ($itineraries as $itinerary) { BusinessTripItinerary::create([ 'business_trip_id' => $businessTrip->id, 'start_time' => $itinerary['start_time'], 'end_time' => $itinerary['end_time'] ?? null, 'departure' => $itinerary['departure'] ?? '', 'destination' => $itinerary['destination'] ?? '', 'transport' => $itinerary['transport'] ?? '', 'trip_type' => $itinerary['trip_type'] ?? 'oneway', 'remark' => $itinerary['remark'] ?? '', ]); } return $businessTrip->load('itineraries'); } catch (\Exception $e) { error_log('BusinessTripService::createDraft error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); throw $e; } }); } /** * 提交出差申请 */ public function submit(int $userId, int $businessTripId, array $approvers, array $ccUsers = []): BusinessTrip { return $this->db->transaction(function () use ($userId, $businessTripId, $approvers, $ccUsers) { $businessTrip = BusinessTrip::with('itineraries')->findOrFail($businessTripId); if ($businessTrip->user_id !== $userId) { throw new \Exception('无权操作此申请'); } if (!$businessTrip->canSubmit()) { throw new \Exception('当前状态无法提交'); } // 验证审批人和抄送人不能重复 // 兼容两种格式:对象数组 [{id:1, name:'xxx'}] 或纯ID数组 [1, 2, 3] if (!empty($approvers) && isset($approvers[0]['id'])) { $approverIds = array_column($approvers, 'id'); } else { $approverIds = $approvers; } if (!empty($ccUsers) && isset($ccUsers[0]['id'])) { $ccUserIds = array_column($ccUsers, 'id'); } else { $ccUserIds = $ccUsers; } // 检查审批人列表中是否有重复 if (count($approverIds) !== count(array_unique($approverIds))) { throw new \Exception('审批人列表中有重复用户'); } // 检查抄送人列表中是否有重复 if (count($ccUserIds) !== count(array_unique($ccUserIds))) { throw new \Exception('抄送人列表中有重复用户'); } // 检查审批人和抄送人是否有重叠 $overlap = array_intersect($approverIds, $ccUserIds); if (!empty($overlap)) { throw new \Exception('审批人和抄送人不能是同一个人'); } // 获取审批流程 $flow = ApprovalFlow::where('business_type', 'business_trip') ->where('status', 1) ->first(); if (!$flow) { // 创建默认流程 $flow = ApprovalFlow::create([ 'business_type' => 'business_trip', 'name' => '出差审批', 'description' => '出差申请审批流程', 'status' => 1, ]); } // approval_node 是流程模板,不应在提交时修改 // 实际的审批人存储在 approval_order.form_data 中 // 获取或创建第一个审批节点(仅用于流程结构) $node = ApprovalNode::where('flow_id', $flow->id) ->where('order', 0) ->first(); if (!$node) { $node = ApprovalNode::create([ 'flow_id' => $flow->id, 'type' => 'approver', 'name' => '审批', 'order' => 0, 'approvers' => [], // 模板节点不存储具体审批人 ]); } // 创建审批单 $approvalOrder = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => $businessTrip->id, 'title' => '出差申请 - ' . $businessTrip->user->name, 'applicant_id' => $userId, 'status' => ApprovalOrder::STATUS_PENDING, 'current_nodes' => [$node->id], // 使用创建的节点 ID 'flow_id' => $flow->id, 'form_data' => [ 'reason' => $businessTrip->reason, 'start_date' => $businessTrip->start_date, 'end_date' => $businessTrip->end_date, 'destination' => $businessTrip->destination, 'approvers' => $approvers, 'cc_users' => $ccUsers, ], ]); // 更新出差申请状态 $businessTrip->status = BusinessTrip::STATUS_PENDING; $businessTrip->approval_order_id = $approvalOrder->id; $businessTrip->save(); // 记录提交操作 ApprovalRecord::create([ 'order_id' => $approvalOrder->id, 'node_id' => $node->id, 'node_type' => $node->type, 'approver_id' => $userId, 'action' => 'submit', 'comment' => null, ]); // 如果发起人在审批人列表中,自动标记为已审批 if (in_array($userId, $approverIds)) { ApprovalRecord::create([ 'order_id' => $approvalOrder->id, 'node_id' => $node->id, 'node_type' => $node->type, 'approver_id' => $userId, 'action' => 'approve', 'comment' => '发起人自动审批通过', ]); // 判断节点是否完成 $nodeCompleted = false; if ($node->type === 'approver') { // 单人审批:发起人即完成 $nodeCompleted = true; } elseif ($node->type === 'or_approve') { // 或签:任一人通过即可 $nodeCompleted = true; } elseif ($node->type === 'and_approve') { // 会签:需要所有人通过 $approvedCount = ApprovalRecord::where('order_id', $approvalOrder->id) ->where('node_id', $node->id) ->where('action', 'approve') ->count(); if ($approvedCount >= count($approverIds)) { $nodeCompleted = true; } } // 如果节点完成,移动到下一节点 if ($nodeCompleted) { // 获取当前节点的 order 值 $currentOrder = $node->order ?? 0; // 查找 order 比当前节点大的后续节点 $nextNodes = ApprovalNode::where('flow_id', $flow->id) ->where('order', '>', $currentOrder) ->orderBy('order') ->get(); if ($nextNodes->isEmpty()) { // 没有后续节点,审批完成 $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_APPROVED; } else { // 检查第一个后续节点 $firstNextNode = $nextNodes->first(); if ($firstNextNode && $firstNextNode->type === 'notify') { // 下一个是抄送节点,跳过并完成流程 $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_APPROVED; } else { // 移动到下一节点 $approvalOrder->current_nodes = $nextNodes->pluck('id')->toArray(); } } $approvalOrder->save(); $businessTrip->save(); } } // 发送飞书通知给审批人 error_log("🔔 准备发送审批通知,调用 sendApprovalNotification 方法"); error_log("📋 传递的参数 - approverIds: " . json_encode($approverIds) . ", ccUserIds: " . json_encode($ccUserIds)); $this->sendApprovalNotification($businessTrip, $approverIds, $ccUserIds); return $businessTrip->fresh(['itineraries', 'approvalOrder']); }); } /** * 发送审批通知(飞书消息) */ private function sendApprovalNotification(BusinessTrip $businessTrip, array $approverIds, array $ccUserIds): void { try { error_log("🔔 准备发送审批通知: trip_id={$businessTrip->id}, approvers=" . json_encode($approverIds) . ", cc=" . json_encode($ccUserIds)); $notificationService = new \app\service\FeishuNotificationService(); // 获取 approval_order_id $approvalOrder = \app\model\ApprovalOrder::where('business_type', 'business_trip') ->where('business_id', $businessTrip->id) ->first(); if (!$approvalOrder) { error_log("⚠️ 未找到审批单,无法发送通知"); return; } // 构建审批链接(统一使用审批详情页) $approvalUrl = "https://uniservice-mobile.hnxinyouhe.com/approval/detail/{$approvalOrder->id}"; $detailUrl = $approvalUrl; // 用于抄送人和其他通知 // 构建内容 $content = "**出差原因**: {$businessTrip->reason}\n"; $content .= "**目的地**: {$businessTrip->destination}\n"; $content .= "**时间**: {$businessTrip->start_date} 至 {$businessTrip->end_date}"; // 通知审批人(带审批按钮) foreach ($approverIds as $approverId) { try { error_log("📧 通知审批人: approver_id={$approverId}"); $notificationService->setAppByUser($approverId) ->sendApprovalNotification([ 'user_id' => $approverId, 'applicant_id' => $approvalOrder->applicant_id, // 添加申请人ID用于自审批检测 'title' => '新的出差审批申请', 'applicant' => $businessTrip->user->name, 'type' => '出差申请', 'content' => $content, 'url' => $approvalUrl, 'order_id' => $approvalOrder->id, 'node_id' => $node->id, // 添加节点ID用于保存消息映射 ]); error_log("✅ 审批人通知成功: approver_id={$approverId}"); } catch (\Exception $e) { error_log("❌ 发送审批通知失败: approver_id={$approverId}, error=" . $e->getMessage()); } } // 通知抄送人(不带审批按钮) foreach ($ccUserIds as $ccUserId) { try { error_log("📧 通知抄送人: cc_user_id={$ccUserId}"); $notificationService->setAppByUser($ccUserId) ->sendSystemNotification([ 'user_ids' => [$ccUserId], 'title' => '出差申请抄送', 'content' => "**申请人**: {$businessTrip->user->name}\n" . $content, 'url' => $detailUrl, ]); error_log("✅ 抄送人通知成功: cc_user_id={$ccUserId}"); } catch (\Exception $e) { error_log("❌ 发送抄送通知失败: cc_user_id={$ccUserId}, error=" . $e->getMessage()); } } error_log("✅ 审批通知发送完成"); } catch (\Exception $e) { error_log("❌ 发送飞书通知异常: " . $e->getMessage()); } } /** * 发送审批结果通知(飞书消息) */ private function sendApprovalResultNotification(BusinessTrip $businessTrip, string $action, string $comment = ''): void { try { $notificationService = new \app\service\FeishuNotificationService(); // 获取关联的审批订单 $approvalOrder = \app\model\ApprovalOrder::where('business_id', $businessTrip->id) ->where('business_type', 'business_trip') ->first(); // 统一使用审批详情页 $detailUrl = $approvalOrder ? "https://uniservice-mobile.hnxinyouhe.com/approval/detail/{$approvalOrder->id}" : "https://uniservice-mobile.hnxinyouhe.com/approval/list"; if ($action === 'approve') { $title = '出差申请已通过'; $content = "您的出差申请已通过审批。\n"; $content .= "**申请原因**: {$businessTrip->reason}\n"; if ($comment) { $content .= "**审批意见**: {$comment}"; } } else { $title = '出差申请已拒绝'; $content = "您的出差申请被拒绝。\n"; $content .= "**申请原因**: {$businessTrip->reason}\n"; if ($comment) { $content .= "**拒绝原因**: {$comment}"; } } $notificationService->setAppByUser($businessTrip->user_id) ->sendSystemNotification([ 'user_ids' => [$businessTrip->user_id], 'title' => $title, 'content' => $content, 'url' => $detailUrl, ]); } catch (\Exception $e) { error_log("发送审批结果通知失败: " . $e->getMessage()); } } /** * 获取出差申请列表 */ public function getList(int $userId, string $type = 'my', int $page = 1, int $pageSize = 20): array { // 先不加 approvalOrder 关系,避免 PHP 8.5 兼容性问题 $query = BusinessTrip::with(['user', 'itineraries']); switch ($type) { case 'my': // 我发起的:当前用户创建的申请 $query->where('user_id', $userId); break; case 'pending': // 待我审批:需要查询当前用户是审批人且还未审批的申请 $allPendingOrders = ApprovalOrder::where('status', ApprovalOrder::STATUS_PENDING) ->where('business_type', 'business_trip') ->get(); error_log("🔍 Pending query result: count=" . count($allPendingOrders)); $pendingOrderIds = $allPendingOrders->pluck('id')->toArray(); if (empty($pendingOrderIds)) { $query->whereRaw('1 = 0'); // 返回空结果 } else { // 获取这些审批单的当前节点 $pendingOrders = ApprovalOrder::whereIn('id', $pendingOrderIds)->get(); $validOrderIds = []; foreach ($pendingOrders as $order) { error_log("🔍 Checking order {$order->id}: form_data=" . json_encode($order->form_data)); // 从 form_data 获取实际的审批人列表 $formData = $order->form_data ?? []; $approvers = $formData['approvers'] ?? []; // 兼容两种格式:[1,2,3] 或 [{id:1},{id:2},{id:3}] if (!empty($approvers)) { if (is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { // 对象数组格式: [{id:1, name:'...'}] $approverIds = array_column($approvers, 'id'); } else { // ID数组格式: [1, 2, 3] $approverIds = $approvers; } } else { $approverIds = []; } error_log("🔍 Order {$order->id}: approverIds=" . json_encode($approverIds) . ", userId={$userId}"); // 检查用户是否在审批人列表中 $isApprover = in_array($userId, $approverIds); error_log("🔍 Order {$order->id}: isApprover=" . ($isApprover ? 'true' : 'false')); // 如果用户是当前节点审批人,且未审批过,则加入列表 if ($isApprover) { $hasApproved = ApprovalRecord::where('order_id', $order->id) ->where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->exists(); if (!$hasApproved) { $validOrderIds[] = $order->id; } } } if (empty($validOrderIds)) { $query->whereRaw('1 = 0'); } else { $query->whereIn('approval_order_id', $validOrderIds); } } break; case 'approved': // 我已审批:当前用户已经审批过的申请 $approvedOrderIds = ApprovalRecord::where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->pluck('order_id') ->toArray(); error_log("🔍 'approved' query: userId={$userId}, approvedOrderIds=" . json_encode($approvedOrderIds)); if (empty($approvedOrderIds)) { $query->whereRaw('1 = 0'); // 返回空结果 } else { // 先检查是否有匹配的 business_trip(不加载关系) $checkCount = BusinessTrip::whereIn('approval_order_id', $approvedOrderIds)->count(); error_log("🔍 Matching business_trips count (without relations): {$checkCount}"); // 获取匹配的 business_trip IDs $matchingTrips = BusinessTrip::whereIn('approval_order_id', $approvedOrderIds) ->pluck('id') ->toArray(); error_log("🔍 Matching business_trip IDs: " . json_encode($matchingTrips)); $query->whereIn('approval_order_id', $approvedOrderIds); } break; } $total = $query->count(); $list = $query->orderBy('created_at', 'desc') ->offset(($page - 1) * $pageSize) ->limit($pageSize) ->get(); return [ 'list' => $list, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, ]; } /** * 获取出差申请详情 */ public function getDetail(int $id): array { $businessTrip = BusinessTrip::with(['user', 'itineraries']) ->findOrFail($id); $result = $businessTrip->toArray(); // 获取审批单信息 if ($businessTrip->approval_order_id) { $approvalOrder = ApprovalOrder::with(['records.approver']) ->find($businessTrip->approval_order_id); if ($approvalOrder) { $result['approval_order'] = $approvalOrder->toArray(); // 获取审批人和抄送人信息 $formData = $approvalOrder->form_data ?? []; $result['approvers'] = $formData['approvers'] ?? []; $result['cc_users'] = $formData['cc_users'] ?? []; // 获取审批记录 $result['approval_records'] = $approvalOrder->records->toArray(); } } return $result; } /** * 更新出差申请 */ public function update(int $userId, int $id, array $data): BusinessTrip { $businessTrip = BusinessTrip::findOrFail($id); if ($businessTrip->user_id !== $userId) { throw new \Exception('无权操作此申请'); } if (!$businessTrip->canEdit()) { throw new \Exception('当前状态无法编辑'); } return $this->db->transaction(function () use ($businessTrip, $data) { // 更新基本信息 if (isset($data['reason'])) { $businessTrip->reason = $data['reason']; } // 更新行程 if (isset($data['itineraries'])) { // 删除旧行程 BusinessTripItinerary::where('business_trip_id', $businessTrip->id)->delete(); // 创建新行程 $startDate = null; $endDate = null; $destinations = []; foreach ($data['itineraries'] as $itinerary) { BusinessTripItinerary::create([ 'business_trip_id' => $businessTrip->id, 'start_time' => $itinerary['start_time'], 'end_time' => $itinerary['end_time'] ?? null, 'departure' => $itinerary['departure'] ?? '', 'destination' => $itinerary['destination'] ?? '', 'transport' => $itinerary['transport'] ?? '', 'trip_type' => $itinerary['trip_type'] ?? 'oneway', 'remark' => $itinerary['remark'] ?? '', ]); if ($itinerary['start_time']) { if (!$startDate || $itinerary['start_time'] < $startDate) { $startDate = $itinerary['start_time']; } } if ($itinerary['end_time']) { if (!$endDate || $itinerary['end_time'] > $endDate) { $endDate = $itinerary['end_time']; } } if (!empty($itinerary['destination'])) { $destinations[] = $itinerary['destination']; } } // 更新日期和目的地 $businessTrip->start_date = $startDate ? date('Y-m-d', strtotime($startDate)) : null; $businessTrip->end_date = $endDate ? date('Y-m-d', strtotime($endDate)) : null; $businessTrip->destination = implode(', ', array_unique($destinations)); } $businessTrip->save(); return $businessTrip->fresh('itineraries'); }); } /** * 取消出差申请 */ public function cancel(int $userId, int $id): BusinessTrip { $businessTrip = BusinessTrip::findOrFail($id); if ($businessTrip->user_id !== $userId) { throw new \Exception('无权操作此申请'); } if (!$businessTrip->canCancel()) { throw new \Exception('当前状态无法取消'); } $businessTrip->status = BusinessTrip::STATUS_CANCELLED; $businessTrip->save(); // 同时取消审批单 if ($businessTrip->approval_order_id) { $approvalOrder = ApprovalOrder::find($businessTrip->approval_order_id); if ($approvalOrder) { $approvalOrder->status = ApprovalOrder::STATUS_CANCELLED; $approvalOrder->current_nodes = []; $approvalOrder->save(); // 发送取消通知给审批人和抄送人 $this->sendCancelNotification($businessTrip, $approvalOrder); } } return $businessTrip->fresh(); } /** * 发送取消通知(飞书消息) */ private function sendCancelNotification(BusinessTrip $businessTrip, ApprovalOrder $approvalOrder): void { try { $notificationService = new \app\service\FeishuNotificationService(); // 统一使用审批详情页 $detailUrl = "https://uniservice-mobile.hnxinyouhe.com/approval/detail/{$approvalOrder->id}"; // 获取审批人和抄送人 $formData = $approvalOrder->form_data ?? []; $approvers = $formData['approvers'] ?? []; $ccUsers = $formData['cc_users'] ?? []; $content = "**申请人**: {$businessTrip->user->name}\n"; $content .= "**出差原因**: {$businessTrip->reason}\n"; $content .= "**目的地**: {$businessTrip->destination}"; // 合并所有需要通知的用户ID $allUserIds = array_unique(array_merge( array_column($approvers, 'id'), array_column($ccUsers, 'id') )); foreach ($allUserIds as $notifyUserId) { try { $notificationService->setAppByUser($notifyUserId) ->sendSystemNotification([ 'user_ids' => [$notifyUserId], 'title' => '出差申请已取消', 'content' => $content, 'url' => $detailUrl, ]); } catch (\Exception $e) { error_log("发送取消通知失败: user_id={$notifyUserId}, error=" . $e->getMessage()); } } } catch (\Exception $e) { error_log("发送取消通知异常: " . $e->getMessage()); } } /** * 删除草稿 */ public function deleteDraft(int $userId, int $id): bool { $businessTrip = BusinessTrip::findOrFail($id); if ($businessTrip->user_id !== $userId) { throw new \Exception('无权操作此申请'); } if ($businessTrip->status !== BusinessTrip::STATUS_DRAFT) { throw new \Exception('只能删除草稿'); } return $this->db->transaction(function () use ($businessTrip) { // 删除行程明细 BusinessTripItinerary::where('business_trip_id', $businessTrip->id)->delete(); // 删除申请 return $businessTrip->delete(); }); } /** * 审批操作 */ public function approve(int $userId, int $businessTripId, string $action, string $comment = ''): BusinessTrip { error_log("📝 approve() 开始: userId={$userId}, businessTripId={$businessTripId}, action={$action}"); try { $result = $this->db->transaction(function () use ($userId, $businessTripId, $action, $comment) { error_log("🔄 Transaction 开始"); $businessTrip = BusinessTrip::with(['itineraries', 'approvalOrder'])->findOrFail($businessTripId); if (!$businessTrip->approval_order_id) { throw new \Exception('未找到审批单'); } $approvalOrder = $businessTrip->approvalOrder; if ($approvalOrder->status !== ApprovalOrder::STATUS_PENDING) { throw new \Exception('当前状态无法审批'); } // 检查用户是否是当前审批人 // 从 form_data 获取实际的审批人列表 $formData = $approvalOrder->form_data ?? []; $approvers = $formData['approvers'] ?? []; // 兼容两种格式:[1,2,3] 或 [{id:1},{id:2},{id:3}] if (!empty($approvers)) { if (is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { // 对象数组格式: [{id:1, name:'...'}] $approverIds = array_column($approvers, 'id'); } else { // ID数组格式: [1, 2, 3] $approverIds = $approvers; } } else { $approverIds = []; } if (!in_array($userId, $approverIds)) { throw new \Exception('您不是当前审批人'); } // 检查是否已经审批过 $existingRecord = ApprovalRecord::where('order_id', $approvalOrder->id) ->where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->first(); if ($existingRecord) { throw new \Exception('您已经审批过了'); } // 获取当前节点ID $currentNodes = $approvalOrder->current_nodes ?? []; $currentNodeId = $currentNodes[0] ?? null; // 记录审批操作 ApprovalRecord::create([ 'order_id' => $approvalOrder->id, 'node_id' => $currentNodeId, 'node_type' => 'approver', 'approver_id' => $userId, 'action' => $action, 'comment' => $comment, ]); // 更新审批单状态 if ($action === 'reject') { // 拒绝:直接结束流程 error_log("❌ 审批拒绝,结束流程"); $approvalOrder->status = ApprovalOrder::STATUS_REJECTED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_REJECTED; } else { error_log("✅ 审批同意,检查节点完成状态"); // 同意:检查当前节点是否完成,并处理流程流转 $nodeCompleted = false; if ($currentNodeId) { $node = ApprovalNode::find($currentNodeId); error_log("🔍 当前节点: id={$currentNodeId}, type=" . ($node ? $node->type : 'null')); if ($node) { // 根据节点类型判断是否完成 if ($node->type === 'approver') { // 单人审批:一人通过即完成 error_log("✅ 单人审批节点,一人通过即完成"); $nodeCompleted = true; } elseif ($node->type === 'or_approve') { // 或签:一人通过即完成 error_log("✅ 或签节点,一人通过即完成"); $nodeCompleted = true; } elseif ($node->type === 'and_approve') { // 会签:需要所有人都通过 $approvedCount = ApprovalRecord::where('order_id', $approvalOrder->id) ->where('node_id', $currentNodeId) ->where('action', 'approve') ->count(); error_log("🔍 会签节点: approvedCount={$approvedCount}, totalApprovers=" . count($approverIds)); if ($approvedCount >= count($approverIds)) { $nodeCompleted = true; } } } } error_log("🔍 nodeCompleted: " . ($nodeCompleted ? 'true' : 'false')); if ($nodeCompleted) { // 当前节点完成,查找下一个节点 error_log("✅ 节点 {$currentNodeId} 已完成,查找下一节点"); // 获取当前节点的 order 值 $currentOrder = $node->order ?? 0; error_log("📍 当前节点 order: {$currentOrder}"); // 查找 order 比当前节点大的下一个节点 $nextNode = ApprovalNode::where('flow_id', $approvalOrder->flow_id) ->where('order', '>', $currentOrder) ->orderBy('order') ->first(); error_log("🔍 下一节点: " . ($nextNode ? "id={$nextNode->id}, type={$nextNode->type}, name={$nextNode->name}, order={$nextNode->order}" : "null")); if ($nextNode) { // 有下一个节点 if ($nextNode->type === 'notify') { // 下一个是抄送节点,跳过并完成流程 error_log("⏭️ 跳过抄送节点,完成流程"); $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_APPROVED; } else { // 下一个是审批节点,移动到该节点 error_log("➡️ 移动到下一节点: {$nextNode->id}"); $approvalOrder->current_nodes = [$nextNode->id]; } } else { // 没有下一个节点,流程完成 error_log("✅ 没有下一节点,流程完成"); $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_APPROVED; } } } error_log("💾 准备保存: order_status={$approvalOrder->status}, trip_status={$businessTrip->status}"); $approvalOrder->save(); $businessTrip->save(); error_log("✅ 保存完成: order_id={$approvalOrder->id}, trip_id={$businessTrip->id}"); // 发送审批结果通知 $this->sendApprovalResultNotification($businessTrip, $action, $comment); error_log("✅ Transaction 成功,返回结果"); return $businessTrip->fresh(['itineraries', 'approvalOrder']); }); error_log("📝 approve() 完成: result_id=" . $result->id); return $result; } catch (\Exception $e) { error_log("❌ approve() 异常: " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine()); throw $e; } } /** * 获取默认审批人和抄送人 * 从审批流程配置表中获取 */ public function getDefaultApprovers(int $userId): array { // 获取出差审批流程 $flow = ApprovalFlow::where('business_type', 'business_trip') ->where('status', 1) ->first(); if (!$flow) { return [ 'approvers' => [], 'cc_users' => [], ]; } // 获取流程节点 $nodes = ApprovalNode::where('flow_id', $flow->id) ->orderBy('order') ->get(); $approvers = []; $ccUsers = []; foreach ($nodes as $node) { $nodeApprovers = $node->approvers ?? []; if (empty($nodeApprovers)) { continue; } // 根据节点类型获取用户信息 $users = User::whereIn('id', $nodeApprovers) ->where('status', 1) ->get(); $userList = []; foreach ($nodeApprovers as $userId) { $user = $users->firstWhere('id', $userId); if ($user) { $userList[] = [ 'id' => $user->id, 'name' => $user->name, 'avatar' => $user->avatar, ]; } } // 审批节点(approver, and_approve, or_approve)作为审批人 if ($node->isApprovalNode()) { $approvers = array_merge($approvers, $userList); } // 通知节点(notify)作为抄送人 elseif ($node->type === ApprovalNode::TYPE_NOTIFY) { $ccUsers = array_merge($ccUsers, $userList); } } return [ 'approvers' => $approvers, 'cc_users' => $ccUsers, ]; } }