db = Db::connection(); } /** * 提交审批 */ public function submit(array $data): ApprovalOrder { return $this->db->transaction(function () use ($data) { // 1. 获取审批流程 $flow = ApprovalFlow::active() ->byBusinessType($data['business_type']) ->with('nodes') ->findOrFail($data['flow_id']); // 2. 创建审批单 $order = ApprovalOrder::create([ 'business_type' => $data['business_type'], 'business_id' => $data['business_id'], 'title' => $data['title'], 'applicant_id' => $data['applicant_id'], 'status' => ApprovalOrder::STATUS_PENDING, 'current_nodes' => $this->getFirstNodeIds($flow), 'flow_id' => $flow->id, 'form_data' => $data['form_data'] ?? [], 'attachments' => $data['attachments'] ?? [], ]); // 3. 记录提交操作 ApprovalRecord::create([ 'order_id' => $order->id, 'node_id' => $flow->nodes->first()->id ?? 0, 'node_type' => 'submit', 'approver_id' => $data['applicant_id'], 'action' => ApprovalRecord::ACTION_SUBMIT, 'comment' => $data['comment'] ?? null, ]); return $order->load(['flow', 'applicant']); }); } /** * 审批通过 */ public function approve(int $orderId, int $approverId, ?string $comment = null): ApprovalOrder { return $this->db->transaction(function () use ($orderId, $approverId, $comment) { $order = ApprovalOrder::with(['flow.nodes'])->findOrFail($orderId); if (!$order->canApprove()) { throw new \Exception('当前状态无法审批'); } $currentNodeIds = $order->current_nodes ?? []; error_log("🔍 当前节点 IDs: " . json_encode($currentNodeIds)); $currentNode = ApprovalNode::whereIn('id', $currentNodeIds)->first(); if (!$currentNode) { error_log("❌ 未找到节点,IDs: " . json_encode($currentNodeIds)); throw new \Exception('当前节点不存在'); } // 记录审批操作 ApprovalRecord::create([ 'order_id' => $order->id, 'node_id' => $currentNode->id, 'node_type' => $currentNode->type, 'approver_id' => $approverId, 'action' => ApprovalRecord::ACTION_APPROVE, 'comment' => $comment, ]); // 判断节点是否完成 if ($this->isNodeCompleted($order->id, $currentNode->id)) { // 移动到下一节点 $nextNodes = $this->getNextNodes($order->flow, $currentNode->order); if (empty($nextNodes)) { // 没有下一节点,审批完成 $order->status = ApprovalOrder::STATUS_APPROVED; $order->current_nodes = []; // 通知申请人审批通过 $this->notifyApplicant($order, 'approve'); } else { // 检查第一个下一节点 $firstNextNode = $nextNodes[0] ?? null; if ($firstNextNode && $firstNextNode['type'] === 'notify') { // 下一个是抄送节点,跳过并完成流程 $order->status = ApprovalOrder::STATUS_APPROVED; $order->current_nodes = []; // 通知申请人审批通过 $this->notifyApplicant($order, 'approve'); } else { // 下一个是审批节点,移动到该节点 $order->current_nodes = collect($nextNodes)->pluck('id')->toArray(); // 通知下一节点的审批人 $this->notifyNextApprovers($order, $nextNodes); } } $order->save(); } return $order->fresh(['flow', 'applicant', 'records']); }); } /** * 审批拒绝 */ public function reject(int $orderId, int $approverId, ?string $comment = null): ApprovalOrder { return $this->db->transaction(function () use ($orderId, $approverId, $comment) { $order = ApprovalOrder::findOrFail($orderId); if (!$order->canApprove()) { throw new \Exception('当前状态无法审批'); } $currentNodeIds = $order->current_nodes ?? []; $currentNode = ApprovalNode::whereIn('id', $currentNodeIds)->first(); // 记录拒绝操作 ApprovalRecord::create([ 'order_id' => $order->id, 'node_id' => $currentNode->id ?? 0, 'node_type' => $currentNode->type ?? 'unknown', 'approver_id' => $approverId, 'action' => ApprovalRecord::ACTION_REJECT, 'comment' => $comment, ]); // 更新状态为已拒绝 $order->status = ApprovalOrder::STATUS_REJECTED; $order->current_nodes = []; $order->save(); // 通知申请人审批被拒绝 $this->notifyApplicant($order, 'reject', $comment); return $order->fresh(['flow', 'applicant', 'records']); }); } /** * 撤销申请 */ public function cancel(int $orderId, int $userId): ApprovalOrder { $order = ApprovalOrder::findOrFail($orderId); if ($order->applicant_id !== $userId) { throw new \Exception('只有申请人可以撤销'); } if (!$order->canEdit() && !$order->canApprove()) { throw new \Exception('当前状态无法撤销'); } // 记录撤销操作 ApprovalRecord::create([ 'order_id' => $order->id, 'node_id' => 0, 'node_type' => 'cancel', 'approver_id' => $userId, 'action' => ApprovalRecord::ACTION_CANCEL, 'comment' => '申请人撤销', ]); $order->status = ApprovalOrder::STATUS_CANCELLED; $order->current_nodes = []; $order->save(); return $order->fresh(['flow', 'applicant', 'records']); } /** * 获取审批列表 */ public function getList(int $userId, string $type = 'my', int $page = 1, int $pageSize = 20): array { $query = ApprovalOrder::with(['flow', 'applicant']); switch ($type) { case 'my': $query->where('applicant_id', $userId); error_log("🔍 Debug 'my' query: userId={$userId}"); break; case 'pending': // 获取用户待审批的订单 $pendingOrderIds = $this->getPendingOrderIds($userId); error_log("🔍 Debug 'pending' query: userId={$userId}, pendingOrderIds=" . json_encode($pendingOrderIds)); $query->whereIn('id', $pendingOrderIds); break; case 'approved': // 获取用户已审批的订单(只显示已完成的:approved、rejected、cancelled) $approvedOrderIds = ApprovalRecord::where('approver_id', $userId) ->where('action', '!=', ApprovalRecord::ACTION_SUBMIT) ->whereIn('action', [ApprovalRecord::ACTION_APPROVE, ApprovalRecord::ACTION_REJECT]) ->pluck('order_id') ->unique() ->toArray(); error_log("🔍 Debug 'approved' query: userId={$userId}, approvedOrderIds=" . json_encode($approvedOrderIds)); $query->whereIn('id', $approvedOrderIds) ->whereIn('status', [ApprovalOrder::STATUS_APPROVED, ApprovalOrder::STATUS_REJECTED, ApprovalOrder::STATUS_CANCELLED]); break; case 'cc': // 获取抄送给我的订单 $allOrders = ApprovalOrder::where('status', '!=', ApprovalOrder::STATUS_DRAFT)->get(); $ccOrderIds = []; foreach ($allOrders as $order) { $formData = $order->form_data ?? []; $ccUsers = $formData['cc_users'] ?? []; // 兼容两种格式:[{id:29, name:'...'}] 或 [29] 或 [{id:29}] if (!empty($ccUsers)) { if (is_array($ccUsers[0] ?? null) && isset($ccUsers[0]['id'])) { // 对象数组格式: [{id:29, name:'...'}] $ccUserIds = array_column($ccUsers, 'id'); } else if (is_numeric($ccUsers[0] ?? null)) { // ID数组格式: [29, 30] $ccUserIds = $ccUsers; } else { $ccUserIds = []; } if (in_array($userId, $ccUserIds)) { $ccOrderIds[] = $order->id; } } } error_log("🔍 Debug 'cc' query: userId={$userId}, ccOrderIds=" . json_encode($ccOrderIds)); $query->whereIn('id', $ccOrderIds); break; } $total = $query->count(); error_log("🔍 Debug total count: type={$type}, total={$total}"); $list = $query->orderBy('created_at', 'desc') ->offset(($page - 1) * $pageSize) ->limit($pageSize) ->get(); error_log("🔍 Debug list count: type={$type}, page={$page}, count=" . $list->count()); // 转换为数组避免序列化问题 $listArray = $list->map(function ($item) { return [ 'id' => $item->id, 'title' => $item->title, 'business_type' => $item->business_type, 'status' => $item->status, 'created_at' => $item->created_at ? $item->created_at->toDateTimeString() : null, 'applicant' => $item->applicant ? [ 'id' => $item->applicant->id, 'name' => $item->applicant->name, ] : null, 'flow' => $item->flow ? [ 'id' => $item->flow->id, 'name' => $item->flow->name, ] : null, ]; })->toArray(); return [ 'list' => $listArray, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, ]; } /** * 获取第一个节点ID列表 */ private function getFirstNodeIds(ApprovalFlow $flow): array { return $flow->nodes ->where('order', 0) ->pluck('id') ->toArray(); } /** * 判断节点是否完成 */ private function isNodeCompleted(int $orderId, int $nodeId): bool { $node = ApprovalNode::findOrFail($nodeId); $records = ApprovalRecord::where('order_id', $orderId) ->where('node_id', $nodeId) ->where('action', 'approve') ->get(); // 从 form_data 获取审批人列表 $order = ApprovalOrder::find($orderId); $formData = $order->form_data ?? []; $approvers = $formData['approvers'] ?? []; // 兼容两种格式:[{id:1, name:'...'}] 或 [1] if (!empty($approvers) && is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { $approverIds = array_column($approvers, 'id'); } else { $approverIds = $approvers; } if ($node->type === ApprovalNode::TYPE_AND_APPROVE) { // 会签:需要所有人通过 return count($records) >= count($approverIds); } elseif ($node->type === ApprovalNode::TYPE_OR_APPROVE) { // 或签:任一人通过即可 return count($records) >= 1; } else { // 单人审批:需要一人通过 return count($records) >= 1; } } /** * 获取下一节点 */ private function getNextNodes(ApprovalFlow $flow, int $currentOrder): array { return $flow->nodes ->where('order', '>', $currentOrder) ->sortBy('order') ->values() ->toArray(); } /** * 获取用户待审批的订单ID列表 */ private function getPendingOrderIds(int $userId): array { // 获取用户参与的待审批订单 $pendingOrders = ApprovalOrder::where('status', ApprovalOrder::STATUS_PENDING) ->whereNotNull('current_nodes') ->get(); $result = []; foreach ($pendingOrders as $order) { // 从 form_data 获取审批人列表 $formData = $order->form_data ?? []; $approvers = $formData['approvers'] ?? []; // 兼容两种格式:[{id:1, name:'...'}] 或 [1] 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; } if (in_array($userId, $approverIds)) { // 检查是否已审批 $hasApproved = ApprovalRecord::where('order_id', $order->id) ->where('approver_id', $userId) ->whereIn('action', [ApprovalRecord::ACTION_APPROVE, ApprovalRecord::ACTION_REJECT]) ->exists(); if (!$hasApproved) { $result[] = $order->id; } } } } return $result; } /** * 通知申请人 */ private function notifyApplicant(ApprovalOrder $order, string $action, ?string $comment = null): void { try { if (!$this->notificationService) { $this->notificationService = new FeishuNotificationService(); } $applicant = User::find($order->applicant_id); if (!$applicant) { error_log("❌ notifyApplicant: 申请人不存在, applicant_id={$order->applicant_id}"); return; } // 根据业务类型获取详情链接 $detailUrl = $this->getDetailUrl($order); // 构建消息内容 $title = $action === 'approve' ? '您的申请已通过' : '您的申请已被拒绝'; $content = "**申请标题**: {$order->title}\n"; $content .= "**申请类型**: {$this->getBusinessTypeName($order->business_type)}\n"; if ($comment) { $content .= "**审批意见**: {$comment}\n"; } error_log("🔔 通知申请人: applicant_id={$applicant->id}, action={$action}"); $this->notificationService->setAppByUser($applicant->id) ->sendSystemNotification([ 'user_ids' => [$applicant->id], 'title' => $title, 'content' => $content, 'url' => $detailUrl, ]); } catch (\Exception $e) { error_log('❌ notifyApplicant 失败: ' . $e->getMessage()); } } /** * 通知下一节点的审批人 */ private function notifyNextApprovers(ApprovalOrder $order, array $nextNodes): void { try { if (!$this->notificationService) { $this->notificationService = new FeishuNotificationService(); } // 从 form_data 获取审批人列表 $formData = $order->form_data ?? []; $approvers = $formData['approvers'] ?? []; // 兼容两种格式 if (!empty($approvers) && is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { $approverIds = array_column($approvers, 'id'); } else { $approverIds = $approvers; } if (empty($approverIds)) { error_log("⚠️ notifyNextApprovers: 没有找到审批人, order_id={$order->id}"); return; } // 获取详情链接 $detailUrl = $this->getDetailUrl($order); // 业务类型名称 $businessTypeName = $this->getBusinessTypeName($order->business_type); error_log("🔔 通知下一节点审批人: order_id={$order->id}, approverIds=" . json_encode($approverIds)); // 为每个审批人发送通知 foreach ($approverIds as $approverId) { try { $this->notificationService->setAppByUser($approverId) ->sendApprovalNotification([ 'user_id' => $approverId, 'title' => '新的审批待办', 'applicant' => $order->applicant->name, 'type' => $businessTypeName, 'content' => "**申请标题**: {$order->title}\n**申请人**: {$order->applicant->name}", 'url' => $detailUrl, 'order_id' => $order->id, ]); error_log("✅ 审批人通知成功: approver_id={$approverId}"); } catch (\Exception $e) { error_log("❌ 发送审批通知失败: approver_id={$approverId}, error=" . $e->getMessage()); } } } catch (\Exception $e) { error_log('❌ notifyNextApprovers 失败: ' . $e->getMessage()); } } /** * 获取详情链接 */ private function getDetailUrl(ApprovalOrder $order): string { $baseUrl = config('feishu_notification.base_url') ?? request()->scheme() . '://' . request()->host(); // 统一使用审批详情页,不再跳转到具体业务模块的详情页 return "{$baseUrl}/approval/detail/{$order->id}"; } /** * 获取业务类型名称 */ private function getBusinessTypeName(string $businessType): string { $typeNames = [ 'business_trip' => '出差申请', 'expense' => '费用报销', 'leave' => '请假申请', 'purchase' => '采购申请', ]; return $typeNames[$businessType] ?? $businessType; } }