회원가입 로직에 광고 트래킹 적용
- 광고 트래킹 관련 Entity 추가 - pid가 현재 광고 중인 pid인 경우 트래킹 로그 생성
This commit is contained in:
parent
801b9934d6
commit
3216c73ee8
|
@ -66,6 +66,7 @@ dependencies {
|
||||||
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20240319-2.0.0")
|
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20240319-2.0.0")
|
||||||
|
|
||||||
implementation("org.apache.poi:poi-ooxml:5.2.3")
|
implementation("org.apache.poi:poi-ooxml:5.2.3")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
runtimeOnly("com.h2database:h2")
|
runtimeOnly("com.h2database:h2")
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.EnumType
|
||||||
|
import javax.persistence.Enumerated
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class AdMediaPartner(
|
||||||
|
val mediaGroup: String,
|
||||||
|
val pid: String,
|
||||||
|
val pidName: String,
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
val type: AdMediaPartnerType,
|
||||||
|
val utmSource: String,
|
||||||
|
val utmMedium: String,
|
||||||
|
val isActive: Boolean = true
|
||||||
|
) : BaseEntity()
|
||||||
|
|
||||||
|
enum class AdMediaPartnerType {
|
||||||
|
SERIES, CONTENT, LIVE, CHANNEL, MAIN
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.marketing.QAdMediaPartner.adMediaPartner
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class AdMediaPartnerRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
|
fun findByPid(pid: String): AdMediaPartner? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(adMediaPartner)
|
||||||
|
.where(adMediaPartner.pid.eq(pid), adMediaPartner.isActive.isTrue)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.Embeddable
|
||||||
|
import javax.persistence.EmbeddedId
|
||||||
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.EnumType
|
||||||
|
import javax.persistence.Enumerated
|
||||||
|
import javax.persistence.PreUpdate
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class AdTrackingHistory(
|
||||||
|
@EmbeddedId
|
||||||
|
val id: AdTrackingHistoryId,
|
||||||
|
val price: Double = 0.toDouble(),
|
||||||
|
val locale: String? = null,
|
||||||
|
var updatedAt: LocalDateTime = LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
@PreUpdate
|
||||||
|
fun preUpdate() {
|
||||||
|
updatedAt = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
data class AdTrackingHistoryId(
|
||||||
|
val pid: String,
|
||||||
|
val memberId: Long,
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
val type: AdTrackingHistoryType,
|
||||||
|
val createdAt: LocalDateTime = LocalDateTime.now()
|
||||||
|
) : Serializable
|
||||||
|
|
||||||
|
enum class AdTrackingHistoryType {
|
||||||
|
// 회원가입
|
||||||
|
SIGNUP,
|
||||||
|
|
||||||
|
// 첫결제
|
||||||
|
FIRST_PAYMENT,
|
||||||
|
|
||||||
|
// 재결제
|
||||||
|
REPEAT_PAYMENT
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface AdTrackingRepository : JpaRepository<AdTrackingHistory, AdTrackingHistoryId>
|
|
@ -0,0 +1,41 @@
|
||||||
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AdTrackingService(
|
||||||
|
private val repository: AdTrackingRepository,
|
||||||
|
private val mediaPartnerRepository: AdMediaPartnerRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
fun saveTrackingHistory(
|
||||||
|
pid: String,
|
||||||
|
type: AdTrackingHistoryType,
|
||||||
|
memberId: Long,
|
||||||
|
price: Double? = null,
|
||||||
|
locale: String? = null
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
val mediaPartner = mediaPartnerRepository.findByPid(pid)
|
||||||
|
|
||||||
|
if (mediaPartner != null) {
|
||||||
|
val id = AdTrackingHistoryId(pid = pid, memberId = memberId, type = type)
|
||||||
|
val trackingHistory = AdTrackingHistory(
|
||||||
|
id = id,
|
||||||
|
price = price ?: 0.toDouble(),
|
||||||
|
locale = locale
|
||||||
|
)
|
||||||
|
repository.save(trackingHistory)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
||||||
import kr.co.vividnext.sodalive.member.notification.MemberNotification
|
import kr.co.vividnext.sodalive.member.notification.MemberNotification
|
||||||
import kr.co.vividnext.sodalive.member.stipulation.StipulationAgree
|
import kr.co.vividnext.sodalive.member.stipulation.StipulationAgree
|
||||||
import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
|
import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
|
||||||
|
import java.time.LocalDateTime
|
||||||
import javax.persistence.CascadeType
|
import javax.persistence.CascadeType
|
||||||
import javax.persistence.Column
|
import javax.persistence.Column
|
||||||
import javax.persistence.Entity
|
import javax.persistence.Entity
|
||||||
|
@ -29,6 +30,12 @@ data class Member(
|
||||||
@Enumerated(value = EnumType.STRING)
|
@Enumerated(value = EnumType.STRING)
|
||||||
var role: MemberRole = MemberRole.USER,
|
var role: MemberRole = MemberRole.USER,
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
var activePid: String? = null,
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
var partnerExpirationDateTime: LocalDateTime? = null,
|
||||||
|
|
||||||
var isVisibleDonationRank: Boolean = true,
|
var isVisibleDonationRank: Boolean = true,
|
||||||
|
|
||||||
var isActive: Boolean = true,
|
var isActive: Boolean = true,
|
||||||
|
|
|
@ -2,9 +2,12 @@ package kr.co.vividnext.sodalive.member
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType
|
||||||
|
import kr.co.vividnext.sodalive.marketing.AdTrackingService
|
||||||
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
||||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowRequest
|
import kr.co.vividnext.sodalive.member.following.CreatorFollowRequest
|
||||||
import kr.co.vividnext.sodalive.member.login.LoginRequest
|
import kr.co.vividnext.sodalive.member.login.LoginRequest
|
||||||
|
import kr.co.vividnext.sodalive.member.login.LoginResponse
|
||||||
import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingRequest
|
import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingRequest
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
@ -23,7 +26,10 @@ import org.springframework.web.multipart.MultipartFile
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/member")
|
@RequestMapping("/member")
|
||||||
class MemberController(private val service: MemberService) {
|
class MemberController(
|
||||||
|
private val service: MemberService,
|
||||||
|
private val trackingService: AdTrackingService
|
||||||
|
) {
|
||||||
@GetMapping("/check/email")
|
@GetMapping("/check/email")
|
||||||
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
||||||
|
|
||||||
|
@ -40,7 +46,19 @@ class MemberController(private val service: MemberService) {
|
||||||
fun signUp(
|
fun signUp(
|
||||||
@RequestPart("profileImage", required = false) profileImage: MultipartFile? = null,
|
@RequestPart("profileImage", required = false) profileImage: MultipartFile? = null,
|
||||||
@RequestPart("request") requestString: String
|
@RequestPart("request") requestString: String
|
||||||
) = service.signUp(profileImage, requestString)
|
): ApiResponse<LoginResponse> {
|
||||||
|
val response = service.signUp(profileImage, requestString)
|
||||||
|
|
||||||
|
if (!response.marketingPid.isNullOrBlank()) {
|
||||||
|
trackingService.saveTrackingHistory(
|
||||||
|
pid = response.marketingPid,
|
||||||
|
type = AdTrackingHistoryType.SIGNUP,
|
||||||
|
memberId = response.memberId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
fun login(@RequestBody loginRequest: LoginRequest) = service.login(loginRequest)
|
fun login(@RequestBody loginRequest: LoginRequest) = service.login(loginRequest)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import kr.co.vividnext.sodalive.member.nickname.NicknameChangeLogRepository
|
||||||
import kr.co.vividnext.sodalive.member.notification.MemberNotificationService
|
import kr.co.vividnext.sodalive.member.notification.MemberNotificationService
|
||||||
import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingRequest
|
import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingRequest
|
||||||
import kr.co.vividnext.sodalive.member.signUp.SignUpRequest
|
import kr.co.vividnext.sodalive.member.signUp.SignUpRequest
|
||||||
|
import kr.co.vividnext.sodalive.member.signUp.SignUpResponse
|
||||||
import kr.co.vividnext.sodalive.member.signUp.SignUpValidator
|
import kr.co.vividnext.sodalive.member.signUp.SignUpValidator
|
||||||
import kr.co.vividnext.sodalive.member.stipulation.Stipulation
|
import kr.co.vividnext.sodalive.member.stipulation.Stipulation
|
||||||
import kr.co.vividnext.sodalive.member.stipulation.StipulationAgree
|
import kr.co.vividnext.sodalive.member.stipulation.StipulationAgree
|
||||||
|
@ -97,7 +98,7 @@ class MemberService(
|
||||||
fun signUp(
|
fun signUp(
|
||||||
profileImage: MultipartFile?,
|
profileImage: MultipartFile?,
|
||||||
requestString: String
|
requestString: String
|
||||||
): ApiResponse<LoginResponse> {
|
): SignUpResponse {
|
||||||
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
||||||
|
|
||||||
|
@ -117,7 +118,11 @@ class MemberService(
|
||||||
member.profileImage = uploadProfileImage(profileImage = profileImage, memberId = member.id!!)
|
member.profileImage = uploadProfileImage(profileImage = profileImage, memberId = member.id!!)
|
||||||
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy)
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = login(request.email, request.password))
|
return SignUpResponse(
|
||||||
|
memberId = member.id!!,
|
||||||
|
marketingPid = request.marketingPid,
|
||||||
|
loginResponse = login(request.email, request.password)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun login(request: LoginRequest): ApiResponse<LoginResponse> {
|
fun login(request: LoginRequest): ApiResponse<LoginResponse> {
|
||||||
|
@ -289,6 +294,11 @@ class MemberService(
|
||||||
container = request.container
|
container = request.container
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!request.marketingPid.isNullOrBlank()) {
|
||||||
|
member.activePid = request.marketingPid
|
||||||
|
member.partnerExpirationDateTime = LocalDateTime.now().plusYears(1)
|
||||||
|
}
|
||||||
|
|
||||||
return repository.save(member)
|
return repository.save(member)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ data class SignUpRequest(
|
||||||
val password: String,
|
val password: String,
|
||||||
val nickname: String,
|
val nickname: String,
|
||||||
val gender: Gender,
|
val gender: Gender,
|
||||||
|
val marketingPid: String? = null,
|
||||||
val isAgreeTermsOfService: Boolean,
|
val isAgreeTermsOfService: Boolean,
|
||||||
val isAgreePrivacyPolicy: Boolean,
|
val isAgreePrivacyPolicy: Boolean,
|
||||||
val container: String = "api"
|
val container: String = "api"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package kr.co.vividnext.sodalive.member.signUp
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.login.LoginResponse
|
||||||
|
|
||||||
|
data class SignUpResponse(
|
||||||
|
val memberId: Long,
|
||||||
|
val marketingPid: String?,
|
||||||
|
val loginResponse: LoginResponse
|
||||||
|
)
|
Loading…
Reference in New Issue