1717import java .util .List ;
1818import java .util .Map ;
1919import java .util .Optional ;
20+ import java .util .UUID ;
2021import java .util .stream .Collectors ;
2122
2223@ Service
@@ -36,10 +37,12 @@ public class FcmMessagingService {
3637 */
3738 public String sendMessage (String fcmToken , FcmMessageRequest messageRequest ) {
3839 String userId = getUserIdFromToken (fcmToken );
39- String campaignId = messageRequest .getCampaignId ();
40+ String campaignGroup = messageRequest .getCampaignId (); // 원래의 campaignId를 그룹으로 사용
41+ String pushId = UUID .randomUUID ().toString ();
4042
4143 try {
4244 Map <String , String > data = buildDataWithUserId (messageRequest , userId );
45+ data .put ("pushId" , pushId );
4346
4447 Message .Builder messageBuilder = Message .builder ()
4548 .setToken (fcmToken )
@@ -50,29 +53,27 @@ public String sendMessage(String fcmToken, FcmMessageRequest messageRequest) {
5053 .putAllData (data );
5154
5255 // Google Analytics 추적을 위한 FcmOptions 설정
53- if (campaignId != null ) {
54- String analyticsLabel = ANALYTICS_LABEL_PREFIX + campaignId ;
56+ if (campaignGroup != null ) {
57+ String analyticsLabel = ANALYTICS_LABEL_PREFIX + campaignGroup ;
5558 messageBuilder .setFcmOptions (FcmOptions .withAnalyticsLabel (analyticsLabel ));
5659 log .debug ("Analytics label set: {}" , analyticsLabel );
5760 }
5861
5962 Message message = messageBuilder .build ();
60- String response = firebaseMessaging .send (message );
61- log .debug ("FCM message sent successfully: {}" , response );
63+ String fcmMessageId = firebaseMessaging .send (message );
64+ log .debug ("FCM message sent successfully - pushId : {}, fcmMessageId: {} " , pushId , fcmMessageId );
6265
63- // 송신 성공 로그 저장
64- if (campaignId != null && userId != null ) {
65- pushLogService .logSent (campaignId , userId , true );
66+ if (userId != null ) {
67+ pushLogService .logSent (pushId , userId , true , campaignGroup , fcmMessageId );
6668 }
6769
68- return response ;
70+ return pushId ; // 자체 UUID 반환
6971
7072 } catch (FirebaseMessagingException e ) {
71- log .error ("Failed to send FCM message: {}" , e . getMessage () );
73+ log .error ("Failed to send FCM message - pushId : {}" , pushId , e );
7274
73- // 송신 실패 로그 저장
74- if (campaignId != null && userId != null ) {
75- pushLogService .logSent (campaignId , userId , false );
75+ if (userId != null ) {
76+ pushLogService .logSent (pushId , userId , false , campaignGroup , null );
7677 }
7778
7879 throw new FcmException (FcmErrorCode .MESSAGE_SEND_FAILED );
@@ -83,7 +84,7 @@ public String sendMessage(String fcmToken, FcmMessageRequest messageRequest) {
8384 * 여러 사용자에게 동시 알림 전송 (각 토큰마다 userId 포함)
8485 */
8586 public BatchResponse sendMulticastMessage (List <String > fcmTokens , FcmMessageRequest messageRequest ) {
86- String campaignId = messageRequest .getCampaignId ();
87+ String campaignGroup = messageRequest .getCampaignId (); // 원래의 campaignId를 그룹으로 사용
8788
8889 try {
8990 if (fcmTokens == null || fcmTokens .isEmpty ()) {
@@ -93,18 +94,24 @@ public BatchResponse sendMulticastMessage(List<String> fcmTokens, FcmMessageRequ
9394 // 각 토큰마다 userId를 포함한 개별 메시지 생성
9495 Map <String , String > tokenToUserId = getTokenToUserIdMap (fcmTokens );
9596 List <Message > messages = new ArrayList <>();
97+ Map <Integer , String > indexToPushId = new java .util .HashMap <>(); // 인덱스별 pushId 매핑
9698
9799 // Google Analytics 추적을 위한 FcmOptions 설정
98100 String analyticsLabel = null ;
99101 FcmOptions fcmOptions = null ;
100- if (campaignId != null ) {
101- analyticsLabel = ANALYTICS_LABEL_PREFIX + campaignId ;
102+ if (campaignGroup != null ) {
103+ analyticsLabel = ANALYTICS_LABEL_PREFIX + campaignGroup ;
102104 fcmOptions = FcmOptions .withAnalyticsLabel (analyticsLabel );
103105 }
104106
107+ int index = 0 ;
105108 for (String fcmToken : fcmTokens ) {
106109 String userId = tokenToUserId .get (fcmToken );
110+ String pushId = UUID .randomUUID ().toString ();
111+ indexToPushId .put (index , pushId );
112+
107113 Map <String , String > data = buildDataWithUserId (messageRequest , userId );
114+ data .put ("pushId" , pushId );
108115
109116 Message .Builder messageBuilder = Message .builder ()
110117 .setToken (fcmToken )
@@ -119,27 +126,23 @@ public BatchResponse sendMulticastMessage(List<String> fcmTokens, FcmMessageRequ
119126 }
120127
121128 messages .add (messageBuilder .build ());
129+ index ++;
122130 }
123131
124- BatchResponse response = firebaseMessaging .sendAll (messages );
132+ BatchResponse response = firebaseMessaging .sendEach (messages );
125133 log .info ("Batch messages sent to {} tokens - Success: {}, Failed: {} (Analytics: {})" ,
126134 fcmTokens .size (), response .getSuccessCount (), response .getFailureCount (),
127135 analyticsLabel != null ? analyticsLabel : "N/A" );
128136
129- // 배치로 로그 저장
130- if (campaignId != null ) {
131- savePushLogsBatch (fcmTokens , campaignId , response );
132- }
137+ // 배치로 로그 저장 (자체 UUID와 FCM messageId 함께 저장)
138+ savePushLogsBatch (fcmTokens , campaignGroup , response , indexToPushId );
133139
134140 return response ;
135141
136142 } catch (FirebaseMessagingException e ) {
137143 log .error ("Failed to send multicast FCM message: {}" , e .getMessage ());
138144
139- // 전체 실패 로그 배치 저장
140- if (campaignId != null ) {
141- savePushLogsAllFailed (fcmTokens , campaignId );
142- }
145+ savePushLogsAllFailed (fcmTokens , campaignGroup );
143146
144147 throw new FcmException (FcmErrorCode .MESSAGE_SEND_FAILED );
145148 }
@@ -148,7 +151,7 @@ public BatchResponse sendMulticastMessage(List<String> fcmTokens, FcmMessageRequ
148151 /**
149152 * 푸시 로그를 배치로 저장 (성공/실패 혼합)
150153 */
151- private void savePushLogsBatch (List <String > fcmTokens , String campaignId , BatchResponse response ) {
154+ private void savePushLogsBatch (List <String > fcmTokens , String campaignGroup , BatchResponse response , Map < Integer , String > indexToPushId ) {
152155 try {
153156 Map <String , String > tokenToUserId = getTokenToUserIdMap (fcmTokens );
154157 List <PushLog > logsToSave = new ArrayList <>();
@@ -157,23 +160,26 @@ private void savePushLogsBatch(List<String> fcmTokens, String campaignId, BatchR
157160 for (int i = 0 ; i < response .getResponses ().size (); i ++) {
158161 String fcmToken = fcmTokens .get (i );
159162 String userId = tokenToUserId .get (fcmToken );
160- boolean success = response .getResponses ().get (i ).isSuccessful ();
163+ SendResponse sendResponse = response .getResponses ().get (i );
164+ boolean success = sendResponse .isSuccessful ();
165+ String pushId = indexToPushId .get (i ); // 미리 생성된 UUID
161166
162- if (userId != null ) {
163- logsToSave .add (createPushLog (campaignId , userId , success , now ));
167+ if (userId != null && pushId != null ) {
168+ String fcmMessageId = success ? sendResponse .getMessageId () : null ;
169+ logsToSave .add (createPushLog (pushId , userId , success , campaignGroup , fcmMessageId , now ));
164170 }
165171 }
166172
167- savePushLogsIfNotEmpty (logsToSave , campaignId );
173+ savePushLogsIfNotEmpty (logsToSave , campaignGroup );
168174 } catch (Exception e ) {
169- log .error ("Failed to batch save push logs for campaign : {}" , campaignId , e );
175+ log .error ("Failed to batch save push logs for campaignGroup : {}" , campaignGroup , e );
170176 }
171177 }
172178
173179 /**
174180 * 전체 실패 시 푸시 로그를 배치로 저장
175181 */
176- private void savePushLogsAllFailed (List <String > fcmTokens , String campaignId ) {
182+ private void savePushLogsAllFailed (List <String > fcmTokens , String campaignGroup ) {
177183 try {
178184 Map <String , String > tokenToUserId = getTokenToUserIdMap (fcmTokens );
179185 List <PushLog > logsToSave = new ArrayList <>();
@@ -182,13 +188,14 @@ private void savePushLogsAllFailed(List<String> fcmTokens, String campaignId) {
182188 for (String fcmToken : fcmTokens ) {
183189 String userId = tokenToUserId .get (fcmToken );
184190 if (userId != null ) {
185- logsToSave .add (createPushLog (campaignId , userId , false , now ));
191+ String pushId = UUID .randomUUID ().toString ();
192+ logsToSave .add (createPushLog (pushId , userId , false , campaignGroup , null , now ));
186193 }
187194 }
188195
189- savePushLogsIfNotEmpty (logsToSave , campaignId );
196+ savePushLogsIfNotEmpty (logsToSave , campaignGroup );
190197 } catch (Exception e ) {
191- log .error ("Failed to batch save failed push logs for campaign : {}" , campaignId , e );
198+ log .error ("Failed to batch save failed push logs for campaignGroup : {}" , campaignGroup , e );
192199 }
193200 }
194201
@@ -208,9 +215,11 @@ private Map<String, String> getTokenToUserIdMap(List<String> fcmTokens) {
208215 /**
209216 * PushLog 객체 생성
210217 */
211- private PushLog createPushLog (String campaignId , String userId , boolean success , LocalDateTime now ) {
218+ private PushLog createPushLog (String pushId , String userId , boolean success , String campaignGroup , String fcmMessageId , LocalDateTime now ) {
212219 return PushLog .builder ()
213- .campaignId (campaignId )
220+ .campaignId (pushId ) // 자체 UUID를 campaignId로 사용
221+ .fcmMessageId (fcmMessageId ) // FCM messageId (선택적)
222+ .campaignGroup (campaignGroup ) // 캠페인 그룹
214223 .userId (userId )
215224 .sentAt (now )
216225 .sentSuccess (success )
@@ -221,10 +230,10 @@ private PushLog createPushLog(String campaignId, String userId, boolean success,
221230 /**
222231 * PushLog 목록이 비어있지 않으면 배치 저장
223232 */
224- private void savePushLogsIfNotEmpty (List <PushLog > logsToSave , String campaignId ) {
233+ private void savePushLogsIfNotEmpty (List <PushLog > logsToSave , String campaignGroup ) {
225234 if (!logsToSave .isEmpty ()) {
226235 pushLogRepository .saveAll (logsToSave );
227- log .debug ("Batch saved {} push logs for campaign : {}" , logsToSave .size (), campaignId );
236+ log .debug ("Batch saved {} push logs for campaignGroup : {}" , logsToSave .size (), campaignGroup );
228237 }
229238 }
230239
0 commit comments