2026-02-24 17:57:25 +08:00
|
|
|
<?php
|
|
|
|
|
|
2026-02-25 18:09:04 +08:00
|
|
|
namespace app\service;
|
2026-02-24 17:57:25 +08:00
|
|
|
|
2026-02-25 18:09:04 +08:00
|
|
|
use app\model\ApprovalFlow;
|
|
|
|
|
use app\model\ApprovalNode;
|
|
|
|
|
use app\model\ApprovalOrder;
|
|
|
|
|
use app\model\ApprovalRecord;
|
|
|
|
|
use app\model\User;
|
|
|
|
|
use app\util\Db;
|
2026-02-24 17:57:25 +08:00
|
|
|
use Illuminate\Database\Connection;
|
|
|
|
|
|
|
|
|
|
class ApprovalService
|
|
|
|
|
{
|
|
|
|
|
private Connection $db;
|
|
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
// Initialize database if not already initialized
|
|
|
|
|
Db::init();
|
|
|
|
|
$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->canApprove()) {
|
|
|
|
|
throw new \Exception('当前状态无法审批');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$currentNodeIds = $order->current_nodes ?? [];
|
|
|
|
|
$currentNode = ApprovalNode::whereIn('id', $currentNodeIds)->first();
|
|
|
|
|
|
|
|
|
|
if (!$currentNode) {
|
|
|
|
|
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 = [];
|
|
|
|
|
} else {
|
|
|
|
|
$order->current_nodes = collect($nextNodes)->pluck('id')->toArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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();
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
break;
|
|
|
|
|
case 'pending':
|
|
|
|
|
// 获取用户待审批的订单
|
|
|
|
|
$pendingOrderIds = $this->getPendingOrderIds($userId);
|
|
|
|
|
$query->whereIn('id', $pendingOrderIds);
|
|
|
|
|
break;
|
|
|
|
|
case 'approved':
|
|
|
|
|
// 获取用户已审批的订单
|
|
|
|
|
$approvedOrderIds = ApprovalRecord::where('approver_id', $userId)
|
|
|
|
|
->where('action', '!=', ApprovalRecord::ACTION_SUBMIT)
|
|
|
|
|
->pluck('order_id')
|
|
|
|
|
->unique()
|
|
|
|
|
->toArray();
|
|
|
|
|
$query->whereIn('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,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取第一个节点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', ApprovalRecord::ACTION_APPROVE)
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
$approvers = $node->approvers ?? [];
|
|
|
|
|
|
|
|
|
|
if ($node->type === ApprovalNode::TYPE_AND_APPROVE) {
|
|
|
|
|
// 会签:需要所有人通过
|
|
|
|
|
return count($records) >= count($approvers);
|
|
|
|
|
} 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) {
|
|
|
|
|
$currentNodeIds = $order->current_nodes ?? [];
|
|
|
|
|
$nodes = ApprovalNode::whereIn('id', $currentNodeIds)->get();
|
|
|
|
|
|
|
|
|
|
foreach ($nodes as $node) {
|
|
|
|
|
$approvers = $node->approvers ?? [];
|
|
|
|
|
if (in_array($userId, $approvers)) {
|
|
|
|
|
// 检查是否已审批
|
|
|
|
|
$hasApproved = ApprovalRecord::where('order_id', $order->id)
|
|
|
|
|
->where('node_id', $node->id)
|
|
|
|
|
->where('approver_id', $userId)
|
|
|
|
|
->exists();
|
|
|
|
|
|
|
|
|
|
if (!$hasApproved) {
|
|
|
|
|
$result[] = $order->id;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
}
|