Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3주차] 송금 서비스 구현 #1

Open
wants to merge 1 commit into
base: feature/subin
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
3. 송금 후에는 계좌잔액을 노출해야 한다.
4. 송금 내역은 5개월동안 보장되고 그 이후의 내역은 삭제되어야 한다.


Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�z�m�f�h�g
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
76
0
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
����
Binary file not shown.
Binary file not shown.
Binary file added build/kotlin/compileKotlin/cacheable/last-build.bin
Binary file not shown.
Binary file not shown.
Binary file not shown.
22 changes: 22 additions & 0 deletions build/resources/main/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;NON_KEYWORDS=USER
username: sa
password:

h2:
console:
enabled: true
path: /h2-console
jpa:
defer-datasource-initialization: true
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
format_sql: true
show_sql: true
physical-naming-strategy: com.example.estpayments.config.ImprovedNamingStrategy
5 changes: 5 additions & 0 deletions http/http-client.env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"default": {
"host": "http://localhost:8080"
}
}
11 changes: 11 additions & 0 deletions http/request/Account.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### 계좌 생성
POST {{host}}/accounts
Content-Type: application/json

{
"userId": 1
}

### 계좌 조회
GET {{host}}/accounts/1
Content-Type: application/json
9 changes: 9 additions & 0 deletions http/request/Transaction.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### 송금
POST {{host}}/transactions
Content-Type: application/json

