131 lines
5.4 KiB
PHP
131 lines
5.4 KiB
PHP
<?php
|
||
namespace app\common\utils\approval\traits;
|
||
|
||
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];
|
||
});
|
||
}
|
||
}
|