882 lines
34 KiB
PHP
882 lines
34 KiB
PHP
<?php
|
||
|
||
namespace app\service;
|
||
|
||
use app\model\ApprovalFlow;
|
||
use app\model\ApprovalNode;
|
||
use app\model\ApprovalOrder;
|
||
use app\model\ApprovalRecord;
|
||
use app\model\User;
|
||
use support\Db;
|
||
use Illuminate\Database\Connection;
|
||
|
||
class ApprovalService
|
||
{
|
||
private Connection $db;
|
||
private ?FeishuNotificationService $notificationService = null;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->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->applicant_id === $approverId) {
|
||
error_log('🔄 检测到自审批,自动通过');
|
||
return $this->autoApproveSelfNode($order, $approverId);
|
||
}
|
||
|
||
if (!$order->canApprove()) {
|
||
throw new \Exception('当前状态无法审批');
|
||
}
|
||
|
||
// 确保 notificationService 已初始化
|
||
if (!$this->notificationService) {
|
||
$this->notificationService = new FeishuNotificationService();
|
||
}
|
||
|
||
$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('当前节点不存在');
|
||
}
|
||
|
||
// 检查当前用户是否已经在当前节点审批过
|
||
$existingRecord = ApprovalRecord::where('order_id', $order->id)
|
||
->where('node_id', $currentNode->id)
|
||
->where('approver_id', $approverId)
|
||
->whereIn('action', [ApprovalRecord::ACTION_APPROVE, ApprovalRecord::ACTION_REJECT])
|
||
->first();
|
||
|
||
if ($existingRecord) {
|
||
$actionText = $existingRecord->action === ApprovalRecord::ACTION_APPROVE ? '通过' : '拒绝';
|
||
throw new \Exception("您已经{$actionText}过此申请,无需重复操作");
|
||
}
|
||
|
||
// 记录审批操作
|
||
ApprovalRecord::create([
|
||
'order_id' => $order->id,
|
||
'node_id' => $currentNode->id,
|
||
'node_type' => $currentNode->type,
|
||
'approver_id' => $approverId,
|
||
'action' => ApprovalRecord::ACTION_APPROVE,
|
||
'comment' => $comment,
|
||
]);
|
||
|
||
// 判断节点是否完成
|
||
$nodeCompleted = $this->isNodeCompleted($order->id, $currentNode->id);
|
||
|
||
// 会签场景:即使节点未完成,也要更新其他审批人的消息卡片(显示进度)
|
||
if (!$nodeCompleted && $currentNode->type === ApprovalNode::TYPE_AND_APPROVE) {
|
||
$approver = User::find($approverId);
|
||
$approverName = $approver ? $approver->name : '未知';
|
||
|
||
// 获取已通过人数和总人数
|
||
$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;
|
||
}
|
||
$totalCount = count($approverIds);
|
||
|
||
$approvedRecords = ApprovalRecord::where('order_id', $order->id)
|
||
->where('node_id', $currentNode->id)
|
||
->where('action', 'approve')
|
||
->get();
|
||
$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,
|
||
$approverId,
|
||
$approverName,
|
||
$approvedCount,
|
||
$totalCount
|
||
);
|
||
}
|
||
}
|
||
|
||
if ($nodeCompleted) {
|
||
// 节点完成:对于或签和单人审批,更新其他审批人的消息卡片
|
||
if ($currentNode->type === ApprovalNode::TYPE_OR_APPROVE || $currentNode->type === 'approver') {
|
||
$approver = User::find($approverId);
|
||
$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,
|
||
$approverId,
|
||
'approved',
|
||
$approverName
|
||
);
|
||
}
|
||
}
|
||
|
||
// 移动到下一节点
|
||
$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->applicant_id === $approverId) {
|
||
error_log('🔄 检测到自审批拒绝,自动取消申请');
|
||
return $this->cancel($orderId, $approverId);
|
||
}
|
||
|
||
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,
|
||
]);
|
||
|
||
// 更新其他审批人的消息卡片(拒绝时所有人都需要更新)
|
||
$approver = User::find($approverId);
|
||
$approverName = $approver ? $approver->name : '未知';
|
||
|
||
// 确保 notificationService 已初始化
|
||
if (!$this->notificationService) {
|
||
$this->notificationService = new FeishuNotificationService();
|
||
}
|
||
|
||
if ($currentNode) {
|
||
// 更新操作人自己的消息卡片
|
||
$this->notificationService->setAppByUser($approverId);
|
||
$this->notificationService->updateActorMessageCard(
|
||
$order->id,
|
||
$currentNode->id,
|
||
$approverId,
|
||
'rejected'
|
||
);
|
||
|
||
// 更新其他审批人的消息卡片
|
||
$this->notificationService->updateMessageCards(
|
||
$order->id,
|
||
$currentNode->id,
|
||
$approverId,
|
||
'rejected',
|
||
$approverName
|
||
);
|
||
}
|
||
|
||
// 更新状态为已拒绝
|
||
$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 getDetail(int $orderId): array
|
||
{
|
||
$order = ApprovalOrder::with(['flow.nodes', 'applicant', 'records.approver'])
|
||
->findOrFail($orderId);
|
||
|
||
// 获取 form_data 中的实际审批人和抄送人
|
||
$formData = $order->form_data ?? [];
|
||
$actualApprovers = $formData['approvers'] ?? [];
|
||
$actualCcUsers = $formData['cc_users'] ?? [];
|
||
|
||
// 兼容两种格式:对象数组 [{id:1, name:'xxx'}] 或纯ID数组 [1, 2, 3]
|
||
if (!empty($actualApprovers) && isset($actualApprovers[0]['id'])) {
|
||
$actualApproverIds = array_column($actualApprovers, 'id');
|
||
} else {
|
||
$actualApproverIds = $actualApprovers;
|
||
}
|
||
|
||
if (!empty($actualCcUsers) && isset($actualCcUsers[0]['id'])) {
|
||
$actualCcUserIds = array_column($actualCcUsers, 'id');
|
||
} else {
|
||
$actualCcUserIds = $actualCcUsers;
|
||
}
|
||
|
||
// 获取实际审批人的完整用户信息
|
||
$actualApproverUsers = [];
|
||
$actualApproverIdsArray = [];
|
||
if (!empty($actualApproverIds)) {
|
||
$users = User::whereIn('id', $actualApproverIds)->get()->keyBy('id');
|
||
foreach ($actualApproverIds as $approverId) {
|
||
if ($users->has($approverId)) {
|
||
$actualApproverUsers[$approverId] = $users->get($approverId)->toArray();
|
||
} else {
|
||
// 用户不存在,至少保留ID信息
|
||
$actualApproverUsers[$approverId] = [
|
||
'id' => $approverId,
|
||
'name' => '用户' . $approverId . '(不存在)',
|
||
'status' => 0,
|
||
];
|
||
}
|
||
$actualApproverIdsArray[] = (int)$approverId;
|
||
}
|
||
}
|
||
|
||
// 获取实际抄送人的完整用户信息
|
||
$actualCcUserObjects = [];
|
||
$actualCcUserIdsArray = [];
|
||
if (!empty($actualCcUserIds)) {
|
||
$users = User::whereIn('id', $actualCcUserIds)->get()->keyBy('id');
|
||
foreach ($actualCcUserIds as $ccUserId) {
|
||
if ($users->has($ccUserId)) {
|
||
$actualCcUserObjects[$ccUserId] = $users->get($ccUserId)->toArray();
|
||
} else {
|
||
// 用户不存在,至少保留ID信息
|
||
$actualCcUserObjects[$ccUserId] = [
|
||
'id' => $ccUserId,
|
||
'name' => '用户' . $ccUserId . '(不存在)',
|
||
'status' => 0,
|
||
];
|
||
}
|
||
$actualCcUserIdsArray[] = (int)$ccUserId;
|
||
}
|
||
}
|
||
|
||
// 处理节点信息,添加实际审批人
|
||
$nodes = [];
|
||
if ($order->flow && $order->flow->nodes) {
|
||
foreach ($order->flow->nodes as $node) {
|
||
$nodeArray = $node->toArray();
|
||
|
||
// 为审批节点添加实际审批人信息
|
||
if (in_array($node->type, ['approver', 'and_approve', 'or_approve'])) {
|
||
$nodeArray['actual_approvers'] = [];
|
||
foreach ($actualApproverIdsArray as $approverId) {
|
||
if (isset($actualApproverUsers[$approverId])) {
|
||
$nodeArray['actual_approvers'][] = $actualApproverUsers[$approverId];
|
||
}
|
||
}
|
||
|
||
// 获取每个实际审批人的审批状态
|
||
$nodeArray['approver_statuses'] = [];
|
||
foreach ($actualApproverIdsArray as $approverId) {
|
||
$record = ApprovalRecord::where('order_id', $orderId)
|
||
->where('node_id', $node->id)
|
||
->where('approver_id', $approverId)
|
||
->whereIn('action', ['approve', 'reject'])
|
||
->first();
|
||
|
||
$nodeArray['approver_statuses'][] = [
|
||
'user_id' => $approverId,
|
||
'user' => $actualApproverUsers[$approverId] ?? null,
|
||
'action' => $record->action ?? null,
|
||
'action_name' => $record ? ($record->action === 'approve' ? '已通过' : '已拒绝') : '待审批',
|
||
'comment' => $record->comment ?? null,
|
||
'created_at' => $record->created_at ?? null,
|
||
];
|
||
}
|
||
}
|
||
|
||
// 为通知节点添加实际抄送人
|
||
if ($node->type === 'notify') {
|
||
$nodeArray['actual_cc_users'] = [];
|
||
foreach ($actualCcUserIdsArray as $ccUserId) {
|
||
if (isset($actualCcUserObjects[$ccUserId])) {
|
||
$nodeArray['actual_cc_users'][] = $actualCcUserObjects[$ccUserId];
|
||
}
|
||
}
|
||
}
|
||
|
||
$nodes[] = $nodeArray;
|
||
}
|
||
}
|
||
|
||
// 构建返回数据
|
||
$result = $order->toArray();
|
||
$result['flow']['nodes'] = $nodes;
|
||
$result['actual_approvers'] = array_values($actualApproverUsers);
|
||
$result['actual_cc_users'] = array_values($actualCcUserObjects);
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 获取审批列表
|
||
*/
|
||
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));
|
||
|
||
// 获取第一个下一节点的 ID(用于消息映射)
|
||
$nextNodeId = $nextNodes[0]['id'] ?? null;
|
||
|
||
// 为每个审批人发送通知
|
||
foreach ($approverIds as $approverId) {
|
||
try {
|
||
$this->notificationService->setAppByUser($approverId)
|
||
->sendApprovalNotification([
|
||
'user_id' => $approverId,
|
||
'applicant_id' => $order->applicant_id,
|
||
'title' => '新的审批待办',
|
||
'applicant' => $order->applicant->name,
|
||
'type' => $businessTypeName,
|
||
'content' => "**申请标题**: {$order->title}\n**申请人**: {$order->applicant->name}",
|
||
'url' => $detailUrl,
|
||
'order_id' => $order->id,
|
||
'node_id' => $nextNodeId, // 添加节点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');
|
||
|
||
// 如果没有配置,使用当前请求构建
|
||
if (!$baseUrl) {
|
||
// 检查是否在 CLI 环境
|
||
if (PHP_SAPI === 'cli' || !request()) {
|
||
// CLI 环境:使用默认域名
|
||
$baseUrl = 'https://uniservice-mobile.hnxinyouhe.com';
|
||
} else {
|
||
// Web 环境:检查反向代理的协议头
|
||
$scheme = request()->header('x-forwarded-proto') ?? request()->header('x-scheme') ?? 'https';
|
||
$host = request()->host();
|
||
$baseUrl = $scheme . '://' . $host;
|
||
}
|
||
}
|
||
|
||
// 统一使用审批详情页,不再跳转到具体业务模块的详情页
|
||
return "{$baseUrl}/approval/detail/{$order->id}";
|
||
}
|
||
|
||
/**
|
||
* 获取业务类型名称
|
||
*/
|
||
private function getBusinessTypeName(string $businessType): string
|
||
{
|
||
$typeNames = [
|
||
'business_trip' => '出差申请',
|
||
'expense' => '费用报销',
|
||
'leave' => '请假申请',
|
||
'purchase' => '采购申请',
|
||
];
|
||
|
||
return $typeNames[$businessType] ?? $businessType;
|
||
}
|
||
|
||
/**
|
||
* 自动通过申请人自己的审批节点
|
||
* 当审批人是申请人自己时,自动通过当前节点
|
||
*/
|
||
private function autoApproveSelfNode(ApprovalOrder $order, int $approverId): ApprovalOrder
|
||
{
|
||
$currentNodeIds = $order->current_nodes ?? [];
|
||
error_log("🔄 自审批自动通过 - 当前节点: " . json_encode($currentNodeIds));
|
||
|
||
// 检查订单是否已经结束
|
||
if (empty($currentNodeIds)) {
|
||
error_log("❌ 自审批:订单已完成,current_nodes为空");
|
||
if ($order->status === ApprovalOrder::STATUS_APPROVED) {
|
||
throw new \Exception('该申请已通过,无需重复操作');
|
||
} else if ($order->status === ApprovalOrder::STATUS_REJECTED) {
|
||
throw new \Exception('该申请已被拒绝');
|
||
} else if ($order->status === ApprovalOrder::STATUS_CANCELLED) {
|
||
throw new \Exception('该申请已被撤销');
|
||
}
|
||
throw new \Exception('该申请已完成,无法重复操作');
|
||
}
|
||
|
||
$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' => '系统自动通过(申请人自审批)',
|
||
]);
|
||
|
||
// 检查节点是否完成(单人审批节点会完成,会签节点需要所有人审批)
|
||
if ($this->isNodeCompleted($order->id, $currentNode->id)) {
|
||
error_log("✅ 自审批节点完成,移动到下一节点");
|
||
|
||
// 移动到下一节点
|
||
$nextNodes = $this->getNextNodes($order->flow, $currentNode->order);
|
||
|
||
if (empty($nextNodes)) {
|
||
// 流程结束
|
||
$order->status = ApprovalOrder::STATUS_APPROVED;
|
||
$order->current_nodes = [];
|
||
error_log("✅ 自审批完成整个流程");
|
||
|
||
// 通知申请人审批通过
|
||
$this->notifyApplicant($order, 'approve');
|
||
} else {
|
||
// 下一个是通知节点或审批节点
|
||
$isNotify = collect($nextNodes)->every(fn($n) => $n->type === 'notify');
|
||
|
||
if ($isNotify) {
|
||
// 下一个是通知节点,流程结束
|
||
$order->status = ApprovalOrder::STATUS_APPROVED;
|
||
$order->current_nodes = [];
|
||
|
||
error_log("✅ 自审批后到达通知节点,流程完成");
|
||
|
||
// 通知申请人审批通过
|
||
$this->notifyApplicant($order, 'approve');
|
||
} else {
|
||
// 下一个是审批节点,移动到该节点
|
||
$order->current_nodes = collect($nextNodes)->pluck('id')->toArray();
|
||
error_log("➡️ 自审批后移动到下一节点: " . json_encode($order->current_nodes));
|
||
|
||
// 通知下一节点的审批人
|
||
$this->notifyNextApprovers($order, $nextNodes);
|
||
}
|
||
}
|
||
} else {
|
||
error_log("⏳ 自审批节点未完成(会签节点),等待其他审批人");
|
||
}
|
||
|
||
$order->save();
|
||
return $order->fresh(['flow', 'applicant', 'records']);
|
||
}
|
||
}
|