Compare commits

..

3 Commits

Author SHA1 Message Date
Klaus a17a6a41da 관리자
- 매체 파트너 코드(pid) 조회 API
2025-03-05 00:39:49 +09:00
Klaus 5db181aa74 관리자
- 매체 파트너 코드(pid) 수정 API
2025-03-04 23:53:22 +09:00
Klaus d74f1ddb81 관리자
- 매체 파트너 코드(pid) 등록 API
2025-03-04 23:36:31 +09:00
7 changed files with 234 additions and 9 deletions

View File

@ -0,0 +1,34 @@
package kr.co.vividnext.sodalive.admin.marketing
import kr.co.vividnext.sodalive.common.ApiResponse
import org.springframework.data.domain.Pageable
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@PreAuthorize("hasRole('ADMIN')")
@RequestMapping("/admin/marketing/media-partner")
class AdminAdMediaPartnerController(private val service: AdminAdMediaPartnerService) {
@PostMapping
fun createMediaPartner(
@RequestBody request: CreateAdMediaPartnerRequest
) = ApiResponse.ok(service.createMediaPartner(request))
@PutMapping
fun updateMediaPartner(
@RequestBody request: UpdateAdMediaPartnerRequest
) = ApiResponse.ok(service.updateMediaPartner(request))
@GetMapping
fun getMediaPartnerList(pageable: Pageable) = ApiResponse.ok(
service.getMediaPartnerList(
offset = pageable.offset,
limit = pageable.pageSize.toLong()
)
)
}

View File

@ -0,0 +1,85 @@
package kr.co.vividnext.sodalive.admin.marketing
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.marketing.AdMediaPartnerRepository
import kr.co.vividnext.sodalive.marketing.AdMediaPartnerType
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.net.URLEncoder
@Service
class AdminAdMediaPartnerService(private val repository: AdMediaPartnerRepository) {
@Transactional
fun createMediaPartner(request: CreateAdMediaPartnerRequest) {
val mediaPartner = request.toEntity()
repository.save(mediaPartner)
}
@Transactional
fun updateMediaPartner(request: UpdateAdMediaPartnerRequest) {
val entity = repository.findByIdOrNull(request.id)
?: throw SodaException("잘못된 접근입니다")
if (request.mediaGroup != null) {
entity.mediaGroup = request.mediaGroup
}
if (request.pid != null) {
entity.pid = request.pid
}
if (request.pidName != null) {
entity.pidName = request.pidName
}
if (request.type != null) {
entity.type = request.type
}
if (request.utmSource != null) {
entity.utmSource = request.utmSource
}
if (request.utmMedium != null) {
entity.utmMedium = request.utmMedium
}
if (request.isActive != null) {
entity.isActive = request.isActive
}
}
fun getMediaPartnerList(offset: Long, limit: Long): List<GetAdminAdMediaPartnerResponse> {
return repository.getMediaPartnerList(offset, limit)
.map {
val deepLinkValue = when (it.type) {
AdMediaPartnerType.SERIES -> "series"
AdMediaPartnerType.CONTENT -> "content"
AdMediaPartnerType.LIVE -> "live"
AdMediaPartnerType.CHANNEL -> "channel"
AdMediaPartnerType.MAIN -> "main"
}
val link = URLEncoder.encode(
"$oneLinkHost?af_dp=voiceon%3A%2F%2F" +
"&deep_link_value=$deepLinkValue" +
"&deep_link_sub1=${it.pid}" +
"&deep_link_sub2=${it.utmSource}" +
"&deep_link_sub3=${it.utmMedium}" +
"&deep_link_sub4=${it.pidName}" +
"&utm_source=${it.utmSource}" +
"&utm_medium=${it.utmMedium}" +
"&utm_campaign=${it.pidName}",
"UTF-8"
)
it.link = link
it
}
}
companion object {
private val oneLinkHost = "https://voiceon.onelink.me/RkTm"
}
}

View File

@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.admin.marketing
import kr.co.vividnext.sodalive.marketing.AdMediaPartner
import kr.co.vividnext.sodalive.marketing.AdMediaPartnerType
data class CreateAdMediaPartnerRequest(
val mediaGroup: String,
val pid: String,
val pidName: String,
val type: AdMediaPartnerType,
val utmSource: String,
val utmMedium: String
) {
fun toEntity(): AdMediaPartner {
return AdMediaPartner(
mediaGroup = mediaGroup,
pid = pid,
pidName = pidName,
type = type,
utmSource = utmSource,
utmMedium = utmMedium
)
}
}

