From 540255ec27d5d5c3b02dce222d1d55b97046d258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Wed, 18 Mar 2026 11:22:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[notification]=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20soundType=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getGroupedSettings()에서 soundType 반환 (settings.sound_type) - updateGroupedSettings()에서 soundType 저장 (settings JSON) - UpdateGroupedSettingRequest에 soundType 검증 추가 (default/sam_voice/mute) --- .../UpdateGroupedSettingRequest.php | 18 ++++++++++++++++++ app/Services/NotificationSettingService.php | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/app/Http/Requests/NotificationSetting/UpdateGroupedSettingRequest.php b/app/Http/Requests/NotificationSetting/UpdateGroupedSettingRequest.php index f550f23c..adcfeb80 100644 --- a/app/Http/Requests/NotificationSetting/UpdateGroupedSettingRequest.php +++ b/app/Http/Requests/NotificationSetting/UpdateGroupedSettingRequest.php @@ -25,75 +25,93 @@ public function rules(): array 'notice.notice' => ['sometimes', 'array'], 'notice.notice.enabled' => ['sometimes', 'boolean'], 'notice.notice.email' => ['sometimes', 'boolean'], + 'notice.notice.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'notice.event' => ['sometimes', 'array'], 'notice.event.enabled' => ['sometimes', 'boolean'], 'notice.event.email' => ['sometimes', 'boolean'], + 'notice.event.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'schedule' => ['sometimes', 'array'], 'schedule.enabled' => ['sometimes', 'boolean'], 'schedule.vatReport' => ['sometimes', 'array'], 'schedule.vatReport.enabled' => ['sometimes', 'boolean'], 'schedule.vatReport.email' => ['sometimes', 'boolean'], + 'schedule.vatReport.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'schedule.incomeTaxReport' => ['sometimes', 'array'], 'schedule.incomeTaxReport.enabled' => ['sometimes', 'boolean'], 'schedule.incomeTaxReport.email' => ['sometimes', 'boolean'], + 'schedule.incomeTaxReport.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'vendor' => ['sometimes', 'array'], 'vendor.enabled' => ['sometimes', 'boolean'], 'vendor.newVendor' => ['sometimes', 'array'], 'vendor.newVendor.enabled' => ['sometimes', 'boolean'], 'vendor.newVendor.email' => ['sometimes', 'boolean'], + 'vendor.newVendor.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'vendor.creditRating' => ['sometimes', 'array'], 'vendor.creditRating.enabled' => ['sometimes', 'boolean'], 'vendor.creditRating.email' => ['sometimes', 'boolean'], + 'vendor.creditRating.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'attendance' => ['sometimes', 'array'], 'attendance.enabled' => ['sometimes', 'boolean'], 'attendance.annualLeave' => ['sometimes', 'array'], 'attendance.annualLeave.enabled' => ['sometimes', 'boolean'], 'attendance.annualLeave.email' => ['sometimes', 'boolean'], + 'attendance.annualLeave.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'attendance.clockIn' => ['sometimes', 'array'], 'attendance.clockIn.enabled' => ['sometimes', 'boolean'], 'attendance.clockIn.email' => ['sometimes', 'boolean'], + 'attendance.clockIn.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'attendance.late' => ['sometimes', 'array'], 'attendance.late.enabled' => ['sometimes', 'boolean'], 'attendance.late.email' => ['sometimes', 'boolean'], + 'attendance.late.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'attendance.absent' => ['sometimes', 'array'], 'attendance.absent.enabled' => ['sometimes', 'boolean'], 'attendance.absent.email' => ['sometimes', 'boolean'], + 'attendance.absent.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'order' => ['sometimes', 'array'], 'order.enabled' => ['sometimes', 'boolean'], 'order.salesOrder' => ['sometimes', 'array'], 'order.salesOrder.enabled' => ['sometimes', 'boolean'], 'order.salesOrder.email' => ['sometimes', 'boolean'], + 'order.salesOrder.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'order.purchaseOrder' => ['sometimes', 'array'], 'order.purchaseOrder.enabled' => ['sometimes', 'boolean'], 'order.purchaseOrder.email' => ['sometimes', 'boolean'], + 'order.purchaseOrder.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'approval' => ['sometimes', 'array'], 'approval.enabled' => ['sometimes', 'boolean'], 'approval.approvalRequest' => ['sometimes', 'array'], 'approval.approvalRequest.enabled' => ['sometimes', 'boolean'], 'approval.approvalRequest.email' => ['sometimes', 'boolean'], + 'approval.approvalRequest.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'approval.draftApproved' => ['sometimes', 'array'], 'approval.draftApproved.enabled' => ['sometimes', 'boolean'], 'approval.draftApproved.email' => ['sometimes', 'boolean'], + 'approval.draftApproved.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'approval.draftRejected' => ['sometimes', 'array'], 'approval.draftRejected.enabled' => ['sometimes', 'boolean'], 'approval.draftRejected.email' => ['sometimes', 'boolean'], + 'approval.draftRejected.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'approval.draftCompleted' => ['sometimes', 'array'], 'approval.draftCompleted.enabled' => ['sometimes', 'boolean'], 'approval.draftCompleted.email' => ['sometimes', 'boolean'], + 'approval.draftCompleted.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'production' => ['sometimes', 'array'], 'production.enabled' => ['sometimes', 'boolean'], 'production.safetyStock' => ['sometimes', 'array'], 'production.safetyStock.enabled' => ['sometimes', 'boolean'], 'production.safetyStock.email' => ['sometimes', 'boolean'], + 'production.safetyStock.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], 'production.productionComplete' => ['sometimes', 'array'], 'production.productionComplete.enabled' => ['sometimes', 'boolean'], 'production.productionComplete.email' => ['sometimes', 'boolean'], + 'production.productionComplete.soundType' => ['sometimes', 'string', 'in:default,sam_voice,mute'], ]; } } diff --git a/app/Services/NotificationSettingService.php b/app/Services/NotificationSettingService.php index 21061fda..4ba1e401 100644 --- a/app/Services/NotificationSettingService.php +++ b/app/Services/NotificationSettingService.php @@ -265,15 +265,18 @@ public function getGroupedSettings(): array if ($settings->has($type)) { $setting = $settings->get($type); + $soundType = $setting->settings['sound_type'] ?? 'default'; $groupData[$camelKey] = [ 'enabled' => $setting->push_enabled, 'email' => $setting->email_enabled, + 'soundType' => $soundType, ]; } else { // 기본값 $groupData[$camelKey] = [ 'enabled' => false, 'email' => false, + 'soundType' => 'default', ]; } } @@ -330,6 +333,17 @@ public function updateGroupedSettings(array $data): array if (isset($groupData[$camelKey])) { $itemData = $groupData[$camelKey]; + + // 기존 settings JSON을 유지하면서 sound_type만 업데이트 + $existing = NotificationSetting::where('tenant_id', $tenantId) + ->where('user_id', $userId) + ->where('notification_type', $type) + ->first(); + $existingSettings = $existing?->settings ?? []; + if (isset($itemData['soundType'])) { + $existingSettings['sound_type'] = $itemData['soundType']; + } + NotificationSetting::updateOrCreate( [ 'tenant_id' => $tenantId, @@ -342,6 +356,7 @@ public function updateGroupedSettings(array $data): array 'sms_enabled' => false, 'in_app_enabled' => $itemData['enabled'] ?? false, 'kakao_enabled' => false, + 'settings' => $existingSettings ?: null, ] ); }