Skip to content

Commit e896804

Browse files
authored
Add NotificationSetting entity and service for managing user notification preferences (#15)
1 parent a5b1f6a commit e896804

File tree

8 files changed

+162
-1
lines changed

8 files changed

+162
-1
lines changed

src/main/kotlin/com/moa/common/exception/ErrorCode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ enum class ErrorCode(
1717
INVALID_WORK_POLICY_INPUT("INVALID_WORK_POLICY_INPUT", "근무정책 입력값이 유효하지 않습니다"),
1818
WORKDAY_NOT_FOUND("WORKDAY_NOT_FOUND", "해당 날짜의 근무 정보를 찾을 수 없습니다"),
1919
REQUIRED_TERMS_MUST_BE_AGREED("REQUIRED_TERMS_MUST_BE_AGREED", "필수 약관은 동의해야 합니다"),
20+
REQUIRED_MARKETING_TERM("REQUIRED_MARKETING_TERM", "마케팅 약관 동의가 필요합니다."),
2021

2122
INVALID_ID_TOKEN("INVALID_ID_TOKEN", "유효하지 않은 ID 토큰입니다"),
2223
EXPIRED_TOKEN("EXPIRED_TOKEN", "토큰이 만료되었습니다"),
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.moa.controller
2+
3+
import com.moa.common.auth.Auth
4+
import com.moa.common.auth.AuthMemberInfo
5+
import com.moa.common.response.ApiResponse
6+
import com.moa.service.NotificationSettingService
7+
import com.moa.service.dto.NotificationSettingUpdateRequest
8+
import org.springframework.web.bind.annotation.*
9+
10+
@RestController
11+
@RequestMapping("/api/v1/settings/notification")
12+
class NotificationSettingController(
13+
private val notificationSettingService: NotificationSettingService,
14+
) {
15+
16+
@GetMapping
17+
fun getSettings(
18+
@Auth member: AuthMemberInfo,
19+
) = ApiResponse.success(notificationSettingService.getSettings(member.id))
20+
21+
@PatchMapping("/work")
22+
fun updateWorkNotification(
23+
@Auth member: AuthMemberInfo,
24+
@RequestBody req: NotificationSettingUpdateRequest,
25+
) = ApiResponse.success(notificationSettingService.updateWorkNotification(member.id, req.enabled))
26+
27+
@PatchMapping("/payday")
28+
fun updatePaydayNotification(
29+
@Auth member: AuthMemberInfo,
30+
@RequestBody req: NotificationSettingUpdateRequest,
31+
) = ApiResponse.success(notificationSettingService.updatePaydayNotification(member.id, req.enabled))
32+
33+
@PatchMapping("/promotion")
34+
fun updatePromotionNotification(
35+
@Auth member: AuthMemberInfo,
36+
@RequestBody req: NotificationSettingUpdateRequest,
37+
) = ApiResponse.success(notificationSettingService.updatePromotionNotification(member.id, req.enabled))
38+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.moa.entity
2+
3+
import jakarta.persistence.*
4+
5+
@Entity
6+
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["memberId"])])
7+
class NotificationSetting(
8+
@Column(nullable = false)
9+
val memberId: Long,
10+
11+
@Column(nullable = false)
12+
var workNotificationEnabled: Boolean = false,
13+
14+
@Column(nullable = false)
15+
var paydayNotificationEnabled: Boolean = false,
16+
17+
@Column(nullable = false)
18+
var promotionNotificationEnabled: Boolean = false,
19+
) : BaseEntity() {
20+
21+
@Id
22+
@GeneratedValue(strategy = GenerationType.IDENTITY)
23+
val id: Long = 0
24+
}

src/main/kotlin/com/moa/entity/Term.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ class Term(
1717

1818
@Column(nullable = false)
1919
val contentUrl: String,
20-
)
20+
) {
21+
companion object {
22+
const val MARKETING = "MARKETING"
23+
}
24+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.moa.repository
2+
3+
import com.moa.entity.NotificationSetting
4+
import org.springframework.data.jpa.repository.JpaRepository
5+
6+
interface NotificationSettingRepository : JpaRepository<NotificationSetting, Long> {
7+
fun findByMemberId(memberId: Long): NotificationSetting?
8+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.moa.service
2+
3+
import com.moa.common.exception.BadRequestException
4+
import com.moa.common.exception.ErrorCode
5+
import com.moa.entity.NotificationSetting
6+
import com.moa.entity.Term
7+
import com.moa.repository.NotificationSettingRepository
8+
import com.moa.repository.TermAgreementRepository
9+
import com.moa.service.dto.NotificationSettingResponse
10+
import org.springframework.stereotype.Service
11+
import org.springframework.transaction.annotation.Transactional
12+
13+
@Service
14+
class NotificationSettingService(
15+
private val notificationSettingRepository: NotificationSettingRepository,
16+
private val termAgreementRepository: TermAgreementRepository,
17+
) {
18+
19+
@Transactional(readOnly = true)
20+
fun getSettings(memberId: Long): NotificationSettingResponse {
21+
val setting = notificationSettingRepository.findByMemberId(memberId)
22+
return NotificationSettingResponse.from(setting)
23+
}
24+
25+
@Transactional
26+
fun updateWorkNotification(memberId: Long, enabled: Boolean): NotificationSettingResponse {
27+
val setting = getOrCreate(memberId)
28+
setting.workNotificationEnabled = enabled
29+
return NotificationSettingResponse.from(setting)
30+
}
31+
32+
@Transactional
33+
fun updatePaydayNotification(memberId: Long, enabled: Boolean): NotificationSettingResponse {
34+
val setting = getOrCreate(memberId)
35+
setting.paydayNotificationEnabled = enabled
36+
return NotificationSettingResponse.from(setting)
37+
}
38+
39+
@Transactional
40+
fun updatePromotionNotification(memberId: Long, enabled: Boolean): NotificationSettingResponse {
41+
val setting = getOrCreate(memberId)
42+
43+
if (enabled) {
44+
validateMarketingAgreed(memberId)
45+
}
46+
47+
setting.promotionNotificationEnabled = enabled
48+
49+
return NotificationSettingResponse.from(setting)
50+
}
51+
52+
private fun getOrCreate(memberId: Long): NotificationSetting {
53+
return notificationSettingRepository.findByMemberId(memberId)
54+
?: notificationSettingRepository.save(NotificationSetting(memberId = memberId))
55+
}
56+
57+
private fun validateMarketingAgreed(memberId: Long) {
58+
val agreement = termAgreementRepository.findByMemberIdAndTermCode(memberId, Term.MARKETING)
59+
60+
if (agreement == null || !agreement.agreed) {
61+
throw BadRequestException(ErrorCode.REQUIRED_MARKETING_TERM)
62+
}
63+
}
64+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.moa.service.dto
2+
3+
import com.moa.entity.NotificationSetting
4+
5+
data class NotificationSettingResponse(
6+
val workNotificationEnabled: Boolean,
7+
val paydayNotificationEnabled: Boolean,
8+
val promotionNotificationEnabled: Boolean,
9+
) {
10+
companion object {
11+
fun from(setting: NotificationSetting?) = NotificationSettingResponse(
12+
workNotificationEnabled = setting?.workNotificationEnabled ?: false,
13+
paydayNotificationEnabled = setting?.paydayNotificationEnabled ?: false,
14+
promotionNotificationEnabled = setting?.promotionNotificationEnabled ?: false,
15+
)
16+
}
17+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.moa.service.dto
2+
3+
data class NotificationSettingUpdateRequest(
4+
val enabled: Boolean,
5+
)

0 commit comments

Comments
 (0)