Skip to content

Commit 553d331

Browse files
authored
Merge pull request #378 from PromptPlace/develop
[FEAT]: 알림 목록 조회 TYPE에 관리자 메세지 알림 추가
2 parents 654f490 + df2285a commit 553d331

File tree

7 files changed

+72
-23
lines changed

7 files changed

+72
-23
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE `Notification` MODIFY `type` ENUM('FOLLOW', 'NEW_PROMPT', 'INQUIRY', 'ANNOUNCEMENT', 'REPORT', 'ADMIN_MESSAGE') NOT NULL;

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ enum NotificationType {
501501
INQUIRY
502502
ANNOUNCEMENT
503503
REPORT
504+
ADMIN_MESSAGE
504505
}
505506

506507
enum Payment_provider {

src/messages/services/message.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Service } from "typedi";
22
import { CreateMessageDto } from "../dtos/message.dto";
33
import { MessageRepository } from "../repositories/message.repository";
44
import { AppError } from "../../errors/AppError";
5+
import eventBus from "../../config/eventBus";
56

67
@Service()
78
export class MessageService {
@@ -114,6 +115,9 @@ async sendMessage(currentUserId: number, data: CreateMessageDto) {
114115

115116
const message = await this.messageRepository.createMessage(data);
116117

118+
// 새 관리자 메세지 알림 이벤트 발생
119+
eventBus.emit('adminMessage.created', data.sender_id, data.receiver_id, data.body);
120+
117121
return {
118122
message: "메시지 전송 성공",
119123
message_id: message.message_id,

src/notifications/listeners/notification.listener.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
createFollowNotification,
66
createInquiryNotification,
77
createPromptNotification,
8+
createAdminMessageNtification
89
} from "../services/notification.service";
910

1011

@@ -52,4 +53,15 @@ eventBus.on('prompt.created', async (prompterId: number, promptId: number) => {
5253
} catch (err) {
5354
console.error("[알림 리스너 오류]: 새로운 프롬프트 업로드 알림 생성 실패", err);
5455
}
55-
});
56+
});
57+
58+
59+
// 관리자 메세지 알림 리스너
60+
eventBus.on('adminMessage.created', async( adminId: number, userId: number, content: string) => {
61+
try {
62+
await createAdminMessageNtification(adminId, userId, content);
63+
} catch (err) {
64+
console.error("[알림 리스너 오류]: 관리자 메세지 알림 생성 실패", err);
65+
}
66+
67+
})

src/notifications/repositories/notification.repository.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ export const createNotification = async ({
100100
});
101101
};
102102

103-
// ==========알림 목록 조회==========
104103
// ==========알림 목록 조회==========
105104
export const findNotificationsByUserId = async (
106105
userId: number,
@@ -131,9 +130,9 @@ export const findNotificationsByUserId = async (
131130
}),
132131
});
133132

134-
// 2. FOLLOW / NEW_PROMPT 알림에 대해 profileImage 조회
133+
// profileImage 조회
135134
const actorIdsToFetch = notifications
136-
.filter(n => (n.type === 'FOLLOW' || n.type === 'NEW_PROMPT') && n.actor)
135+
.filter(n => (n.type === 'FOLLOW' || n.type === 'NEW_PROMPT' || n.type === 'ADMIN_MESSAGE') && n.actor)
137136
.map(n => n.actor!.user_id);
138137

