diff --git a/http/AccountRequest.http b/http/AccountRequest.http new file mode 100644 index 0000000..ce13b49 --- /dev/null +++ b/http/AccountRequest.http @@ -0,0 +1,7 @@ +### 계좌 등록 API +POST {{apiAddress}}/accounts +Content-Type: application/json + +{ + "userId": 1 +} \ No newline at end of file diff --git a/http/TransactionRequest.http b/http/TransactionRequest.http new file mode 100644 index 0000000..e236f2e --- /dev/null +++ b/http/TransactionRequest.http @@ -0,0 +1,11 @@ +### 사용자 추가 API +POST {{apiAddress}}/transactions +Content-Type: application/json + +{ + "senderId": 1, + "senderAccountId": 1, + "receiverId": 1, + "receiverAccountId": 2, + "amount": 1000 +} diff --git a/http/UserRequest.http b/http/UserRequest.http new file mode 100644 index 0000000..9f67991 --- /dev/null +++ b/http/UserRequest.http @@ -0,0 +1,7 @@ +### 사용자 추가 API +POST {{apiAddress}}/users +Content-Type: application/json + +{ + "name": "이상경" +} \ No newline at end of file diff --git a/http/http-client.env.json b/http/http-client.env.json new file mode 100644 index 0000000..765dfec --- /dev/null +++ b/http/http-client.env.json @@ -0,0 +1,5 @@ +{ + "local": { + "apiAddress": "http://localhost:8080" + } +} diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/AccountController.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/AccountController.kt new file mode 100644 index 0000000..7a149ae --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/AccountController.kt @@ -0,0 +1,34 @@ +package com.example.estdelivery.adapter.`in`.web + +import com.example.estdelivery.adapter.`in`.web.dto.account.request.RegisterAccountRequest +import com.example.estdelivery.adapter.`in`.web.dto.account.response.AccountSummaryResponse +import com.example.estdelivery.application.port.`in`.RegisterAccountUseCase +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.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/accounts") +class AccountController( + private val registerAccountUseCase: RegisterAccountUseCase +) { + + @GetMapping + fun getAccounts(@RequestParam userId: Long): List { + TODO() + } + + @GetMapping("/{id}") + fun getAccount(@PathVariable id: Long): AccountSummaryResponse { + TODO() + } + + @PostMapping + fun registerAccount(@RequestBody request: RegisterAccountRequest) { + registerAccountUseCase.registerAccount(request.userId) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/ProcessTransactionController.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/ProcessTransactionController.kt new file mode 100644 index 0000000..fa05574 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/ProcessTransactionController.kt @@ -0,0 +1,27 @@ +package com.example.estdelivery.adapter.`in`.web + +import com.example.estdelivery.adapter.`in`.web.dto.transaction.request.RemitRequest +import com.example.estdelivery.application.port.`in`.ProcessTransactionUseCase +import com.example.estdelivery.application.service.ProcessTransactionService +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 ProcessTransactionController( + private val processTransactionUseCase: ProcessTransactionUseCase +) { + + @PostMapping + fun processTransaction(@RequestBody request: RemitRequest) { + processTransactionUseCase.process( + request.senderId, + request.senderAccountId, + request.receiverId, + request.receiverAccountId, + request.amount + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/RegisterUserController.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/RegisterUserController.kt new file mode 100644 index 0000000..6f64943 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/RegisterUserController.kt @@ -0,0 +1,19 @@ +package com.example.estdelivery.adapter.`in`.web + +import com.example.estdelivery.adapter.`in`.web.dto.user.request.RegisterUserRequest +import com.example.estdelivery.application.port.`in`.RegisterUserUseCase +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("/users") +class RegisterUserController( + private val registerUserUseCase: RegisterUserUseCase +) { + @PostMapping + fun registerUser(@RequestBody request: RegisterUserRequest) { + registerUserUseCase.registerUser(request.name) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/request/RegisterAccountRequest.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/request/RegisterAccountRequest.kt new file mode 100644 index 0000000..ca2e333 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/request/RegisterAccountRequest.kt @@ -0,0 +1,5 @@ +package com.example.estdelivery.adapter.`in`.web.dto.account.request + +data class RegisterAccountRequest( + val userId: Long +) diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountDetailResponse.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountDetailResponse.kt new file mode 100644 index 0000000..84cf09c --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountDetailResponse.kt @@ -0,0 +1,6 @@ +package com.example.estdelivery.adapter.`in`.web.dto.account.response + +data class AccountDetailResponse( + val id: Long, + val balance: Long +) diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountSummaryResponse.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountSummaryResponse.kt new file mode 100644 index 0000000..d206040 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/account/response/AccountSummaryResponse.kt @@ -0,0 +1,6 @@ +package com.example.estdelivery.adapter.`in`.web.dto.account.response + +data class AccountSummaryResponse( + val id: Long, + val balance: Long +) diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/DepositRequest.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/DepositRequest.kt new file mode 100644 index 0000000..b7c8c24 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/DepositRequest.kt @@ -0,0 +1,3 @@ +//package com.example.estdelivery.adapter.`in`.web.dto.transaction.request +// +//data class DepositRequest() diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/RemitRequest.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/RemitRequest.kt new file mode 100644 index 0000000..d8f347b --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/transaction/request/RemitRequest.kt @@ -0,0 +1,12 @@ +package com.example.estdelivery.adapter.`in`.web.dto.transaction.request + +import java.math.BigDecimal +import java.time.LocalDateTime + +data class RemitRequest( + val senderId: Long, + val senderAccountId: Long, + val receiverId: Long, + val receiverAccountId: Long, + val amount: BigDecimal, +) diff --git a/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/user/request/RegisterUserRequest.kt b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/user/request/RegisterUserRequest.kt new file mode 100644 index 0000000..37dfda6 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/in/web/dto/user/request/RegisterUserRequest.kt @@ -0,0 +1,5 @@ +package com.example.estdelivery.adapter.`in`.web.dto.user.request + +data class RegisterUserRequest( + val name: String, +) diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/adapter/AccountPersistenceAdapter.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/adapter/AccountPersistenceAdapter.kt new file mode 100644 index 0000000..cecc0e1 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/adapter/AccountPersistenceAdapter.kt @@ -0,0 +1,33 @@ +package com.example.estdelivery.adapter.out.persistence.account.adapter + +import com.example.estdelivery.adapter.out.persistence.account.mapper.toDomain +import com.example.estdelivery.adapter.out.persistence.account.mapper.toEntity +import com.example.estdelivery.adapter.out.persistence.account.repository.AccountRepository +import com.example.estdelivery.application.port.out.AccountPersistencePort +import com.example.estdelivery.application.port.out.LoadAccountsPort +import com.example.estdelivery.domain.Account +import org.springframework.stereotype.Component + +@Component +class AccountPersistenceAdapter( + private val accountRepository: AccountRepository +) : AccountPersistencePort, LoadAccountsPort { + override fun loadAccount(userId: Long, accountId: Long): Account { + return accountRepository.findByIdAndUserId(accountId, userId).toDomain() + } + + override fun loadAccounts(userId: Long): List { + TODO() + } + + override fun updateAccountBalance(account: Account) { + val accountEntity = accountRepository.findById(account.id!!).orElseThrow { + throw Exception("account not found") + } + accountEntity.updateBalance(account.balance) + } + + override fun registerAccount(account: Account) { + accountRepository.save(account.toEntity()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/entity/AccountEntity.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/entity/AccountEntity.kt new file mode 100644 index 0000000..a0be0be --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/entity/AccountEntity.kt @@ -0,0 +1,34 @@ +package com.example.estdelivery.adapter.out.persistence.account.entity + +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.math.BigDecimal + +@Entity +@Table(name = "account") +class AccountEntity( + userId: Long, + accountNumber: String, + balance: BigDecimal +) { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + var userId: Long = userId + protected set + + var accountNumber: String = accountNumber + protected set + + var balance: BigDecimal = balance + protected set + + fun updateBalance(amount: BigDecimal) { + balance += amount + } +} diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/mapper/AccountMapper.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/mapper/AccountMapper.kt new file mode 100644 index 0000000..a3aefa0 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/mapper/AccountMapper.kt @@ -0,0 +1,21 @@ +package com.example.estdelivery.adapter.out.persistence.account.mapper + +import com.example.estdelivery.adapter.out.persistence.account.entity.AccountEntity +import com.example.estdelivery.domain.Account + +fun Account.toEntity(): AccountEntity { + return AccountEntity( + userId = this.userId, + accountNumber = this.accountNumber, + balance = this.balance + ) +} + +fun AccountEntity.toDomain(): Account { + return Account( + id = this.id!!, + userId = this.userId, + accountNumber = this.accountNumber, + balance = this.balance + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/repository/AccountRepository.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/repository/AccountRepository.kt new file mode 100644 index 0000000..07ece23 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/account/repository/AccountRepository.kt @@ -0,0 +1,8 @@ +package com.example.estdelivery.adapter.out.persistence.account.repository + +import com.example.estdelivery.adapter.out.persistence.account.entity.AccountEntity +import org.springframework.data.jpa.repository.JpaRepository + +interface AccountRepository : JpaRepository { + fun findByIdAndUserId(id: Long, userId: Long): AccountEntity +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/transaction/entity/TransactionEntity.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/transaction/entity/TransactionEntity.kt new file mode 100644 index 0000000..bc3ae89 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/transaction/entity/TransactionEntity.kt @@ -0,0 +1,27 @@ +package com.example.estdelivery.adapter.out.persistence.transaction.entity + +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.math.BigDecimal + +@Entity +@Table(name = "transaction") +class TransactionEntity( + accountId: String, + amount: BigDecimal +) { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + var accountId: String? = accountId + protected set + + var amount: BigDecimal = amount + protected set +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/adapter/UserPersistenceAdapter.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/adapter/UserPersistenceAdapter.kt new file mode 100644 index 0000000..92ee016 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/adapter/UserPersistenceAdapter.kt @@ -0,0 +1,16 @@ +package com.example.estdelivery.adapter.out.persistence.user.adapter + +import com.example.estdelivery.adapter.out.persistence.user.repository.UserRepository +import com.example.estdelivery.adapter.out.persistence.user.mapper.toEntity +import com.example.estdelivery.application.port.out.UserPersistencePort +import com.example.estdelivery.domain.User +import org.springframework.stereotype.Component + +@Component +class UserPersistenceAdapter( + private val userRepository: UserRepository +) : UserPersistencePort { + override fun save(user: User) { + userRepository.save(user.toEntity()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/entity/UserEntity.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/entity/UserEntity.kt new file mode 100644 index 0000000..519c578 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/entity/UserEntity.kt @@ -0,0 +1,26 @@ +package com.example.estdelivery.adapter.out.persistence.user.entity + +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.math.BigDecimal + +@Entity +@Table(name = "user") +class UserEntity( + name: String +// balance: BigDecimal +) { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + protected set + + var name: String? = name + protected set + +// var balance: BigDecimal = balance +// protected set +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/mapper/UserMapper.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/mapper/UserMapper.kt new file mode 100644 index 0000000..82174b2 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/mapper/UserMapper.kt @@ -0,0 +1,19 @@ +package com.example.estdelivery.adapter.out.persistence.user.mapper + +import com.example.estdelivery.adapter.out.persistence.user.entity.UserEntity +import com.example.estdelivery.domain.User + +fun User.toEntity(): UserEntity { + return UserEntity( + name = this.name +// balance = this.balance + ) +} + +fun UserEntity.toDomain(): User { + return User( + id = this.id!!, + name = this.name!!, +// balance = this.balance + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/repository/UserRepository.kt b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/repository/UserRepository.kt new file mode 100644 index 0000000..679e6c1 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/adapter/out/persistence/user/repository/UserRepository.kt @@ -0,0 +1,7 @@ +package com.example.estdelivery.adapter.out.persistence.user.repository + +import com.example.estdelivery.adapter.out.persistence.user.entity.UserEntity +import org.springframework.data.jpa.repository.JpaRepository + +interface UserRepository : JpaRepository { +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/in/ProcessTransactionUseCase.kt b/src/main/kotlin/com/example/estdelivery/application/port/in/ProcessTransactionUseCase.kt new file mode 100644 index 0000000..87cceca --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/in/ProcessTransactionUseCase.kt @@ -0,0 +1,14 @@ +package com.example.estdelivery.application.port.`in` + +import java.math.BigDecimal +import java.time.LocalDateTime + +interface ProcessTransactionUseCase { + fun process( + senderId: Long, + senderAccountId: Long, + receiverId: Long, + receiverAccountId: Long, + amount: BigDecimal + ): BigDecimal +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterAccountUseCase.kt b/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterAccountUseCase.kt new file mode 100644 index 0000000..d5feaa3 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterAccountUseCase.kt @@ -0,0 +1,5 @@ +package com.example.estdelivery.application.port.`in` + +interface RegisterAccountUseCase { + fun registerAccount(userId: Long) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterUserUseCase.kt b/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterUserUseCase.kt new file mode 100644 index 0000000..f283c30 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/in/RegisterUserUseCase.kt @@ -0,0 +1,5 @@ +package com.example.estdelivery.application.port.`in` + +interface RegisterUserUseCase { + fun registerUser(name: String) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/out/AccountPersistencePort.kt b/src/main/kotlin/com/example/estdelivery/application/port/out/AccountPersistencePort.kt new file mode 100644 index 0000000..383a489 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/out/AccountPersistencePort.kt @@ -0,0 +1,8 @@ +package com.example.estdelivery.application.port.out + +import com.example.estdelivery.domain.Account + +interface AccountPersistencePort { + fun updateAccountBalance(account: Account) + fun registerAccount(account: Account) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/out/LoadAccountsPort.kt b/src/main/kotlin/com/example/estdelivery/application/port/out/LoadAccountsPort.kt new file mode 100644 index 0000000..b083850 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/out/LoadAccountsPort.kt @@ -0,0 +1,8 @@ +package com.example.estdelivery.application.port.out + +import com.example.estdelivery.domain.Account + +interface LoadAccountsPort { + fun loadAccounts(userId: Long): List + fun loadAccount(userId: Long, accountId: Long): Account +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/out/TransactionPersistencePort.kt b/src/main/kotlin/com/example/estdelivery/application/port/out/TransactionPersistencePort.kt new file mode 100644 index 0000000..b60081e --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/out/TransactionPersistencePort.kt @@ -0,0 +1,4 @@ +package com.example.estdelivery.application.port.out + +interface TransactionPersistencePort { +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/port/out/UserPersistencePort.kt b/src/main/kotlin/com/example/estdelivery/application/port/out/UserPersistencePort.kt new file mode 100644 index 0000000..a8f60d4 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/port/out/UserPersistencePort.kt @@ -0,0 +1,7 @@ +package com.example.estdelivery.application.port.out + +import com.example.estdelivery.domain.User + +interface UserPersistencePort { + fun save(user: User) +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/service/ProcessTransactionService.kt b/src/main/kotlin/com/example/estdelivery/application/service/ProcessTransactionService.kt new file mode 100644 index 0000000..9e2e366 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/service/ProcessTransactionService.kt @@ -0,0 +1,57 @@ +package com.example.estdelivery.application.service + +import com.example.estdelivery.application.port.`in`.ProcessTransactionUseCase +import com.example.estdelivery.application.port.out.AccountPersistencePort +import com.example.estdelivery.application.port.out.LoadAccountsPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.util.concurrent.ConcurrentHashMap + +@Service +class ProcessTransactionService( + private val accountPersistencePort: AccountPersistencePort, +// private val transactionPersistencePort: TransactionPersistencePort, +// private val userPersistencePort: UserPersistencePort, + private val loadAccountPort : LoadAccountsPort +) : ProcessTransactionUseCase { + + private val processingTransactions = ConcurrentHashMap() + + /* + * 1. 보내는 사용자의 계좌 정보 조회 + * 2. 받는 사용자의 계좌 정보 조회 + * 3. 보내는 사용자의 계좌에서 출금 + * 4. 받는 사용자의 계좌에 입금 + */ + @Transactional + override fun process( + senderId: Long, + senderAccountId: Long, + receiverId: Long, + receiverAccountId: Long, + amount: BigDecimal + ): BigDecimal { + if (processingTransactions.putIfAbsent("${senderId}${receiverId}", true) != null) { + throw RuntimeException("Processing same transaction") + } + + try { + val senderAccount = loadAccountPort.loadAccount(senderId, senderAccountId) + val receiverAccount = loadAccountPort.loadAccount(receiverId, receiverAccountId) + + senderAccount.withdraw(amount) + receiverAccount.deposit(amount) + + accountPersistencePort.updateAccountBalance(senderAccount) + accountPersistencePort.updateAccountBalance(receiverAccount) + + // TODO : 트랜잭션 정보 저장 + + } finally { + processingTransactions.remove("${senderId}${receiverId}") + } + + return amount + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/service/RegisterAccountService.kt b/src/main/kotlin/com/example/estdelivery/application/service/RegisterAccountService.kt new file mode 100644 index 0000000..0bd9bda --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/service/RegisterAccountService.kt @@ -0,0 +1,21 @@ +package com.example.estdelivery.application.service + +import com.example.estdelivery.application.port.out.AccountPersistencePort +import com.example.estdelivery.application.port.`in`.RegisterAccountUseCase +import com.example.estdelivery.application.utils.AccountNumberGenerator +import com.example.estdelivery.domain.Account +import org.springframework.stereotype.Service +import java.math.BigDecimal + +@Service +class RegisterAccountService( + private val accountPersistencePort: AccountPersistencePort +) : RegisterAccountUseCase { + override fun registerAccount(userId: Long) { + val accountNumber = AccountNumberGenerator.generate() + val initialBalance = BigDecimal.ZERO + + val account = Account(userId, accountNumber, initialBalance) + accountPersistencePort.registerAccount(account) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/service/RegisterUserService.kt b/src/main/kotlin/com/example/estdelivery/application/service/RegisterUserService.kt new file mode 100644 index 0000000..9a366c7 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/service/RegisterUserService.kt @@ -0,0 +1,20 @@ +package com.example.estdelivery.application.service + +import com.example.estdelivery.application.port.`in`.RegisterUserUseCase +import com.example.estdelivery.application.port.out.UserPersistencePort +import com.example.estdelivery.domain.User +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal + +@Service +class RegisterUserService( + private val userPersistencePort: UserPersistencePort +) : RegisterUserUseCase { + + @Transactional + override fun registerUser(name: String) { + val user = User(name) + userPersistencePort.save(user) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/application/utils/AccountNumberGenerator.kt b/src/main/kotlin/com/example/estdelivery/application/utils/AccountNumberGenerator.kt new file mode 100644 index 0000000..d085263 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/application/utils/AccountNumberGenerator.kt @@ -0,0 +1,13 @@ +package com.example.estdelivery.application.utils + +import java.util.concurrent.atomic.AtomicLong + +object AccountNumberGenerator { + private const val ACCOUNT_NUMBER_LENGTH = 10 + private val lastNumber = AtomicLong(System.currentTimeMillis()) + + fun generate(): String { + val newNumber = lastNumber.incrementAndGet() + return newNumber.toString().takeLast(ACCOUNT_NUMBER_LENGTH) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/example/estdelivery/domain/Account.kt b/src/main/kotlin/com/example/estdelivery/domain/Account.kt new file mode 100644 index 0000000..6ed8aee --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/domain/Account.kt @@ -0,0 +1,18 @@ +package com.example.estdelivery.domain + +import java.math.BigDecimal + +data class Account( + val userId: Long, + val accountNumber: String, + var balance: BigDecimal, + val id: Long? = null +) { + fun deposit(amount: BigDecimal) { + balance += amount + } + + fun withdraw(amount: BigDecimal) { + balance -= amount + } +} diff --git a/src/main/kotlin/com/example/estdelivery/domain/Transaction.kt b/src/main/kotlin/com/example/estdelivery/domain/Transaction.kt new file mode 100644 index 0000000..cd55ac5 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/domain/Transaction.kt @@ -0,0 +1,12 @@ +package com.example.estdelivery.domain + +import java.math.BigDecimal +import java.time.LocalDateTime + +data class Transaction( + val id: Long, + val senderId: Long, + val receiverId: Long, + val amount: BigDecimal, + val timestamp: LocalDateTime +) diff --git a/src/main/kotlin/com/example/estdelivery/domain/User.kt b/src/main/kotlin/com/example/estdelivery/domain/User.kt new file mode 100644 index 0000000..bbe33a0 --- /dev/null +++ b/src/main/kotlin/com/example/estdelivery/domain/User.kt @@ -0,0 +1,6 @@ +package com.example.estdelivery.domain + +data class User( + val name: String, + val id: Long? = null +)