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 ApprovalDiscussionTrait
|
|
|
|
|
|
{
|
|
|
|
|
|
/** 是否该实例参与者 */
|
|
|
|
|
|
private function isParticipantOfInstance(int $instanceId, int $userId): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
$inst = Db::table('approval_instances')->where('id', $instanceId)->first();
|
|
|
|
|
|
if (!$inst) return false;
|
|
|
|
|
|
if ((int)$inst->initiator_id === $userId) return true;
|
|
|
|
|
|
|
|
|
|
|
|
$inRecords = Db::table('approval_records')
|
|
|
|
|
|
->where('instance_id', $instanceId)->where('approver_id', $userId)->exists();
|
|
|
|
|
|
if ($inRecords) return true;
|
|
|
|
|
|
|
|
|
|
|
|
$inCc = Db::table('approval_ccs')
|
|
|
|
|
|
->where('instance_id', $instanceId)->where('user_id', $userId)->exists();
|
|
|
|
|
|
return $inCc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 是否该实例任一步的审批人 */
|
|
|
|
|
|
private function isApproverOfInstance(int $instanceId, int $userId): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
return Db::table('approval_records')
|
|
|
|
|
|
->where('instance_id', $instanceId)
|
|
|
|
|
|
->where('approver_id', $userId)
|
|
|
|
|
|
->exists();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取讨论受众 */
|
|
|
|
|
|
private function getDiscussionAudienceUserIds(int $instanceId, bool $approversOnly, int $excludeUserId): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$uids = [];
|
|
|
|
|
|
if (!$approversOnly) {
|
|
|
|
|
|
$inst = Db::table('approval_instances')->where('id', $instanceId)->first();
|
|
|
|
|
|
if ($inst) $uids[] = (int)$inst->initiator_id;
|
|
|
|
|
|
$ccs = Db::table('approval_ccs')->where('instance_id', $instanceId)->pluck('user_id')->toArray();
|
|
|
|
|
|
foreach ($ccs as $u) $uids[] = (int)$u;
|
|
|
|
|
|
}
|
|
|
|
|
|
$approvers = Db::table('approval_records')
|
|
|
|
|
|
->where('instance_id', $instanceId)
|
|
|
|
|
|
->distinct()->pluck('approver_id')->toArray();
|
|
|
|
|
|
foreach ($approvers as $u) $uids[] = (int)$u;
|
|
|
|
|
|
|
|
|
|
|
|
$uids = array_values(array_unique(array_filter($uids, fn($u) => $u !== $excludeUserId)));
|
|
|
|
|
|
return $uids;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 发表讨论 */
|
|
|
|
|
|
public function postDiscussion(
|
|
|
|
|
|
int $instanceId,
|
|
|
|
|
|
int $userId,
|
|
|
|
|
|
string $content,
|
|
|
|
|
|
bool $isInternal = false,
|
|
|
|
|
|
array $attachments = [],
|
|
|
|
|
|
?string $userNameColumn = null
|
|
|
|
|
|
): array {
|
|
|
|
|
|
$content = trim($content);
|
|
|
|
|
|
if ($content === '') throw new BusinessException('讨论内容不能为空');
|
|
|
|
|
|
if (!$this->isParticipantOfInstance($instanceId, $userId)) {
|
|
|
|
|
|
throw new BusinessException('无权参与该实例的讨论');
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($isInternal && !$this->isApproverOfInstance($instanceId, $userId)) {
|
|
|
|
|
|
throw new BusinessException('仅审批人可发布内部讨论');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
|
|
$id = Db::table('approval_discussions')->insertGetId([
|
|
|
|
|
|
'instance_id' => $instanceId,
|
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
|
'is_internal' => $isInternal ? 1 : 0,
|
|
|
|
|
|
'content' => $content,
|
|
|
|
|
|
'attachments' => empty($attachments) ? null : json_encode($attachments, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
|
|
|
|
|
|
'created_at' => $now,
|
|
|
|
|
|
'updated_at' => $now,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 通知其他人
|
|
|
|
|
|
$inst = Db::table('approval_instances')->where('id', $instanceId)->first();
|
|
|
|
|
|
if ($inst) {
|
|
|
|
|
|
$audience = $this->getDiscussionAudienceUserIds($instanceId, $isInternal, $userId);
|
|
|
|
|
|
// 发言人姓名(可选)
|
|
|
|
|
|
$speaker = (string)$userId;
|
|
|
|
|
|
if ($userNameColumn) {
|
|
|
|
|
|
$name = Db::table('user')->where('id', $userId)->value($userNameColumn);
|
|
|
|
|
|
if ($name) $speaker = $name;
|
|
|
|
|
|
}
|
|
|
|
|
|
$snippet = function_exists('mb_strimwidth') ? mb_strimwidth($content, 0, 80, '...', 'UTF-8') : $content;
|
|
|
|
|
|
$extra = ['note' => "【讨论】{$speaker}:{$snippet}"];
|
|
|
|
|
|
|
|
|
|
|
|
$this->sendNotification(
|
|
|
|
|
|
$audience,
|
|
|
|
|
|
(object)[
|
|
|
|
|
|
'id' => (int)$inst->id,
|
|
|
|
|
|
'flow_type' => (string)$inst->flow_type,
|
|
|
|
|
|
'initiator_id' => (int)$inst->initiator_id,
|
|
|
|
|
|
'form_data' => $inst->form_data,
|
|
|
|
|
|
'biz_type' => (string)$inst->biz_type,
|
|
|
|
|
|
'biz_id' => (string)$inst->biz_id,
|
|
|
|
|
|
],
|
|
|
|
|
|
'pending',
|
|
|
|
|
|
false,
|
|
|
|
|
|
$extra
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'id' => (int)$id,
|
|
|
|
|
|
'instance_id' => $instanceId,
|
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
|
'is_internal' => (bool)$isInternal,
|
|
|
|
|
|
'content' => $content,
|
|
|
|
|
|
'attachments' => $attachments,
|
|
|
|
|
|
'created_at' => $now,
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取讨论列表 */
|
|
|
|
|
|
public function listDiscussions(int $instanceId, ?int $afterId = null, int $limit = 50, ?string $userNameColumn = null): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$q = Db::table('approval_discussions as d')->where('d.instance_id', $instanceId);
|
|
|
|
|
|
if ($afterId) $q->where('d.id', '>', $afterId);
|
|
|
|
|
|
$q->orderBy('d.id', 'asc')->limit(max(1, min(200, $limit)));
|
|
|
|
|
|
|
|
|
|
|
|
if ($userNameColumn) {
|
|
|
|
|
|
$rows = $q->leftJoin('user as u', 'd.user_id', '=', 'u.id')
|
|
|
|
|
|
->select('d.*', Db::raw("u.`{$userNameColumn}` as user_name"))
|
|
|
|
|
|
->get()->toArray();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$rows = $q->select('d.*')->get()->toArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return array_map(function($r){
|
|
|
|
|
|
return [
|
|
|
|
|
|
'id' => (int)$r->id,
|
|
|
|
|
|
'instance_id' => (int)$r->instance_id,
|
|
|
|
|
|
'user_id' => (int)$r->user_id,
|
|
|
|
|
|
'is_internal' => (bool)$r->is_internal,
|
|
|
|
|
|
'content' => $r->content,
|
|
|
|
|
|
'attachments' => $r->attachments ? json_decode($r->attachments, true) : [],
|
|
|
|
|
|
'created_at' => $r->created_at,
|
|
|
|
|
|
'updated_at' => $r->updated_at,
|
|
|
|
|
|
'user_name' => property_exists($r, 'user_name') ? $r->user_name : null,
|
|
|
|
|
|
];
|
|
|
|
|
|
}, $rows);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|