2025-12-09 07:33:04 +00:00
|
|
|
|
<?php
|
2026-01-07 17:23:03 +08:00
|
|
|
|
namespace app\common\utils\approval\traits;
|
2025-12-09 07:33:04 +00:00
|
|
|
|
|
|
|
|
|
|
use support\Db;
|
|
|
|
|
|
use support\exception\BusinessException;
|
|
|
|
|
|
|
|
|
|
|
|
trait ApprovalTransferTrait
|
|
|
|
|
|
{
|
|
|
|
|
|
/** 查看某个实例的转交流水(按时间升序) */
|
|
|
|
|
|
public function listTransfers(int $instanceId): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$rows = Db::table('approval_transfers')
|
|
|
|
|
|
->where('instance_id', $instanceId)
|
|
|
|
|
|
->orderBy('id', 'asc')
|
|
|
|
|
|
->get()
|
|
|
|
|
|
->toArray();
|
|
|
|
|
|
|
|
|
|
|
|
return array_map(function($r){
|
|
|
|
|
|
return [
|
|
|
|
|
|
'id' => (int)$r->id,
|
|
|
|
|
|
'instance_id' => (int)$r->instance_id,
|
|
|
|
|
|
'step_index' => (int)$r->step_index,
|
|
|
|
|
|
'record_id' => (int)$r->record_id,
|
|
|
|
|
|
'from_user_id' => (int)$r->from_user_id,
|
|
|
|
|
|
'to_user_id' => (int)$r->to_user_id,
|
|
|
|
|
|
'reason' => $r->reason,
|
|
|
|
|
|
'created_at' => $r->created_at,
|
|
|
|
|
|
];
|
|
|
|
|
|
}, $rows);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 转交当前待办给他人 */
|
|
|
|
|
|
public function transferApproval(
|
|
|
|
|
|
int $instanceId,
|
|
|
|
|
|
int $stepIndex,
|
|
|
|
|
|
int $fromUserId,
|
|
|
|
|
|
int $toUserId,
|
|
|
|
|
|
string $reason = '',
|
|
|
|
|
|
?string $userNameColumn = null
|
|
|
|
|
|
): array {
|
|
|
|
|
|
if ($fromUserId === $toUserId) {
|
|
|
|
|
|
return ['status' => 'noop', 'msg' => '目标与来源相同,无需转交'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return \support\Db::transaction(function () use ($instanceId, $stepIndex, $fromUserId, $toUserId, $reason, $userNameColumn) {
|
|
|
|
|
|
// 锁实例,避免并发推进
|
|
|
|
|
|
$instance = Db::table('approval_instances')->where('id', $instanceId)->lockForUpdate()->first();
|
|
|
|
|
|
if (!$instance) throw new BusinessException('审批实例未找到');
|
|
|
|
|
|
if ($instance->status !== 'processing') throw new BusinessException('流程非进行中,无法转交');
|
|
|
|
|
|
|
|
|
|
|
|
// 锁定来源人的 pending 记录
|
|
|
|
|
|
$record = Db::table('approval_records')
|
|
|
|
|
|
->where('instance_id', $instanceId)
|
|
|
|
|
|
->where('round', (int)$instance->round)
|
|
|
|
|
|
->where('step_index', $stepIndex)
|
|
|
|
|
|
->where('approver_id', $fromUserId)
|
|
|
|
|
|
->where('status', 'pending')
|
|
|
|
|
|
->lockForUpdate()
|
|
|
|
|
|
->first();
|
|
|
|
|
|
if (!$record) throw new BusinessException('未找到可转交的待办或已处理');
|
|
|
|
|
|
|
|
|
|
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
|
|
|
|
|
|
|
|
// 若目标已有 pending:合并(本记录置为 skipped)
|
|
|
|
|
|
$target = Db::table('approval_records')
|
|
|
|
|
|
->where('instance_id', $instanceId)
|
|
|
|
|
|
->where('round', (int)$instance->round)
|
|
|
|
|
|
->where('step_index', $stepIndex)
|
|
|
|
|
|
->where('approver_id', $toUserId)
|
|
|
|
|
|
->where('status', 'pending')
|
|
|
|
|
|
->lockForUpdate()
|
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
|
|
if ($target) {
|
|
|
|
|
|
Db::table('approval_records')->where('id', $record->id)->update([
|
|
|
|
|
|
'status' => 'skipped',
|
|
|
|
|
|
'approved_at' => $now,
|
|
|
|
|
|
'updated_at' => $now,
|
|
|
|
|
|
'comment' => trim(($record->comment ?? '')."\n[转交给 {$toUserId},目标已有待办,已合并]"),
|
|
|
|
|
|
]);
|
|
|
|
|
|
$effectiveRecordId = (int)$target->id;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 原地替换 approver_id
|
|
|
|
|
|
Db::table('approval_records')->where('id', $record->id)->update([
|
|
|
|
|
|
'approver_id' => $toUserId,
|
|
|
|
|
|
'updated_at' => $now,
|
|
|
|
|
|
'comment' => trim(($record->comment ?? '')."\n[自 {$fromUserId} 转交]"),
|
|
|
|
|
|
]);
|
|
|
|
|
|
$effectiveRecordId = (int)$record->id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 组织“转交来源/原因”文案(优先姓名)
|
|
|
|
|
|
$fromLabel = (string)$fromUserId;
|
|
|
|
|
|
$toLabel = (string)$toUserId;
|
|
|
|
|
|
if ($userNameColumn) {
|
|
|
|
|
|
$fromName = Db::table('user')->where('id', $fromUserId)->value($userNameColumn);
|
|
|
|
|
|
$toName = Db::table('user')->where('id', $toUserId)->value($userNameColumn);
|
|
|
|
|
|
if ($fromName) $fromLabel = $fromName;
|
|
|
|
|
|
if ($toName) $toLabel = $toName;
|
|
|
|
|
|
}
|
|
|
|
|
|
$note = "【转交】{$fromLabel} → {$toLabel}";
|
|
|
|
|
|
$r = trim($reason);
|
|
|
|
|
|
if ($r !== '') $note .= ",原因:{$r}";
|
|
|
|
|
|
|
|
|
|
|
|
// 通知被转交人(在内容里追加 note)
|
|
|
|
|
|
$this->sendNotification([$toUserId], (object)[
|
|
|
|
|
|
'id' => (int)$instance->id,
|
|
|
|
|
|
'flow_type' => (string)$instance->flow_type,
|
|
|
|
|
|
'biz_type' => (string)$instance->biz_type,
|
|
|
|
|
|
'biz_id' => (string)$instance->biz_id,
|
|
|
|
|
|
'initiator_id' => (int)$instance->initiator_id,
|
|
|
|
|
|
'form_data' => $instance->form_data,
|
|
|
|
|
|
], 'pending', false, ['note' => $note]);
|
|
|
|
|
|
|
|
|
|
|
|
// 记转交流水
|
|
|
|
|
|
Db::table('approval_transfers')->insert([
|
|
|
|
|
|
'instance_id' => (int)$instanceId,
|
|
|
|
|
|
'step_index' => (int)$stepIndex,
|
|
|
|
|
|
'record_id' => $effectiveRecordId,
|
|
|
|
|
|
'from_user_id' => (int)$fromUserId,
|
|
|
|
|
|
'to_user_id' => (int)$toUserId,
|
|
|
|
|
|
'reason' => $reason,
|
|
|
|
|
|
'created_at' => $now,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$this->pushTodoChanged([$fromUserId, $toUserId]);
|
|
|
|
|
|
return ['status' => 'ok', 'record_id' => $effectiveRecordId];
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|