관리자 - 캔, 충전현황 API
This commit is contained in:
parent
841e32a50b
commit
94551b05ff
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.can
|
||||||
|
|
||||||
|
data class AdminCanChargeRequest(
|
||||||
|
val memberId: Long,
|
||||||
|
val method: String,
|
||||||
|
val can: Int
|
||||||
|
)
|
|
@ -0,0 +1,24 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.can
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin/can")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
class AdminCanController(private val service: AdminCanService) {
|
||||||
|
@PostMapping
|
||||||
|
fun insertCan(@RequestBody request: AdminCanRequest) = ApiResponse.ok(service.saveCan(request))
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
fun deleteCan(@PathVariable id: Long) = ApiResponse.ok(service.deleteCan(id))
|
||||||
|
|
||||||
|
@PostMapping("/charge")
|
||||||
|
fun charge(@RequestBody request: AdminCanChargeRequest) = ApiResponse.ok(service.charge(request))
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.can
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.can.Can
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface AdminCanRepository : JpaRepository<Can, Long>
|
|
@ -0,0 +1,26 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.can
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.can.Can
|
||||||
|
import kr.co.vividnext.sodalive.can.CanStatus
|
||||||
|
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||||
|
|
||||||
|
data class AdminCanRequest(
|
||||||
|
val can: Int,
|
||||||
|
val rewardCan: Int,
|
||||||
|
val price: Int
|
||||||
|
) {
|
||||||
|
fun toEntity(): Can {
|
||||||
|
var title = "${can.moneyFormat()} 캔"
|
||||||
|
if (rewardCan > 0) {
|
||||||
|
title = "$title + ${rewardCan.moneyFormat()} 캔"
|
||||||
|
}
|
||||||
|
|
||||||
|
return Can(
|
||||||
|
title = title,
|
||||||
|
can = can,
|
||||||
|
rewardCan = rewardCan,
|
||||||
|
price = price,
|
||||||
|
status = CanStatus.SALE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.can
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.admin.member.AdminMemberRepository
|
||||||
|
import kr.co.vividnext.sodalive.can.CanStatus
|
||||||
|
import kr.co.vividnext.sodalive.can.charge.Charge
|
||||||
|
import kr.co.vividnext.sodalive.can.charge.ChargeRepository
|
||||||
|
import kr.co.vividnext.sodalive.can.charge.ChargeStatus
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.Payment
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AdminCanService(
|
||||||
|
private val repository: AdminCanRepository,
|
||||||
|
private val chargeRepository: ChargeRepository,
|
||||||
|
private val memberRepository: AdminMemberRepository
|
||||||
|
) {
|
||||||
|
@Transactional
|
||||||
|
fun saveCan(request: AdminCanRequest) {
|
||||||
|
repository.save(request.toEntity())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun deleteCan(id: Long) {
|
||||||
|
val can = repository.findByIdOrNull(id)
|
||||||
|
?: throw SodaException("잘못된 요청입니다.")
|
||||||
|
|
||||||
|
can.status = CanStatus.END_OF_SALE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun charge(request: AdminCanChargeRequest) {
|
||||||
|
val member = memberRepository.findByIdOrNull(request.memberId)
|
||||||
|
?: throw SodaException("잘못된 회원번호 입니다.")
|
||||||
|
|
||||||
|
if (request.can <= 0) throw SodaException("0 코인 이상 입력하세요.")
|
||||||
|
if (request.method.isBlank()) throw SodaException("기록내용을 입력하세요.")
|
||||||
|
|
||||||
|
val charge = Charge(0, request.can, status = ChargeStatus.ADMIN)
|
||||||
|
charge.title = "${request.can.moneyFormat()} 캔"
|
||||||
|
charge.member = member
|
||||||
|
|
||||||
|
val payment = Payment(status = PaymentStatus.COMPLETE, paymentGateway = PaymentGateway.PG)
|
||||||
|
payment.method = request.method
|
||||||
|
charge.payment = payment
|
||||||
|
|
||||||
|
chargeRepository.save(charge)
|
||||||
|
|
||||||
|
member.pgRewardCan += charge.rewardCan
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
@RequestMapping("/admin/charge/status")
|
||||||
|
class AdminChargeStatusController(private val service: AdminChargeStatusService) {
|
||||||
|
@GetMapping
|
||||||
|
fun getChargeStatus(
|
||||||
|
@RequestParam startDateStr: String,
|
||||||
|
@RequestParam endDateStr: String
|
||||||
|
) = ApiResponse.ok(service.getChargeStatus(startDateStr, endDateStr))
|
||||||
|
|
||||||
|
@GetMapping("/detail")
|
||||||
|
fun getChargeStatusDetail(
|
||||||
|
@RequestParam startDateStr: String,
|
||||||
|
@RequestParam paymentGateway: PaymentGateway
|
||||||
|
) = ApiResponse.ok(service.getChargeStatusDetail(startDateStr, paymentGateway))
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
import com.querydsl.core.types.dsl.Expressions
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.can.QCan.can1
|
||||||
|
import kr.co.vividnext.sodalive.can.charge.ChargeStatus
|
||||||
|
import kr.co.vividnext.sodalive.can.charge.QCharge.charge
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.QPayment.payment
|
||||||
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
|
fun getChargeStatus(startDate: LocalDateTime, endDate: LocalDateTime): List<GetChargeStatusQueryDto> {
|
||||||
|
val formattedDate = Expressions.stringTemplate(
|
||||||
|
"DATE_FORMAT({0}, {1})",
|
||||||
|
Expressions.dateTimeTemplate(
|
||||||
|
LocalDateTime::class.java,
|
||||||
|
"CONVERT_TZ({0},{1},{2})",
|
||||||
|
charge.createdAt,
|
||||||
|
"UTC",
|
||||||
|
"Asia/Seoul"
|
||||||
|
),
|
||||||
|
"%Y-%m-%d"
|
||||||
|
)
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QGetChargeStatusQueryDto(
|
||||||
|
formattedDate,
|
||||||
|
payment.price.sum(),
|
||||||
|
can1.price.sum(),
|
||||||
|
payment.id.count(),
|
||||||
|
payment.paymentGateway
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(payment)
|
||||||
|
.innerJoin(payment.charge, charge)
|
||||||
|
.leftJoin(charge.can, can1)
|
||||||
|
.where(
|
||||||
|
charge.createdAt.goe(startDate)
|
||||||
|
.and(charge.createdAt.loe(endDate))
|
||||||
|
.and(charge.status.eq(ChargeStatus.CHARGE))
|
||||||
|
.and(payment.status.eq(PaymentStatus.COMPLETE))
|
||||||
|
)
|
||||||
|
.groupBy(formattedDate, payment.paymentGateway)
|
||||||
|
.orderBy(formattedDate.desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getChargeStatusDetail(startDate: LocalDateTime, endDate: LocalDateTime): List<GetChargeStatusDetailQueryDto> {
|
||||||
|
val formattedDate = Expressions.stringTemplate(
|
||||||
|
"DATE_FORMAT({0}, {1})",
|
||||||
|
Expressions.dateTimeTemplate(
|
||||||
|
LocalDateTime::class.java,
|
||||||
|
"CONVERT_TZ({0},{1},{2})",
|
||||||
|
charge.createdAt,
|
||||||
|
"UTC",
|
||||||
|
"Asia/Seoul"
|
||||||
|
),
|
||||||
|
"%Y-%m-%d %H:%i:%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QGetChargeStatusDetailQueryDto(
|
||||||
|
member.id,
|
||||||
|
member.nickname,
|
||||||
|
payment.method.coalesce(""),
|
||||||
|
payment.price,
|
||||||
|
can1.price,
|
||||||
|
formattedDate
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(charge)
|
||||||
|
.innerJoin(charge.member, member)
|
||||||
|
.innerJoin(charge.payment, payment)
|
||||||
|
.leftJoin(charge.can, can1)
|
||||||
|
.where(
|
||||||
|
charge.createdAt.goe(startDate)
|
||||||
|
.and(charge.createdAt.loe(endDate))
|
||||||
|
.and(charge.status.eq(ChargeStatus.CHARGE))
|
||||||
|
.and(payment.status.eq(PaymentStatus.COMPLETE))
|
||||||
|
)
|
||||||
|
.orderBy(formattedDate.desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) {
|
||||||
|
fun getChargeStatus(startDateStr: String, endDateStr: String): List<GetChargeStatusResponse> {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
|
val startDate = LocalDate.parse(startDateStr, dateTimeFormatter).atTime(0, 0, 0)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
val endDate = LocalDate.parse(endDateStr, dateTimeFormatter).atTime(23, 59, 59)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
var totalChargeAmount = 0
|
||||||
|
var totalChargeCount = 0L
|
||||||
|
|
||||||
|
val chargeStatusList = repository.getChargeStatus(startDate, endDate)
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val chargeAmount = if (it.paymentGateWay == PaymentGateway.APPLE_IAP) {
|
||||||
|
it.appleChargeAmount.toInt()
|
||||||
|
} else {
|
||||||
|
it.pgChargeAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
val chargeCount = it.chargeCount
|
||||||
|
|
||||||
|
totalChargeAmount += chargeAmount
|
||||||
|
totalChargeCount += chargeCount
|
||||||
|
|
||||||
|
GetChargeStatusResponse(
|
||||||
|
date = it.date,
|
||||||
|
chargeAmount = chargeAmount,
|
||||||
|
chargeCount = chargeCount,
|
||||||
|
pg = it.paymentGateWay.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toMutableList()
|
||||||
|
|
||||||
|
chargeStatusList.add(
|
||||||
|
0,
|
||||||
|
GetChargeStatusResponse(
|
||||||
|
date = "합계",
|
||||||
|
chargeAmount = totalChargeAmount,
|
||||||
|
chargeCount = totalChargeCount,
|
||||||
|
pg = ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return chargeStatusList.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getChargeStatusDetail(
|
||||||
|
startDateStr: String,
|
||||||
|
paymentGateway: PaymentGateway
|
||||||
|
): List<GetChargeStatusDetailResponse> {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
|
val startDate = LocalDate.parse(startDateStr, dateTimeFormatter).atTime(0, 0, 0)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
val endDate = LocalDate.parse(startDateStr, dateTimeFormatter).atTime(23, 59, 59)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
return repository.getChargeStatusDetail(startDate, endDate)
|
||||||
|
.asSequence()
|
||||||
|
.filter {
|
||||||
|
if (paymentGateway == PaymentGateway.APPLE_IAP) {
|
||||||
|
it.appleChargeAmount > 0
|
||||||
|
} else {
|
||||||
|
it.pgChargeAmount > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
GetChargeStatusDetailResponse(
|
||||||
|
accountId = it.accountId,
|
||||||
|
nickname = it.nickname,
|
||||||
|
method = it.method,
|
||||||
|
amount = if (paymentGateway == PaymentGateway.APPLE_IAP) {
|
||||||
|
it.appleChargeAmount.toInt()
|
||||||
|
} else {
|
||||||
|
it.pgChargeAmount
|
||||||
|
},
|
||||||
|
datetime = it.datetime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
|
||||||
|
data class GetChargeStatusDetailQueryDto @QueryProjection constructor(
|
||||||
|
val accountId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val method: String,
|
||||||
|
val appleChargeAmount: Double,
|
||||||
|
val pgChargeAmount: Int,
|
||||||
|
val datetime: String
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
data class GetChargeStatusDetailResponse(
|
||||||
|
val accountId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val method: String,
|
||||||
|
val amount: Int,
|
||||||
|
val datetime: String
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||||
|
|
||||||
|
data class GetChargeStatusQueryDto @QueryProjection constructor(
|
||||||
|
val date: String,
|
||||||
|
val appleChargeAmount: Double,
|
||||||
|
val pgChargeAmount: Int,
|
||||||
|
val chargeCount: Long,
|
||||||
|
val paymentGateWay: PaymentGateway
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.charge
|
||||||
|
|
||||||
|
data class GetChargeStatusResponse(
|
||||||
|
val date: String,
|
||||||
|
val chargeAmount: Int,
|
||||||
|
val chargeCount: Long,
|
||||||
|
val pg: String
|
||||||
|
)
|
|
@ -10,9 +10,9 @@ import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/admin/member")
|
@RequestMapping("/admin/member")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
class AdminMemberController(private val service: AdminMemberService) {
|
class AdminMemberController(private val service: AdminMemberService) {
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun getMemberList(pageable: Pageable) = ApiResponse.ok(service.getMemberList(pageable))
|
fun getMemberList(pageable: Pageable) = ApiResponse.ok(service.getMemberList(pageable))
|
||||||
|
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
|
|
|
@ -14,20 +14,18 @@ import org.springframework.web.multipart.MultipartFile
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/admin/member/tag")
|
@RequestMapping("/admin/member/tag")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
class AdminMemberTagController(private val service: AdminMemberTagService) {
|
class AdminMemberTagController(private val service: AdminMemberTagService) {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun enrollmentCreatorTag(
|
fun enrollmentCreatorTag(
|
||||||
@RequestPart("image") image: MultipartFile,
|
@RequestPart("image") image: MultipartFile,
|
||||||
@RequestPart("request") requestString: String
|
@RequestPart("request") requestString: String
|
||||||
) = ApiResponse.ok(service.uploadTagImage(image, requestString), "등록되었습니다.")
|
) = ApiResponse.ok(service.uploadTagImage(image, requestString), "등록되었습니다.")
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun deleteCreatorTag(@PathVariable id: Long) = ApiResponse.ok(service.deleteTag(id), "삭제되었습니다.")
|
fun deleteCreatorTag(@PathVariable id: Long) = ApiResponse.ok(service.deleteTag(id), "삭제되었습니다.")
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun modifyCreatorTag(
|
fun modifyCreatorTag(
|
||||||
@PathVariable id: Long,
|
@PathVariable id: Long,
|
||||||
@RequestPart("image") image: MultipartFile?,
|
@RequestPart("image") image: MultipartFile?,
|
||||||
|
@ -35,7 +33,6 @@ class AdminMemberTagController(private val service: AdminMemberTagService) {
|
||||||
) = ApiResponse.ok(service.modifyTag(id, image, requestString), "수정되었습니다.")
|
) = ApiResponse.ok(service.modifyTag(id, image, requestString), "수정되었습니다.")
|
||||||
|
|
||||||
@PutMapping("/orders")
|
@PutMapping("/orders")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun updateTagOrders(
|
fun updateTagOrders(
|
||||||
@RequestBody request: UpdateTagOrdersRequest
|
@RequestBody request: UpdateTagOrdersRequest
|
||||||
) = ApiResponse.ok(service.updateTagOrders(request.ids), "수정되었습니다.")
|
) = ApiResponse.ok(service.updateTagOrders(request.ids), "수정되었습니다.")
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package kr.co.vividnext.sodalive.extensions
|
||||||
|
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
|
fun Int.moneyFormat(): String = DecimalFormat("###,###").format(this)
|
Loading…
Reference in New Issue