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); } }