v7.0.0 release
This commit is contained in:
1 parent
0e7fa04b03
commit
c4ea9a6cd1
3582 files changed
+626189
-160151
No files matched your search
@@ -2,8 +2,11 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Role;
|
||||
use App\Contracts\Notifications\GuestRecipientInterface;
|
||||
use App\Contracts\Repositories\RoleRepositoryInterface;
|
||||
use App\Contracts\Repositories\UserRepositoryInterface;
|
||||
use App\Models\User;
|
||||
use App\Notifications\GuestNotifiable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -11,23 +14,36 @@ use Illuminate\Support\Facades\Log;
|
||||
* 알림 수신자 해석 서비스
|
||||
*
|
||||
* recipients JSON 규칙 배열을 해석하여
|
||||
* 최종 수신자 User 컬렉션을 반환합니다.
|
||||
* 최종 수신자 컬렉션(회원 User + 비회원 GuestNotifiable)을 반환합니다.
|
||||
*
|
||||
* 수신자 타입:
|
||||
* - trigger_user: 이벤트를 유발한 사용자 (주문자, 가입자 등)
|
||||
* - trigger_user: 이벤트를 유발한 사용자 (주문자, 가입자 등). 회원이면 User,
|
||||
* 비회원이면 컨텍스트의 표준 키 guest_recipient 로 GuestNotifiable 생성.
|
||||
* - related_user: 관련 사용자 (문의 답변 → 문의 작성자)
|
||||
* - role: 특정 역할의 사용자들 (admin, manager 등)
|
||||
* - permission: 특정 권한을 가진 역할에 소속된 사용자들 (예: sirsoft-board.reports.manage)
|
||||
* - specific_users: 지정된 사용자 UUID 목록
|
||||
*
|
||||
* 비회원 수신자는 user_id 없이 이메일/이름/로케일만 가진 1급 Notifiable 로,
|
||||
* 회원과 동일한 발송 경로($notifiable->notify())를 공유합니다.
|
||||
*/
|
||||
class NotificationRecipientResolver
|
||||
{
|
||||
/**
|
||||
* @param UserRepositoryInterface $userRepository 사용자 조회 Repository
|
||||
* @param RoleRepositoryInterface $roleRepository 역할 조회 Repository
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly UserRepositoryInterface $userRepository,
|
||||
private readonly RoleRepositoryInterface $roleRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* recipients 규칙 배열을 해석하여 수신자 목록을 반환합니다.
|
||||
*
|
||||
* @param array $rules 수신자 규칙 배열
|
||||
* @param array $context 컨텍스트 데이터 (trigger_user_id, related_users 등)
|
||||
* @return Collection<int, User>
|
||||
* @param array $rules 수신자 규칙 배열
|
||||
* @param array $context 컨텍스트 데이터 (trigger_user_id, guest_recipient, related_users 등)
|
||||
* @return Collection<int, object> 회원(User) + 비회원(GuestNotifiable) 혼합 가능
|
||||
*/
|
||||
public function resolve(array $rules, array $context): Collection
|
||||
{
|
||||
@@ -60,44 +76,75 @@ class NotificationRecipientResolver
|
||||
}
|
||||
}
|
||||
|
||||
// exclude_trigger_user 규칙 적용
|
||||
// exclude_trigger_user 규칙 적용 (회원 유발자 기준 — 게스트는 user_id 가 없어 자연 비대상)
|
||||
if ($triggerUserId) {
|
||||
$shouldExclude = collect($rules)->contains(fn ($rule) => ! empty($rule['exclude_trigger_user']));
|
||||
if ($shouldExclude) {
|
||||
$recipients = $recipients->filter(fn (User $user) => $user->id !== $triggerUserId);
|
||||
$recipients = $recipients->reject(
|
||||
fn (object $recipient) => isset($recipient->id) && $recipient->id === $triggerUserId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $recipients->unique('id')->values();
|
||||
// 중복 제거: 회원(user:{id})과 비회원(guest:{hash}) 키 네임스페이스를 분리해
|
||||
// null-id 게스트가 하나로 뭉개지는 것을 방지한다.
|
||||
return $recipients->unique(fn (object $recipient) => $this->recipientDedupKey($recipient))->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* 수신자 중복 제거 키를 반환합니다.
|
||||
*
|
||||
* 회원은 `user:{id}`, 비회원(게스트)은 `guest:{이메일 해시}` 네임스페이스를 사용해
|
||||
* 서로 충돌하지 않게 합니다.
|
||||
*
|
||||
* @param object $recipient 수신자 (User 또는 GuestNotifiable)
|
||||
* @return string 중복 제거 키
|
||||
*/
|
||||
private function recipientDedupKey(object $recipient): string
|
||||
{
|
||||
if ($recipient instanceof GuestRecipientInterface && $recipient->isGuest()) {
|
||||
return (string) $recipient->getKey();
|
||||
}
|
||||
|
||||
return 'user:'.($recipient->id ?? spl_object_id($recipient));
|
||||
}
|
||||
|
||||
/**
|
||||
* 이벤트 유발자를 수신자에 추가합니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $context 컨텍스트
|
||||
* @return void
|
||||
* 회원이면 trigger_user_id 로 User 를, 비회원이면 컨텍스트의 표준 키
|
||||
* guest_recipient({email, name, locale})로 GuestNotifiable 을 생성해 추가합니다.
|
||||
* 템플릿의 {"type":"trigger_user"} 규칙이 회원/비회원 분기 없이 동작합니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $context 컨텍스트
|
||||
*/
|
||||
private function addTriggerUser(Collection $recipients, array $context): void
|
||||
{
|
||||
$triggerUserId = $context['trigger_user_id'] ?? null;
|
||||
if (! $triggerUserId) {
|
||||
|
||||
if ($triggerUserId) {
|
||||
$user = $context['trigger_user'] ?? $this->userRepository->findById($triggerUserId);
|
||||
if ($user) {
|
||||
$recipients->push($user);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $context['trigger_user'] ?? User::find($triggerUserId);
|
||||
if ($user) {
|
||||
$recipients->push($user);
|
||||
// 비회원 폴백: user_id 가 없으면 표준 guest_recipient 키로 1급 게스트 수신자 생성
|
||||
$guest = $context['guest_recipient'] ?? null;
|
||||
if (is_array($guest) && ($notifiable = GuestNotifiable::fromContext($guest)) !== null) {
|
||||
$recipients->push($notifiable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관련 사용자를 수신자에 추가합니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $context 컨텍스트
|
||||
* @param array $rule 수신자 규칙 (relation 키 필요)
|
||||
* @return void
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $context 컨텍스트
|
||||
* @param array $rule 수신자 규칙 (relation 키 필요)
|
||||
*/
|
||||
private function addRelatedUser(Collection $recipients, array $context, array $rule): void
|
||||
{
|
||||
@@ -119,9 +166,8 @@ class NotificationRecipientResolver
|
||||
/**
|
||||
* 역할 기반 수신자를 추가합니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = role identifier)
|
||||
* @return void
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = role identifier)
|
||||
*/
|
||||
private function addByRole(Collection $recipients, array $rule): void
|
||||
{
|
||||
@@ -130,12 +176,11 @@ class NotificationRecipientResolver
|
||||
return;
|
||||
}
|
||||
|
||||
$role = Role::where('identifier', $roleIdentifier)->first();
|
||||
$roleUsers = $role ? $role->users()->get() : collect();
|
||||
$roleUsers = $this->roleRepository->getUsersByIdentifier($roleIdentifier);
|
||||
|
||||
// 역할 사용자가 없으면 superAdmin 폴백
|
||||
if ($roleUsers->isEmpty()) {
|
||||
$superAdmin = User::superAdmins()->first();
|
||||
$superAdmin = $this->userRepository->findSuperAdmin();
|
||||
if ($superAdmin) {
|
||||
$roleUsers = collect([$superAdmin]);
|
||||
}
|
||||
@@ -151,8 +196,8 @@ class NotificationRecipientResolver
|
||||
* role 기반 수신자와 달리 "권한을 가진 자" 기준이므로 테스트에서 권한을 제거하면
|
||||
* 자동으로 수신자 목록에서 제외됩니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = permission identifier)
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = permission identifier)
|
||||
*/
|
||||
private function addByPermission(Collection $recipients, array $rule): void
|
||||
{
|
||||
@@ -161,27 +206,15 @@ class NotificationRecipientResolver
|
||||
return;
|
||||
}
|
||||
|
||||
$permission = \App\Models\Permission::where('identifier', $permissionIdentifier)->first();
|
||||
if (! $permission) {
|
||||
return;
|
||||
}
|
||||
|
||||
$roleIds = $permission->roles()->pluck('roles.id');
|
||||
$userIds = \DB::table('user_roles')
|
||||
->whereIn('role_id', $roleIds)
|
||||
->pluck('user_id')
|
||||
->unique();
|
||||
|
||||
$users = User::whereIn('id', $userIds)->get();
|
||||
$users = $this->userRepository->findManyByPermissionIdentifier($permissionIdentifier);
|
||||
$recipients->push(...$users->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 사용자 UUID 목록을 수신자에 추가합니다.
|
||||
*
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = user UUID 배열)
|
||||
* @return void
|
||||
* @param Collection $recipients 수신자 컬렉션
|
||||
* @param array $rule 수신자 규칙 (value = user UUID 배열)
|
||||
*/
|
||||
private function addSpecificUsers(Collection $recipients, array $rule): void
|
||||
{
|
||||
@@ -190,7 +223,7 @@ class NotificationRecipientResolver
|
||||
return;
|
||||
}
|
||||
|
||||
$users = User::whereIn('uuid', $userIds)->get();
|
||||
$users = $this->userRepository->findManyByUuids($userIds);
|
||||
$recipients->push(...$users->all());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user