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('当前状态无法提交'); } // 获取审批流程(简化版:直接使用第一个审批人作为单人审批) $flow = ApprovalFlow::where('business_type', 'business_trip') ->where('status', 1) ->first(); if (!$flow) { // 创建默认流程 $flow = ApprovalFlow::create([ 'business_type' => 'business_trip', 'name' => '出差审批', 'description' => '出差申请审批流程', 'status' => 1, ]); } // 创建审批单 $approvalOrder = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => $businessTrip->id, 'title' => '出差申请 - ' . $businessTrip->user->name, 'applicant_id' => $userId, 'status' => ApprovalOrder::STATUS_PENDING, 'current_nodes' => [1], // 简化:使用节点1 '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' => 1, 'node_type' => 'single_approve', 'approver_id' => $userId, 'action' => 'submit', 'comment' => null, ]); return $businessTrip->fresh(['itineraries', 'approvalOrder']); }); } /** * 获取出差申请列表 */ public function getList(int $userId, string $type = 'my', int $page = 1, int $pageSize = 20): array { $query = BusinessTrip::with(['user', 'itineraries']); switch ($type) { case 'my': // 我发起的:当前用户创建的申请 $query->where('user_id', $userId); break; case 'pending': // 待我审批:需要查询当前用户是审批人且还未审批的申请 $pendingOrderIds = ApprovalOrder::where('status', ApprovalOrder::STATUS_PENDING) ->where('business_type', 'business_trip') ->pluck('id') ->toArray(); if (empty($pendingOrderIds)) { $query->whereRaw('1 = 0'); // 返回空结果 } else { // 获取这些审批单中当前用户待审批的,排除已审批的 $approvedOrderIds = ApprovalRecord::whereIn('order_id', $pendingOrderIds) ->where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->pluck('order_id') ->toArray(); $pendingOrderIds = array_diff($pendingOrderIds, $approvedOrderIds); $query->whereIn('approval_order_id', $pendingOrderIds); } break; case 'approved': // 我已审批:当前用户已经审批过的申请 $approvedOrderIds = ApprovalRecord::where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->pluck('order_id') ->toArray(); if (empty($approvedOrderIds)) { $query->whereRaw('1 = 0'); // 返回空结果 } else { $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(); } } return $businessTrip->fresh(); } /** * 删除草稿 */ 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 { return $this->db->transaction(function () use ($userId, $businessTripId, $action, $comment) { $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('当前状态无法审批'); } // 检查用户是否是当前审批人 $currentNodes = $approvalOrder->current_nodes ?? []; $isApprover = false; foreach ($currentNodes as $nodeId) { $node = ApprovalNode::where('order_id', $approvalOrder->id) ->where('id', $nodeId) ->first(); if (!$node) { continue; } // 检查是否是单人审批节点 if ($node->node_type === 'single_approve') { $approvers = $node->approvers ?? []; if (in_array($userId, $approvers)) { $isApprover = true; break; } } } if (!$isApprover) { throw new \Exception('您不是当前审批人'); } // 检查是否已经审批过 $existingRecord = ApprovalRecord::where('order_id', $approvalOrder->id) ->where('approver_id', $userId) ->whereIn('action', ['approve', 'reject']) ->first(); if ($existingRecord) { throw new \Exception('您已经审批过了'); } // 记录审批操作 ApprovalRecord::create([ 'order_id' => $approvalOrder->id, 'node_id' => $currentNodes[0] ?? 1, 'node_type' => 'single_approve', 'approver_id' => $userId, 'action' => $action, 'comment' => $comment, ]); // 更新审批单状态 if ($action === 'reject') { // 拒绝:直接结束流程 $approvalOrder->status = ApprovalOrder::STATUS_REJECTED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_REJECTED; } else { // 同意:检查是否还有后续节点(简化版:直接通过) $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; $approvalOrder->current_nodes = []; $businessTrip->status = BusinessTrip::STATUS_APPROVED; } $approvalOrder->save(); $businessTrip->save(); return $businessTrip->fresh(['itineraries', 'approvalOrder']); }); } }