update api register
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Services\Notification;
|
||||
|
||||
use App\Jobs\ProcessBroadcast;
|
||||
use App\Jobs\ProcessNotificationDelivery;
|
||||
use App\Jobs\ProcessSystemNotificationCampaign;
|
||||
use App\Models\Customer;
|
||||
use App\Models\CustomerContact;
|
||||
use App\Models\NotificationBroadcast;
|
||||
@@ -14,6 +15,7 @@ use App\Models\NotificationEvent;
|
||||
use App\Models\NotificationMessage;
|
||||
use App\Models\NotificationPreference;
|
||||
use App\Models\NotificationTemplate;
|
||||
use App\Models\SystemNotificationCampaign;
|
||||
use App\Models\TopologyLink;
|
||||
use App\Models\User;
|
||||
use App\Models\UserNotification;
|
||||
@@ -32,29 +34,126 @@ class NotificationService
|
||||
return NotificationChannel::with('tenant:id,tenant_code,tenant_name')
|
||||
->where('channel', $channel)
|
||||
->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId))
|
||||
->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId))
|
||||
->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where(
|
||||
fn ($scope) => $scope->where('tenant_id', $tenantId)->orWhereNull('tenant_id')
|
||||
))
|
||||
->orderByDesc('is_default')->orderBy('name')->get()
|
||||
->each(fn ($item) => $item->setAttribute('credentials_configured', ! empty($item->getRawOriginal('credentials'))));
|
||||
}
|
||||
|
||||
public function systemCampaigns(array $filters, User $actor, ?int $tenantId)
|
||||
{
|
||||
return SystemNotificationCampaign::with(['tenant:id,tenant_code,tenant_name', 'creator:id,name'])
|
||||
->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId))
|
||||
->when($actor->isMasterAdmin() && ! empty($filters['tenant_id']), fn ($q) => $q->where('tenant_id', $filters['tenant_id']))
|
||||
->when(! empty($filters['status']), fn ($q) => $q->where('status', $filters['status']))
|
||||
->latest('id')->paginate($filters['per_page'] ?? 10);
|
||||
}
|
||||
|
||||
public function createSystemCampaign(array $data, User $actor, ?int $tenantId): SystemNotificationCampaign
|
||||
{
|
||||
$effectiveTenantId = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? null) : $tenantId;
|
||||
if (! $actor->isMasterAdmin() && ! $effectiveTenantId) {
|
||||
throw ValidationException::withMessages(['tenant_id' => 'Tenant aktif wajib tersedia.']);
|
||||
}
|
||||
if (! $actor->isMasterAdmin() && ($data['target_scope'] ?? null) === 'all_users') {
|
||||
throw ValidationException::withMessages(['target_scope' => 'Hanya Master Admin yang dapat mengirim ke semua user.']);
|
||||
}
|
||||
if (($data['target_scope'] ?? null) === 'selected_users' && empty($data['user_ids'])) {
|
||||
throw ValidationException::withMessages(['user_ids' => 'Pilih minimal satu user penerima.']);
|
||||
}
|
||||
if ($effectiveTenantId && ! empty($data['user_ids'])) {
|
||||
$validCount = User::whereIn('id', $data['user_ids'])
|
||||
->whereHas('userTenants', fn ($q) => $q->where('tenant_id', $effectiveTenantId))->count();
|
||||
if ($validCount !== count(array_unique($data['user_ids']))) {
|
||||
throw ValidationException::withMessages(['user_ids' => 'Terdapat user di luar tenant aktif.']);
|
||||
}
|
||||
}
|
||||
|
||||
$campaign = SystemNotificationCampaign::create([
|
||||
'uuid' => Str::uuid(),
|
||||
'tenant_id' => $effectiveTenantId,
|
||||
'created_by' => $actor->id,
|
||||
'title' => $data['title'],
|
||||
'body' => $data['body'],
|
||||
'action_url' => $data['action_url'] ?? null,
|
||||
'icon' => $data['icon'] ?? 'cil-bell',
|
||||
'audience' => [
|
||||
'target_scope' => $data['target_scope'],
|
||||
'statuses' => $data['statuses'] ?? [],
|
||||
'access_levels' => $data['access_levels'] ?? [],
|
||||
'user_ids' => $data['user_ids'] ?? [],
|
||||
],
|
||||
]);
|
||||
ProcessSystemNotificationCampaign::dispatch($campaign->id)->onQueue('system-notifications');
|
||||
|
||||
return $campaign;
|
||||
}
|
||||
|
||||
public function systemAudienceOptions(User $actor, ?int $tenantId, ?int $requestedTenantId, ?string $search)
|
||||
{
|
||||
$effectiveTenantId = $actor->isMasterAdmin() ? $requestedTenantId : $tenantId;
|
||||
|
||||
return User::query()
|
||||
->when($effectiveTenantId, fn ($q) => $q->whereHas('userTenants', fn ($x) => $x->where('tenant_id', $effectiveTenantId)))
|
||||
->when($search, fn ($q, $term) => $q->where(fn ($x) => $x
|
||||
->where('name', 'ILIKE', "%{$term}%")
|
||||
->orWhere('username', 'ILIKE', "%{$term}%")
|
||||
->orWhere('email', 'ILIKE', "%{$term}%")))
|
||||
->with(['userTenants' => fn ($q) => $q->when($effectiveTenantId, fn ($x) => $x->where('tenant_id', $effectiveTenantId))])
|
||||
->orderBy('name')->limit(50)->get(['id', 'name', 'username', 'email', 'access_level', 'status'])
|
||||
->map(fn (User $user) => [
|
||||
...$user->only(['id', 'name', 'username', 'email', 'status']),
|
||||
'access_level' => $user->access_level->value,
|
||||
'tenant_role' => $user->userTenants->first()?->role,
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveChannel(array $data, User $actor, ?int $tenantId, ?NotificationChannel $channel = null): NotificationChannel
|
||||
{
|
||||
$effectiveTenant = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $channel?->tenant_id ?? $tenantId) : $tenantId;
|
||||
if (! $effectiveTenant) {
|
||||
$effectiveTenant = $actor->isMasterAdmin()
|
||||
? (array_key_exists('tenant_id', $data) ? $data['tenant_id'] : ($channel ? $channel->tenant_id : $tenantId))
|
||||
: $tenantId;
|
||||
$isGlobalWhapi = $actor->isMasterAdmin()
|
||||
&& ($data['channel'] ?? $channel?->channel) === 'whatsapp_unofficial'
|
||||
&& ! $effectiveTenant;
|
||||
if (! $effectiveTenant && ! $isGlobalWhapi) {
|
||||
throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']);
|
||||
}
|
||||
if ($channel && (int) $channel->tenant_id !== (int) $effectiveTenant) {
|
||||
if ($channel && $channel->tenant_id !== null && (int) $channel->tenant_id !== (int) $effectiveTenant) {
|
||||
throw ValidationException::withMessages(['tenant_id' => 'Konfigurasi bukan milik tenant tersebut.']);
|
||||
}
|
||||
if ($channel && $channel->tenant_id === null && ! $isGlobalWhapi) {
|
||||
throw ValidationException::withMessages(['tenant_id' => 'Konfigurasi global hanya dapat digunakan untuk WHAPI Master Admin.']);
|
||||
}
|
||||
$credentials = array_filter($data['credentials'] ?? [], fn ($value) => $value !== '' && $value !== null);
|
||||
if ($channel && $credentials) {
|
||||
$credentials = [...($channel->credentials ?? []), ...$credentials];
|
||||
}
|
||||
if (($data['is_default'] ?? false) === true) {
|
||||
NotificationChannel::where('tenant_id', $effectiveTenant)->where('channel', $data['channel'])
|
||||
NotificationChannel::where('channel', $data['channel'])
|
||||
->when($effectiveTenant, fn ($q) => $q->where('tenant_id', $effectiveTenant), fn ($q) => $q->whereNull('tenant_id'))
|
||||
->when($channel, fn ($q) => $q->whereKeyNot($channel->id))
|
||||
->update(['is_default' => false]);
|
||||
}
|
||||
if ($data['channel'] === 'whatsapp_unofficial') {
|
||||
$data['provider'] = 'whapi_id';
|
||||
$apiKey = $credentials['api_key'] ?? data_get($channel?->credentials, 'api_key');
|
||||
$baseUrl = data_get($data, 'settings.base_url', data_get($channel?->settings, 'base_url'));
|
||||
if (! $apiKey || ! $baseUrl) {
|
||||
throw ValidationException::withMessages([
|
||||
'credentials' => 'API Key perangkat dan Base URL WHAPI wajib diisi.',
|
||||
]);
|
||||
}
|
||||
if ($isGlobalWhapi && NotificationChannel::whereNull('tenant_id')
|
||||
->where('channel', 'whatsapp_unofficial')
|
||||
->when($channel, fn ($q) => $q->whereKeyNot($channel->id))
|
||||
->exists()) {
|
||||
throw ValidationException::withMessages([
|
||||
'channel' => 'Konfigurasi WHAPI global sudah tersedia. Silakan perbarui konfigurasi tersebut.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
$payload = [
|
||||
...$data,
|
||||
'tenant_id' => $effectiveTenant,
|
||||
@@ -72,6 +171,16 @@ class NotificationService
|
||||
->setAttribute('credentials_configured', ! empty($channel->getRawOriginal('credentials')));
|
||||
}
|
||||
|
||||
public function deleteChannel(NotificationChannel $channel): void
|
||||
{
|
||||
if ($channel->templates()->exists() || $channel->deliveries()->exists()) {
|
||||
$channel->update(['enabled' => false, 'is_default' => false]);
|
||||
|
||||
return;
|
||||
}
|
||||
$channel->delete();
|
||||
}
|
||||
|
||||
public function templates(string $channel, User $actor, ?int $tenantId)
|
||||
{
|
||||
return NotificationTemplate::with(['event:id,code,name,category,variables', 'channelConfig:id,name'])
|
||||
@@ -170,7 +279,9 @@ class NotificationService
|
||||
'system' => 'Penyimpanan notifikasi aplikasi siap.',
|
||||
'telegram' => data_get(Http::timeout(15)->get('https://api.telegram.org/bot'.$credentials['bot_token'].'/getMe')->throw()->json(), 'result.username', 'Bot Telegram terhubung.'),
|
||||
'whatsapp_official' => data_get(Http::withToken($credentials['access_token'])->timeout(15)->get(rtrim($settings['base_url'] ?? 'https://graph.facebook.com/v22.0', '/').'/'.$credentials['phone_number_id'])->throw()->json(), 'display_phone_number', 'WhatsApp Official terhubung.'),
|
||||
'whatsapp_unofficial' => data_get(Http::withHeaders(isset($credentials['api_key']) ? [($settings['api_key_header'] ?? 'X-API-Key') => $credentials['api_key']] : [])->timeout(15)->get($settings['health_url'])->throw()->json(), 'status', 'Gateway terhubung.'),
|
||||
'whatsapp_unofficial' => $channel->provider === 'whapi_id'
|
||||
? data_get(app(WhapiIdService::class)->state($channel), 'results.state', 'WHAPI terhubung.')
|
||||
: data_get(Http::withHeaders(isset($credentials['api_key']) ? [($settings['api_key_header'] ?? 'X-API-Key') => $credentials['api_key']] : [])->timeout(15)->get($settings['health_url'])->throw()->json(), 'status', 'Gateway terhubung.'),
|
||||
'email' => ! empty($settings['host']) && ! empty($settings['from_address']) ? 'Konfigurasi SMTP lengkap.' : throw new \RuntimeException('Host dan alamat pengirim SMTP wajib diisi.'),
|
||||
};
|
||||
$channel->update(['last_health_status' => 'healthy', 'last_health_message' => (string) $message, 'last_health_check_at' => now()]);
|
||||
|
||||
Reference in New Issue
Block a user