{
"senderAccountId": 1,
"receiverAccountId": 2,
"amount": 500
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.estdelivery
package com.example.estpayments

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.estpayments.adapter.`in`.web

import com.example.estpayments.adapter.`in`.web.dto.AccountCreateRequest
import com.example.estpayments.adapter.`in`.web.dto.AccountCreateResponse
import com.example.estpayments.adapter.`in`.web.dto.AccountResponse
import com.example.estpayments.application.domain.model.AccountId
import com.example.estpayments.application.port.`in`.AccountCreateUseCase
import com.example.estpayments.application.port.`in`.AccountFindUseCase
import com.example.estpayments.application.port.`in`.command.AccountFindCommand
import org.springframework.web.bind.annotation.GetMapping
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("/accounts")
class AccountController(
private val accountCreateUseCase: AccountCreateUseCase,
private val accountFindUseCase: AccountFindUseCase
) {
@PostMapping
fun createAccount(
@RequestBody accountCreateRequest: AccountCreateRequest
): Result<AccountCreateResponse> =
Result.success(
AccountCreateResponse.from(
accountCreateUseCase.create(accountCreateRequest.toCommand())
)
)

@GetMapping("/{id}")
fun getAccount(
@PathVariable id: Long
): Result<AccountResponse> =
Result.success(
AccountResponse.from(
accountFindUseCase.get(AccountFindCommand(AccountId(id)))
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.estpayments.adapter.`in`.web

import com.example.estpayments.adapter.`in`.web.dto.TransactionExecuteRequest
import com.example.estpayments.adapter.`in`.web.dto.TransactionExecuteResponse
import com.example.estpayments.application.port.`in`.TransactionExecuteUseCase
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("/transactions")
class TransactionController(
private val transactionExecuteUseCase: TransactionExecuteUseCase
) {
@PostMapping
fun executeTransaction(
@RequestBody transactionExecuteRequest: TransactionExecuteRequest
): Result<TransactionExecuteResponse> =
Result.success(
TransactionExecuteResponse.from(
transactionExecuteUseCase.execute(transactionExecuteRequest.toCommand())
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.estpayments.adapter.`in`.web.dto

import com.example.estpayments.application.domain.model.Account
import com.example.estpayments.application.domain.model.UserId
import com.example.estpayments.application.port.`in`.command.AccountCreateCommand

data class AccountResponse(
val id: Long,
val userId: Long,
val balance: Long
) {
companion object {
fun from(account: Account): AccountResponse =
AccountResponse(id = account.id.value, userId = account.userId.value, balance = account.balance.value)
}
}

data class AccountCreateRequest(
val userId: Long
) {
fun toCommand(): AccountCreateCommand = AccountCreateCommand(userId = UserId(userId))
}

data class AccountCreateResponse(
val id: Long,
val userId: Long,
val balance: Long
) {
companion object {
fun from(account: Account): AccountCreateResponse =
AccountCreateResponse(id = account.id.value, userId = account.userId.value, balance = account.balance.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.estpayments.adapter.`in`.web.dto

import com.example.estpayments.application.domain.model.Account
import com.example.estpayments.application.domain.model.AccountId
import com.example.estpayments.application.domain.model.Money
import com.example.estpayments.application.port.`in`.command.TransactionExecuteCommand

data class TransactionExecuteRequest(
val senderAccountId: Long,
val receiverAccountId: Long,
val amount: Long
) {
fun toCommand(): TransactionExecuteCommand = TransactionExecuteCommand(
senderAccountId = AccountId(senderAccountId),
receiverAccountId = AccountId(receiverAccountId),
amount = Money(amount)
)
}

data class TransactionExecuteResponse(
val balance: Long
) {
companion object {
fun from(account: Account): TransactionExecuteResponse =
TransactionExecuteResponse(balance = account.balance.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.estpayments.adapter.out.persistence

import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table

@Entity
@Table(name = "account")
class AccountEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val userId: Long,
var balance: Long,
) : BaseTimeEntity() {
fun updateBalance(money: Long) {
balance = money
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.estpayments.adapter.out.persistence

import com.example.estpayments.application.domain.model.Account
import com.example.estpayments.application.domain.model.AccountId
import com.example.estpayments.application.domain.model.Money
import com.example.estpayments.application.domain.model.UserId
import com.example.estpayments.common.Constant.INITIAL_BALANCE


object AccountMapper {
fun toAccount(accountEntity: AccountEntity): Account =
Account(
id = AccountId(accountEntity.id!!),
userId = UserId(accountEntity.userId),
balance = Money(INITIAL_BALANCE)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.estpayments.adapter.out.persistence

import com.example.estpayments.application.domain.model.Account
import com.example.estpayments.application.port.out.AccountCreatePort
import com.example.estpayments.application.port.out.AccountFindPort
import com.example.estpayments.application.port.out.AccountUpdatePort
import com.example.estpayments.common.Constant
import com.example.estpayments.common.annotation.PersistenceAdapter
import org.springframework.data.repository.findByIdOrNull

@PersistenceAdapter
class AccountPersistenceAdapter(
private val accountRepository: AccountRepository
) : AccountCreatePort, AccountFindPort, AccountUpdatePort {
override fun create(userId: Long): Account =
AccountMapper.toAccount(
accountRepository.save(
AccountEntity(
userId = userId,
balance = Constant.INITIAL_BALANCE
)
)
)

override fun get(id: Long): Account =
AccountMapper.toAccount(
accountRepository.findByIdOrNull(id)
?: throw IllegalArgumentException("not exists account.")
)

override fun updateBalance(account: Account) {
val accountEntity = accountRepository.findByIdOrNull(account.id.value)
?: throw IllegalArgumentException("not exists account.")
accountEntity.updateBalance(account.balance.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.estpayments.adapter.out.persistence

import org.springframework.data.jpa.repository.JpaRepository

interface AccountRepository : JpaRepository<AccountEntity, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.estpayments.adapter.out.persistence

import jakarta.persistence.EntityListeners
import jakarta.persistence.MappedSuperclass
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseTimeEntity {
@CreatedDate
var createdAt: LocalDateTime? = null

@LastModifiedDate
var updatedAt: LocalDateTime? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.estpayments.adapter.out.persistence

import com.example.estpayments.application.domain.model.TransactionStatus
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import java.time.LocalDateTime

@Entity
@Table(name = "transaction")
class TransactionEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val senderAccountId: Long,
val receiverAccountId: Long,
val amount: Long,
val transactionDate: LocalDateTime,
@Enumerated(EnumType.STRING)
var status: TransactionStatus
) : BaseTimeEntity() {
fun completed() {
status = TransactionStatus.COMPLETED
}

fun failed() {
status = TransactionStatus.FAILED
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.estpayments.adapter.out.persistence

import com.example.estpayments.application.domain.model.AccountId
import com.example.estpayments.application.domain.model.Money
import com.example.estpayments.application.domain.model.Transaction
import com.example.estpayments.application.domain.model.TransactionId
import java.time.LocalDateTime


object TransactionMapper {
fun toTransaction(transactionEntity: TransactionEntity): Transaction =
Transaction(
id = TransactionId(transactionEntity.id!!),
senderAccountId = AccountId(transactionEntity.senderAccountId),
receiverAccountId = AccountId(transactionEntity.receiverAccountId),
amount = Money(transactionEntity.amount),
transactionDate = transactionEntity.transactionDate,
status = transactionEntity.status
)

fun toTransactionEntity(transaction: Transaction) = TransactionEntity(
senderAccountId = transaction.senderAccountId.value,
receiverAccountId = transaction.receiverAccountId.value,
amount = transaction.amount.value,
status = transaction.status!!,
transactionDate = LocalDateTime.now()
)
}
Loading