mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
add customer table, dto, repository, service
This commit is contained in:
@@ -2,12 +2,11 @@ package com.example.demo.config
|
||||
|
||||
import com.example.demo.config.properties.KafkaProperties
|
||||
import com.example.demo.config.properties.ValidationProperties
|
||||
import com.example.demo.providers.CityRepository
|
||||
import com.example.demo.providers.MockedShopProvider
|
||||
import com.example.demo.providers.ProductRepository
|
||||
import com.example.demo.providers.ShopProvider
|
||||
import com.example.demo.providers.*
|
||||
import com.example.demo.services.database.city.CityService
|
||||
import com.example.demo.services.database.city.CityServiceImpl
|
||||
import com.example.demo.services.database.customer.CustomerService
|
||||
import com.example.demo.services.database.customer.CustomerServiceImpl
|
||||
import com.example.demo.services.database.product.ProductService
|
||||
import com.example.demo.services.database.product.ProductServiceImpl
|
||||
import com.example.demo.services.kafka.Producer
|
||||
@@ -55,6 +54,12 @@ class AppConfig(
|
||||
@Bean
|
||||
fun cityService(@Autowired cityRepository: CityRepository): CityService = CityServiceImpl(cityRepository)
|
||||
|
||||
@Bean
|
||||
fun customerService(
|
||||
@Autowired customerRepository: CustomerRepository,
|
||||
@Autowired cityRepository: CityRepository,
|
||||
): CustomerService = CustomerServiceImpl(customerRepository, cityRepository)
|
||||
|
||||
@Bean
|
||||
fun schemaValidator(
|
||||
@Autowired validationProperties: ValidationProperties,
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
package com.example.demo.models
|
||||
|
||||
data class Customer(val name: String, val city: City, val orders: List<Order>) {
|
||||
/**
|
||||
* Return the most expensive product among all delivered products
|
||||
*/
|
||||
fun getMostExpensiveDeliveredProduct(): Product? = orders.filter { ord -> ord.isDelivered }
|
||||
.flatMap { ord -> ord.products }
|
||||
.maxByOrNull { pr -> pr.price }
|
||||
import com.example.demo.services.serializables.OffsetDateTimeSerialization
|
||||
import com.example.demo.services.serializables.UuidSerialization
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.relational.core.mapping.Column
|
||||
import org.springframework.data.relational.core.mapping.Table
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.*
|
||||
|
||||
fun getMostExpensiveOrderedProduct(): Product? = orders.flatMap { ord -> ord.products }.maxByOrNull { pr -> pr.price }
|
||||
|
||||
fun getOrderedProducts(): Set<Product> = orders.flatMap { order -> order.products }.toSet()
|
||||
|
||||
fun getTotalOrderPrice(): Double = orders.flatMap { ord -> ord.products }.sumOf { pr -> pr.getPriceDouble()}
|
||||
}
|
||||
@Table("customer")
|
||||
data class Customer(
|
||||
@Id
|
||||
val id: Long?,
|
||||
@Serializable(with = UuidSerialization::class)
|
||||
val guid: UUID,
|
||||
val name: String,
|
||||
val cityId: Long?,
|
||||
@Serializable(with = OffsetDateTimeSerialization::class)
|
||||
@Column(value = "created_at")
|
||||
val createdAt: OffsetDateTime,
|
||||
@Serializable(with = OffsetDateTimeSerialization::class)
|
||||
@Column(value = "updated_at")
|
||||
val updatedAt: OffsetDateTime?,
|
||||
)
|
||||
|
||||
16
src/main/kotlin/com/example/demo/models/CustomerLocal.kt
Normal file
16
src/main/kotlin/com/example/demo/models/CustomerLocal.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.example.demo.models
|
||||
|
||||
data class CustomerLocal(val name: String, val city: City, val orders: List<Order>) {
|
||||
/**
|
||||
* Return the most expensive product among all delivered products
|
||||
*/
|
||||
fun getMostExpensiveDeliveredProduct(): Product? = orders.filter { ord -> ord.isDelivered }
|
||||
.flatMap { ord -> ord.products }
|
||||
.maxByOrNull { pr -> pr.price }
|
||||
|
||||
fun getMostExpensiveOrderedProduct(): Product? = orders.flatMap { ord -> ord.products }.maxByOrNull { pr -> pr.price }
|
||||
|
||||
fun getOrderedProducts(): Set<Product> = orders.flatMap { order -> order.products }.toSet()
|
||||
|
||||
fun getTotalOrderPrice(): Double = orders.flatMap { ord -> ord.products }.sumOf { pr -> pr.getPriceDouble()}
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
package com.example.demo.models
|
||||
|
||||
data class Shop(val name: String, val customers: List<Customer>) {
|
||||
data class Shop(val name: String, val customers: List<CustomerLocal>) {
|
||||
fun checkAllCustomersAreFrom(city: City): Boolean = customers.count { cus -> cus.city == city } == customers.count()
|
||||
|
||||
fun countCustomersFrom(city: City): Int = customers.count { cus -> cus.city == city }
|
||||
|
||||
fun getCitiesCustomersAreFrom(): Set<City> = customers.map { cus -> cus.city }.toSet()
|
||||
|
||||
fun findAnyCustomerFrom(city: City): Customer? = customers.firstOrNull { cus -> cus.city == city }
|
||||
fun findAnyCustomerFrom(city: City): CustomerLocal? = customers.firstOrNull { cus -> cus.city == city }
|
||||
|
||||
fun getAllOrderedProducts(): Set<Product> = customers.flatMap { cus -> cus.getOrderedProducts() }.toSet()
|
||||
|
||||
fun getCustomersFrom(city: City): List<Customer> = customers.filter { cus -> cus.city == city }
|
||||
fun getCustomersFrom(city: City): List<CustomerLocal> = customers.filter { cus -> cus.city == city }
|
||||
|
||||
fun getCustomersSortedByNumberOfOrders(): List<Customer> = customers.sortedBy { cus -> cus.orders.count() }
|
||||
fun getCustomersSortedByNumberOfOrders(): List<CustomerLocal> = customers.sortedBy { cus -> cus.orders.count() }
|
||||
|
||||
fun getCustomerWithMaximumNumberOfOrders(): Customer? = customers.maxByOrNull { cus -> cus.orders.count() }
|
||||
fun getCustomerWithMaximumNumberOfOrders(): CustomerLocal? = customers.maxByOrNull { cus -> cus.orders.count() }
|
||||
|
||||
/**
|
||||
* Return customers who have more undelivered orders than delivered
|
||||
*/
|
||||
fun getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set<Customer> = customers.partition(predicate = fun (cus): Boolean {
|
||||
fun getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set<CustomerLocal> = customers.partition(predicate = fun (cus): Boolean {
|
||||
val (del, undel) = cus.orders.partition { ord -> ord.isDelivered }
|
||||
|
||||
return del.count() < undel.count()
|
||||
@@ -42,7 +42,7 @@ data class Shop(val name: String, val customers: List<Customer>) {
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
fun groupCustomersByCity(): Map<City, List<Customer>> = customers.groupBy { cus -> cus.city }
|
||||
fun groupCustomersByCity(): Map<City, List<CustomerLocal>> = customers.groupBy { cus -> cus.city }
|
||||
|
||||
fun hasCustomerFrom(city: City): Boolean = customers.any { cus -> cus.city == city }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example.demo.providers
|
||||
|
||||
import com.example.demo.models.Customer
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.*
|
||||
|
||||
@Repository
|
||||
interface CustomerRepository: CrudRepository<Customer, Long> {
|
||||
fun findByGuid(guid: UUID): Customer?
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class MockedShopProvider: ShopProvider {
|
||||
val productFour = makeProduct(id = 4, name = "four", price = 14.2)
|
||||
|
||||
return Shop(name="shop", customers= listOf(
|
||||
Customer(
|
||||
CustomerLocal(
|
||||
name = "Foo-1",
|
||||
city = makeCity(id = 1, name = "Foo"),
|
||||
orders = listOf(
|
||||
@@ -20,7 +20,7 @@ class MockedShopProvider: ShopProvider {
|
||||
Order(products = listOf(productThree), isDelivered = false),
|
||||
)
|
||||
),
|
||||
Customer(
|
||||
CustomerLocal(
|
||||
name = "Foo-2",
|
||||
city = makeCity(id = 2, name = "Bar"),
|
||||
orders = listOf(
|
||||
@@ -51,4 +51,4 @@ class MockedShopProvider: ShopProvider {
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example.demo.services.database.customer
|
||||
|
||||
import com.example.demo.models.Customer
|
||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||
import java.util.*
|
||||
|
||||
interface CustomerService {
|
||||
fun findByGuid(guid: UUID): Customer?
|
||||
|
||||
@Throws(CityNotFoundException::class)
|
||||
fun create(name: String, cityGuid: UUID?): Customer
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.example.demo.services.database.customer
|
||||
|
||||
import com.example.demo.models.Customer
|
||||
import com.example.demo.providers.CityRepository
|
||||
import com.example.demo.providers.CustomerRepository
|
||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.*
|
||||
|
||||
class CustomerServiceImpl(
|
||||
private val customerRepository: CustomerRepository,
|
||||
private val cityRepository: CityRepository
|
||||
): CustomerService {
|
||||
override fun findByGuid(guid: UUID): Customer? = customerRepository.findByGuid(guid)
|
||||
|
||||
override fun create(name: String, cityGuid: UUID?): Customer {
|
||||
val cityId: Long? = cityGuid?.let {
|
||||
cityRepository.findByGuid(it)?.id ?: throw CityNotFoundException()
|
||||
}
|
||||
|
||||
val customer = Customer(
|
||||
id = null,
|
||||
guid = UUID.randomUUID(),
|
||||
name = name,
|
||||
cityId = cityId,
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
)
|
||||
|
||||
return customerRepository.save(customer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
create table customer (
|
||||
id bigserial primary key,
|
||||
guid uuid not null,
|
||||
name varchar(255) not null,
|
||||
city_id bigint,
|
||||
created_at timestamptz not null,
|
||||
updated_at timestamptz,
|
||||
CONSTRAINT customer_city_foreign
|
||||
FOREIGN KEY(city_id)
|
||||
REFERENCES city(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
||||
create unique index customer_guid_idx ON customer (guid);
|
||||
@@ -28,7 +28,7 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
||||
val productFour = makeProduct(id = 4, name = "four", price = 14.2)
|
||||
|
||||
val shopMock = Shop(name="shop", customers= listOf(
|
||||
Customer(
|
||||
CustomerLocal(
|
||||
name = "cus-one",
|
||||
city = makeCity(id = 1, name = "city-one"),
|
||||
orders = listOf(
|
||||
@@ -37,7 +37,7 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
||||
Order(products = listOf(productThree), isDelivered = true),
|
||||
)
|
||||
),
|
||||
Customer(
|
||||
CustomerLocal(
|
||||
name = "cus-two",
|
||||
city = makeCity(id = 2, name = "city-two"),
|
||||
orders = listOf(
|
||||
@@ -94,4 +94,4 @@ class ShopControllerTest(@Autowired val mockMvc: MockMvc): BaseUnitTest() {
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.example.demo.services.database.city
|
||||
import com.example.demo.BaseDbTest
|
||||
import com.example.demo.models.City
|
||||
import com.example.demo.providers.CityRepository
|
||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||
import com.example.demo.services.database.exceptions.AlreadyDeletedException
|
||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
@@ -25,7 +25,6 @@ class CityServiceImplDbTest: BaseDbTest() {
|
||||
|
||||
try {
|
||||
city = cityServiceImpl.create(name = name)
|
||||
assertNotNull(city)
|
||||
assertNotNull(city.id)
|
||||
assertEquals(name, city.name)
|
||||
|
||||
@@ -35,7 +34,6 @@ class CityServiceImplDbTest: BaseDbTest() {
|
||||
assertFalse(dbCity.isDeleted())
|
||||
|
||||
val deletedCity = cityServiceImpl.delete(city.guid)
|
||||
assertNotNull(deletedCity)
|
||||
assertEquals(city.id, deletedCity.id)
|
||||
assertNotNull(deletedCity.deletedAt)
|
||||
assertTrue(deletedCity.isDeleted())
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.example.demo.services.database.customer
|
||||
|
||||
import com.example.demo.BaseDbTest
|
||||
import com.example.demo.models.City
|
||||
import com.example.demo.providers.CityRepository
|
||||
import com.example.demo.providers.CustomerRepository
|
||||
import com.example.demo.services.database.exceptions.CityNotFoundException
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.*
|
||||
import kotlin.test.*
|
||||
|
||||
@ContextConfiguration(classes = [CustomerRepository::class, CityRepository::class, CustomerServiceImpl::class])
|
||||
class CustomerServiceImplDbTest: BaseDbTest() {
|
||||
@Autowired
|
||||
private lateinit var customerRepository: CustomerRepository
|
||||
|
||||
@Autowired
|
||||
private lateinit var cityRepository: CityRepository
|
||||
|
||||
@Autowired
|
||||
private lateinit var customerServiceImpl: CustomerServiceImpl
|
||||
|
||||
@Test
|
||||
fun createFind_success() {
|
||||
val nameOne = "Some Dude-One"
|
||||
val nameTwo = "Some Dude-Two"
|
||||
val nameThree = "Some Dude-Three"
|
||||
var city = City(
|
||||
id = null,
|
||||
guid = UUID.randomUUID(),
|
||||
name = "some city name",
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
deletedAt = null,
|
||||
)
|
||||
var customerIds = longArrayOf()
|
||||
|
||||
try {
|
||||
city = cityRepository.save(city)
|
||||
|
||||
customerServiceImpl.create(nameOne, city.guid).let {
|
||||
customerIds += it.id ?: fail("customerWithCity id is null")
|
||||
assertEquals(city.id, it.cityId)
|
||||
assertNotNull(it.createdAt)
|
||||
assertNull(it.updatedAt)
|
||||
}
|
||||
|
||||
val customerWithNoCity = customerServiceImpl.create(nameTwo, null)
|
||||
customerIds += customerWithNoCity.id ?: fail("customerWithNoCity id is null")
|
||||
assertNull(customerWithNoCity.cityId)
|
||||
assertNotNull(customerWithNoCity.createdAt)
|
||||
assertNull(customerWithNoCity.updatedAt)
|
||||
|
||||
val existedCustomer = customerServiceImpl.findByGuid(customerWithNoCity.guid)
|
||||
assertNotNull(existedCustomer)
|
||||
assertEquals(customerWithNoCity.id, existedCustomer.id)
|
||||
|
||||
assertThrows<CityNotFoundException> {
|
||||
customerServiceImpl.create(nameThree, UUID.randomUUID())
|
||||
}
|
||||
} finally {
|
||||
val cityId = city.id
|
||||
if (cityId != null) {
|
||||
cityRepository.deleteById(cityId)
|
||||
}
|
||||
|
||||
customerIds.onEach { customerRepository.deleteById(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user