139138
let actorProfilesMap: Record<number, { url: string } | null> = {};
@@ -160,7 +159,7 @@ export const findNotificationsByUserId = async (
160159
? {
161160
...n.actor,
162161
profileImage:
163-
n.type === 'FOLLOW' || n.type === 'NEW_PROMPT'
162+
n.type === 'FOLLOW' || n.type === 'NEW_PROMPT' || n.type === 'ADMIN_MESSAGE'
164163
? actorProfilesMap[n.actor.user_id] ?? null
165164
: null,
166165
}

src/notifications/routes/notification.route.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,25 @@ router.get('/me', authenticateJwt, getNotificationList); // 알림 목록 조회
7171
* description: |
7272
* - 커서 기반 페이지네이션(cursor-based-pagination) 사용.
7373
* - `cursor`는 이전 요청에서 받은 마지막 데이터의 ID를 의미하며, 이를 기준으로 이후 데이터를 조회.
74+
*
7475
* - 첫 요청 시에는 `cursor`를 생략하여 최신 데이터부터 조회.
76+
*
7577
* - `has_more` 속성으로 더 불러올 데이터가 있는지 미리 확인 가능.
7678
*
7779
* - type 종류:
78-
* - FOLLOW: 누가 나를 팔로우 했을 때
79-
* - NEW_PROMPT: 알림 설정한 프롬프터가 새 프롬프트를 올렸을 때
80-
* - INQUIRY: 나에게 문의사항이 도착했을 때
81-
* - ANNOUNCEMENT: 공지사항이 등록되었을 때
82-
* - REPORT: 내 신고가 접수되었을 때
80+
* - `FOLLOW`: 누가 나를 팔로우 했을 때
81+
*
82+
* - `NEW_PROMPT`: 알림 설정한 프롬프터가 새 프롬프트를 올렸을 때
83+
*
84+
* - `INQUIRY`: 나에게 문의사항이 도착했을 때
85+
*
86+
* - `ANNOUNCEMENT`: 공지사항이 등록되었을 때
8387
*
84-
* - actor 필드는 알림을 유발한 사용자를 뜻하며, 타입이 REPORT, ANNOUNCEMENT일 때에만 null입니다.
85-
* - profile_image 필드는 타입이 FOLLOW, NEW_PROMPT일 경우에만 반환됩니다.
88+
* - `REPORT`: 내 신고가 접수되었을 때
89+
*
90+
* - `ADMIN_MESSAGE`: 관리자 메시지가 도착했을 때
91+
*
92+
* - `actor` 필드는 알림을 유발한 사용자를 뜻하며, 타입이 `REPORT`, `ANNOUNCEMENT`일 때에는 null입니다.
8693
8794
* tags: [Notifications]
8895
* security:
@@ -117,28 +124,31 @@ router.get('/me', authenticateJwt, getNotificationList); // 알림 목록 조회
117124
* has_more: false
118125
* notifications:
119126
* - notification_id: 600
120-
* content: 신고가 접수되었습니다.
127+
* content: "신고가 접수되었습니다."
121128
* type: REPORT
122129
* created_at: "2025-10-26T16:58:29.743Z"
123130
* link_url: null
124131
* actor: null
125132
* - notification_id: 599
126-
* content: 신고가 접수되었습니다.
127-
* type: REPORT
133+
* content: "`홍길동`님이 새 프롬프트를 업로드하셨습니다."
134+
* type: NEW_PROMPT
128135
* created_at: "2025-10-26T16:57:04.162Z"
129-
* link_url: null
130-
* actor: null
136+
* link_url: /profile/10
137+
* actor:
138+
* user_id: 10
139+
* nickname: "홍길동"
140+
* profile_image: "https://promptplace-s3.s3.ap-northeast-2.amazonaws.com/profile-images/1a2b3c4d-5678-90ab-cdef-1234567890ab_1755892991870.png"
131141
* - notification_id: 598
132-
* content: 신고가 접수되었습니다.
133-
* type: REPORT
142+
* content: "새로운 공지사항이 등록되었습니다."
143+
* type: ANNOUNCEMENT
134144
* created_at: "2025-10-26T13:38:26.906Z"
135145
* link_url: null
136146
* actor: null
137147
* - notification_id: 489
138-
* content: "‘또도도잉’님이 회원님을 팔로우합니다."
139-
* type: FOLLOW
148+
* content: "프롬프트에 새로운 문의가 도착했습니다."
149+
* type: INQUIRY
140150
* created_at: "2025-08-21T12:26:45.288Z"
141-
* link_url: "/profile/33"
151+
* link_url: "/inquiries/2"
142152
* actor:
143153
* user_id: 33
144154
* nickname: "또도도잉"
@@ -152,6 +162,15 @@ router.get('/me', authenticateJwt, getNotificationList); // 알림 목록 조회
152162
* user_id: 33
153163
* nickname: "또도도잉"
154164
* profile_image: "https://promptplace-s3.s3.ap-northeast-2.amazonaws.com/profile-images/3b137096-7915-408d-ad94-b70e5aa53107_1755892991870.png"
165+
* - notification_id: 487
166+
* content: "안녕하세요. 프롬프트 플레이스 관리자입니다. 회원님의 원활한 서비스 이용을 위해 공지사항을 확인해주세요."
167+
* type: ADMIN_MESSAGE
168+
* created_at: "2025-08-21T12:26:42.522Z"
169+
* link_url: null
170+
* actor:
171+
* user_id: 45
172+
* nickname: "관리자"
173+
* profile_image: "https://promptplace-s3.s3.ap-northeast-2.amazonaws.com/profile-images/3b137096-7915-408d-ad94-b70e5aa53107_1755892991870.png"
155174
* statusCode: 200
156175
* 401:
157176
* description: 인증 실패

src/notifications/services/notification.service.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,16 @@ export const getNotificationHasNewStatusService = async (
235235
const hasNew = latestNotificationTime > lastNotificationCheckTime;
236236

237237
return { hasNew };
238-
};
238+
};
239+
240+
// 관리자 메세지 알림
241+
export const createAdminMessageNtification = async( adminId: number, userId: number, content: string) => {
242+
243+
return createNotificationService({
244+
userId,
245+
type: NotificationType.ADMIN_MESSAGE,
246+
content: content,
247+
linkUrl: null,
248+
actorId: adminId,
249+
});
250+
}

0 commit comments

Comments
 (0)