From f8524d333cfa67ddeb539a150db481ebb3d6d4e0 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 1 Mar 2026 12:46:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E5=90=8E=E6=B6=88=E6=81=AF=E5=8D=A1=E7=89=87=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=92=8C=E5=A4=9A=E8=A1=8C=E7=A8=8B=E6=98=BE=E7=A4=BA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 审批人审批后,自己的消息卡片也更新为显示查看详情按钮 - 新增 updateActorMessageCard() 方法更新操作人自己的卡片 - 审批通过/拒绝时同时更新操作人和其他审批人的卡片 2. 多行程路线在审批详情中正确显示 - 特殊处理 itineraries 字段,展示每个行程的详细信息 - 添加行程列表样式,包括目的地、日期、费用等 Co-Authored-By: Claude Opus 4.6 --- backend/app/service/ApprovalService.php | 30 +++++ .../app/service/FeishuNotificationService.php | 115 ++++++++++++++++++ frontend/src/pages/approval/detail.module.css | 50 ++++++++ frontend/src/pages/approval/detail.tsx | 41 +++++++ 4 files changed, 236 insertions(+) diff --git a/backend/app/service/ApprovalService.php b/backend/app/service/ApprovalService.php index 38dff33..93c938c 100644 --- a/backend/app/service/ApprovalService.php +++ b/backend/app/service/ApprovalService.php @@ -134,6 +134,16 @@ class ApprovalService $approvedCount = count($approvedRecords); if ($this->notificationService) { + // 更新操作人自己的消息卡片 + $this->notificationService->setAppByUser($approverId); + $this->notificationService->updateActorMessageCard( + $order->id, + $currentNode->id, + $approverId, + 'approved' + ); + + // 更新其他审批人的消息卡片 $this->notificationService->updateMessageCardsForPartialApprove( $order->id, $currentNode->id, @@ -152,6 +162,16 @@ class ApprovalService $approverName = $approver ? $approver->name : '未知'; if ($this->notificationService) { + // 更新操作人自己的消息卡片 + $this->notificationService->setAppByUser($approverId); + $this->notificationService->updateActorMessageCard( + $order->id, + $currentNode->id, + $approverId, + 'approved' + ); + + // 更新其他审批人的消息卡片 $this->notificationService->updateMessageCards( $order->id, $currentNode->id, @@ -239,6 +259,16 @@ class ApprovalService } if ($currentNode) { + // 更新操作人自己的消息卡片 + $this->notificationService->setAppByUser($approverId); + $this->notificationService->updateActorMessageCard( + $order->id, + $currentNode->id, + $approverId, + 'rejected' + ); + + // 更新其他审批人的消息卡片 $this->notificationService->updateMessageCards( $order->id, $currentNode->id, diff --git a/backend/app/service/FeishuNotificationService.php b/backend/app/service/FeishuNotificationService.php index 71085a4..80ac9d7 100644 --- a/backend/app/service/FeishuNotificationService.php +++ b/backend/app/service/FeishuNotificationService.php @@ -670,6 +670,121 @@ class FeishuNotificationService * @param string $operatorName 操作人姓名 * @return bool */ + /** + * 更新操作人自己的消息卡片(审批完成后显示查看详情按钮) + * + * @param int $orderId 订单ID + * @param int $nodeId 节点ID + * @param int $actorUserId 操作人ID(刚刚完成审批的用户) + * @param string $status 审批状态 (approved/rejected) + * @return bool + */ + public function updateActorMessageCard(int $orderId, int $nodeId, int $actorUserId, string $status): bool + { + try { + // 获取该操作人的消息 + $message = \app\model\FeishuMessage::where('order_id', $orderId) + ->where('node_id', $nodeId) + ->where('user_id', $actorUserId) + ->where('status', \app\model\FeishuMessage::STATUS_ACTIVE) + ->first(); + + if (!$message) { + error_log("📭 没有找到操作人的消息: order_id={$orderId}, node_id={$nodeId}, user_id={$actorUserId}"); + return true; + } + + $companyId = $message->company_id; + $feishuAppId = $message->feishu_app_id; + + if (!$companyId || !$feishuAppId) { + error_log("❌ 消息记录缺少 company_id 或 feishu_app_id: message_id={$message->message_id}"); + return false; + } + + error_log("🔍 准备更新操作人消息: message_id={$message->message_id}, company_id={$companyId}, feishu_app_id={$feishuAppId}"); + + // 使用该消息对应的飞书应用配置 + $this->companyId = $companyId; + $this->appId = $feishuAppId; + + // 获取订单信息 + $order = \app\model\ApprovalOrder::find($orderId); + $orderTitle = $order ? $order->title : '审批单'; + + // 构建操作人自己的卡片(显示操作结果和查看详情按钮) + $statusText = $status === 'approved' ? '已通过' : '已拒绝'; + $statusColor = $status === 'approved' ? 'green' : 'red'; + $statusEmoji = $status === 'approved' ? '✅' : '❌'; + + $updatedCard = [ + 'config' => [ + 'wide_screen_mode' => false, + ], + 'header' => [ + 'title' => [ + 'content' => "{$statusEmoji} 已操作", + 'tag' => 'plain_text', + ], + 'template' => $statusColor, + ], + 'elements' => [ + [ + 'tag' => 'div', + 'text' => [ + 'content' => "**审批单**: {$orderTitle}\n\n**您的操作**: {$statusText}\n\n您已完成此审批操作。", + 'tag' => 'lark_md', + ], + ], + [ + 'tag' => 'action', + 'actions' => [ + [ + 'tag' => 'button', + 'text' => [ + 'content' => '查看详情', + 'tag' => 'plain_text', + ], + 'type' => 'default', + 'url' => "https://uniservice-mobile.hnxinyouhe.com/approval/detail/{$orderId}?company={$companyId}", + ], + ], + ], + ], + ]; + + $contentJson = json_encode($updatedCard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + $response = HttpClient::httpPatchJson( + "https://open.feishu.cn/open-apis/im/v1/messages/{$message->message_id}", + [ + 'msg_type' => 'interactive', + 'content' => $contentJson, + ], + ['content'], + [ + 'Authorization: Bearer ' . $this->getAccessToken(), + ] + ); + + $code = $response['code'] ?? -1; + if ($code === 0) { + error_log("✅ 操作人消息卡片更新成功: message_id={$message->message_id}, user_id={$actorUserId}, status={$status}"); + // 标记消息为已更新 + $message->status = \app\model\FeishuMessage::STATUS_UPDATED; + $message->save(); + } else { + error_log("❌ 操作人消息卡片更新失败: message_id={$message->message_id}, error=" . json_encode($response)); + return false; + } + + return true; + } catch (Exception $e) { + error_log("❌ updateActorMessageCard 异常: " . $e->getMessage()); + return false; + } + } + /** * 更新其他审批人的消息卡片 * 多公司多飞书应用场景:使用每个消息对应的原始飞书应用去更新 diff --git a/frontend/src/pages/approval/detail.module.css b/frontend/src/pages/approval/detail.module.css index 1f2013a..9f773e1 100644 --- a/frontend/src/pages/approval/detail.module.css +++ b/frontend/src/pages/approval/detail.module.css @@ -115,6 +115,56 @@ word-break: break-all; } +.formSection { + display: flex; + flex-direction: column; + gap: 12px; +} + +.itineraryList { + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 8px; +} + +.itineraryItem { + background: #fafafa; + border-radius: 8px; + padding: 12px; + border: 1px solid #f0f0f0; +} + +.itineraryHeader { + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px dashed #e8e8e8; +} + +.itineraryIndex { + font-size: 14px; + font-weight: 600; + color: #1890ff; +} + +.itineraryRow { + display: flex; + font-size: 14px; + line-height: 1.6; + margin-bottom: 4px; +} + +.itineraryLabel { + color: #666; + width: 80px; + flex-shrink: 0; +} + +.itineraryValue { + color: #333; + flex: 1; +} + .flowNodes { display: flex; flex-direction: column; diff --git a/frontend/src/pages/approval/detail.tsx b/frontend/src/pages/approval/detail.tsx index d174df3..923ee63 100644 --- a/frontend/src/pages/approval/detail.tsx +++ b/frontend/src/pages/approval/detail.tsx @@ -600,6 +600,47 @@ export default function ApprovalDetail() { .map(([key, value]) => { // 获取字段标签,优先使用映射表,没有则使用原始key const label = formFieldLabelMap[key] || key + + // 特殊处理行程安排(itineraries) + if (key === 'itineraries' && Array.isArray(value) && value.length > 0) { + return ( +
+
{label}:
+
+ {value.map((itinerary: any, index: number) => ( +
+
+ 行程 {index + 1} +
+ {Object.entries(itinerary).map(([ikey, ivalue]) => { + if (ikey === 'id') return null + const itineraryLabelMap: Record = { + destination: '目的地', + start_date: '开始日期', + end_date: '结束日期', + estimated_cost: '预计费用', + } + const itineraryLabel = itineraryLabelMap[ikey] || ikey + let displayValue = ivalue as string + if (ikey.includes('date') && ivalue) { + displayValue = dayjs(String(ivalue)).format('YYYY-MM-DD') + } else if (ikey === 'estimated_cost' && ivalue) { + displayValue = `¥${Number(ivalue).toFixed(2)}` + } + return ( +
+ {itineraryLabel}: + {displayValue} +
+ ) + })} +
+ ))} +
+
+ ) + } + return (
{label}: