fix: 修复审批消息推送问题

问题1:BusinessTripService 数据格式兼容性
- 后端期望对象数组 [{id:1, name:'...'}],前端传纯 ID 数组 [1,2,3]
- 修复:兼容两种格式,自动提取 id

问题2:ApprovalService 没有消息推送
- 添加 FeishuNotificationService 集成
- approve() 方法:节点完成后通知下一节点审批人,流程完成后通知申请人
- reject() 方法:审批拒绝时通知申请人

问题3:修复重复类定义导致的语法错误

相关文件:
- backend/app/service/BusinessTripService.php
- backend/app/service/ApprovalService.php

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-26 18:49:25 +08:00
parent ee5f7791de
commit 32d19d56ee
2 changed files with 159 additions and 2 deletions
+147
View File
@@ -13,6 +13,7 @@ use Illuminate\Database\Connection;
class ApprovalService
{
private Connection $db;
private ?FeishuNotificationService $notificationService = null;
public function __construct()
{
@@ -96,6 +97,9 @@ class ApprovalService
// 没有下一节点,审批完成
$order->status = ApprovalOrder::STATUS_APPROVED;
$order->current_nodes = [];
// 通知申请人审批通过
$this->notifyApplicant($order, 'approve');
} else {
// 检查第一个下一节点
$firstNextNode = $nextNodes[0] ?? null;
@@ -103,9 +107,15 @@ class ApprovalService
// 下一个是抄送节点,跳过并完成流程
$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);
}
}
@@ -146,6 +156,9 @@ class ApprovalService
$order->current_nodes = [];
$order->save();
// 通知申请人审批被拒绝
$this->notifyApplicant($order, 'reject', $comment);
return $order->fresh(['flow', 'applicant', 'records']);
});
}
@@ -381,4 +394,138 @@ class ApprovalService
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,
]);
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 = 'https://xsm.dev.hnxinyouhe.com';
switch ($order->business_type) {
case 'business_trip':
return "{$baseUrl}/business-trip/detail/{$order->business_id}";
case 'expense':
return "{$baseUrl}/expense/detail/{$order->business_id}";
case 'leave':
return "{$baseUrl}/leave/detail/{$order->business_id}";
case 'purchase':
return "{$baseUrl}/purchase/detail/{$order->business_id}";
default:
return "{$baseUrl}/approval/detail/{$order->id}";
}
}
/**
* 获取业务类型名称
*/
private function getBusinessTypeName(string $businessType): string
{
$typeNames = [
'business_trip' => '出差申请',
'expense' => '费用报销',
'leave' => '请假申请',
'purchase' => '采购申请',
];
return $typeNames[$businessType] ?? $businessType;
}
}
+12 -2
View File
@@ -104,8 +104,18 @@ class BusinessTripService
}
// 验证审批人和抄送人不能重复
$approverIds = array_column($approvers, 'id');
$ccUserIds = array_column($ccUsers, 'id');
// 兼容两种格式:对象数组 [{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))) {