View File

@ -0,0 +1,17 @@
package kr.co.vividnext.sodalive.admin.marketing
import com.querydsl.core.annotations.QueryProjection
import kr.co.vividnext.sodalive.marketing.AdMediaPartnerType
data class GetAdminAdMediaPartnerResponse @QueryProjection constructor(
val id: Long,
val mediaGroup: String,
val pid: String,
val pidName: String,
val type: AdMediaPartnerType,
val utmSource: String,
val utmMedium: String,
val isActive: Boolean,
val createdAt: String,
var link: String
)

View File

@ -0,0 +1,14 @@
package kr.co.vividnext.sodalive.admin.marketing
import kr.co.vividnext.sodalive.marketing.AdMediaPartnerType
data class UpdateAdMediaPartnerRequest(
val id: Long,
val mediaGroup: String?,
val pid: String?,
val pidName: String?,
val type: AdMediaPartnerType?,
val utmSource: String?,
val utmMedium: String?,
val isActive: Boolean?
)

View File

@ -7,14 +7,14 @@ import javax.persistence.Enumerated
@Entity
data class AdMediaPartner(
val mediaGroup: String,
val pid: String,
val pidName: String,
var mediaGroup: String,
var pid: String,
var pidName: String,
@Enumerated(value = EnumType.STRING)
val type: AdMediaPartnerType,
val utmSource: String,
val utmMedium: String,
val isActive: Boolean = true
var type: AdMediaPartnerType,
var utmSource: String,
var utmMedium: String,
var isActive: Boolean = true
) : BaseEntity()
enum class AdMediaPartnerType {

View File

@ -1,15 +1,66 @@
package kr.co.vividnext.sodalive.marketing
import com.querydsl.core.types.dsl.DateTimePath
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.core.types.dsl.StringTemplate
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.admin.marketing.GetAdminAdMediaPartnerResponse
import kr.co.vividnext.sodalive.admin.marketing.QGetAdminAdMediaPartnerResponse
import kr.co.vividnext.sodalive.marketing.QAdMediaPartner.adMediaPartner
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import java.time.LocalDateTime
interface AdMediaPartnerRepository : JpaRepository<AdMediaPartner, Long>, AdMediaPartnerQueryRepository
interface AdMediaPartnerQueryRepository {
fun findByPid(pid: String): AdMediaPartner?
fun getMediaPartnerList(offset: Long, limit: Long): List<GetAdminAdMediaPartnerResponse>
}
@Repository
class AdMediaPartnerRepository(private val queryFactory: JPAQueryFactory) {
fun findByPid(pid: String): AdMediaPartner? {
class AdMediaPartnerQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : AdMediaPartnerQueryRepository {
override fun findByPid(pid: String): AdMediaPartner? {
return queryFactory
.selectFrom(adMediaPartner)
.where(adMediaPartner.pid.eq(pid), adMediaPartner.isActive.isTrue)
.fetchFirst()
}
override fun getMediaPartnerList(offset: Long, limit: Long): List<GetAdminAdMediaPartnerResponse> {
return queryFactory
.select(
QGetAdminAdMediaPartnerResponse(
adMediaPartner.id,
adMediaPartner.mediaGroup,
adMediaPartner.pid,
adMediaPartner.pidName,
adMediaPartner.type,
adMediaPartner.utmSource,
adMediaPartner.utmMedium,
adMediaPartner.isActive,
getFormattedDate(adMediaPartner.createdAt),
Expressions.constant("")
)
)
.from(adMediaPartner)
.orderBy(adMediaPartner.isActive.desc(), adMediaPartner.id.asc())
.offset(offset)
.limit(limit)
.fetch()
}
private fun getFormattedDate(dateTimePath: DateTimePath<LocalDateTime>): StringTemplate {
return Expressions.stringTemplate(
"DATE_FORMAT({0}, {1})",
Expressions.dateTimeTemplate(
LocalDateTime::class.java,
"CONVERT_TZ({0},{1},{2})",
dateTimePath,
"UTC",
"Asia/Seoul"
),
"%Y-%m-%d %H:%i:%s"
)
}
}