diff --git a/build.gradle.kts b/build.gradle.kts index dcc59e4f..78527237 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,12 @@ plugins { - java - id("org.springframework.boot") - id("io.spring.dependency-management") - id("com.gorylenko.gradle-git-properties") - id("com.github.spotbugs") + java + kotlin("jvm") + kotlin("plugin.spring") + kotlin("plugin.jpa") + id("org.springframework.boot") + id("io.spring.dependency-management") + id("com.gorylenko.gradle-git-properties") + id("com.github.spotbugs") } val appGroup: String by project @@ -15,9 +18,23 @@ version = appVersion description = appDescription java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xjsr305=strict", "-Xannotation-default-target=param-property") + } +} + +spotbugs { + excludeFilter.set(file("${projectDir}/spotbugs-exclude.xml")) +} + +springBoot { + buildInfo() } val springCloud: String by project @@ -33,81 +50,76 @@ val tsid: String by project val mockitoAgent: Configuration? = configurations.create("mockitoAgent") repositories { - mavenCentral() + mavenCentral() } dependencyManagement { - imports { - mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloud") - } + imports { + mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloud") + } } dependencies { - implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("org.springframework.boot:spring-boot-starter-aspectj") - implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-data-redis") - implementation("org.springframework.boot:spring-boot-starter-flyway") - implementation("org.springframework.boot:spring-boot-starter-mail") - implementation("org.springframework.boot:spring-boot-starter-security") - implementation("org.springframework.boot:spring-boot-starter-thymeleaf") - implementation("org.springframework.boot:spring-boot-starter-validation") - implementation("org.springframework.boot:spring-boot-starter-webmvc") - implementation("org.springframework.cloud:spring-cloud-starter-openfeign") - implementation("org.flywaydb:flyway-mysql") - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springdoc") { - exclude(group = "org.webjars", module = "swagger-ui") - } - implementation("org.redisson:redisson-spring-boot-starter:$redisson") - implementation("com.github.f4b6a3:tsid-creator:$tsid") - implementation("io.jsonwebtoken:jjwt-api:$jjwt") - implementation("io.github.openfeign.querydsl:querydsl-core:$queryDSL") - implementation("io.github.openfeign.querydsl:querydsl-jpa:$queryDSL") - implementation("org.apache.pdfbox:pdfbox:$pdfbox") - implementation("com.sksamuel.scrimage:scrimage-core:$scrimage") - implementation("com.sksamuel.scrimage:scrimage-webp:$scrimage") - implementation("com.oracle.oci.sdk:oci-java-sdk-common:$oci") - implementation("com.oracle.oci.sdk:oci-java-sdk-objectstorage:$oci") - implementation("com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey3:$oci") - - compileOnly("org.projectlombok:lombok") - - runtimeOnly("com.mysql:mysql-connector-j") - runtimeOnly("io.micrometer:micrometer-registry-prometheus") - runtimeOnly("io.jsonwebtoken:jjwt-impl:$jjwt") - runtimeOnly("io.jsonwebtoken:jjwt-jackson:$jjwt") - - annotationProcessor("org.projectlombok:lombok") - annotationProcessor("io.github.openfeign.querydsl:querydsl-apt:$queryDSL:jpa") - annotationProcessor("jakarta.annotation:jakarta.annotation-api") - annotationProcessor("jakarta.persistence:jakarta.persistence-api") - - testImplementation("org.springframework.boot:spring-boot-starter-actuator-test") - testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test") - testImplementation("org.springframework.boot:spring-boot-starter-data-redis-test") - testImplementation("org.springframework.boot:spring-boot-starter-flyway-test") - testImplementation("org.springframework.boot:spring-boot-starter-mail-test") - testImplementation("org.springframework.boot:spring-boot-starter-security-test") - testImplementation("org.springframework.boot:spring-boot-starter-thymeleaf-test") - testImplementation("org.springframework.boot:spring-boot-starter-validation-test") - testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test") - testImplementation("org.mockito:mockito-core:$mockito") - mockitoAgent?.let { it("org.mockito:mockito-core:$mockito") { isTransitive = false } } - - testCompileOnly("org.projectlombok:lombok") - testAnnotationProcessor("org.projectlombok:lombok") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("tools.jackson.module:jackson-module-kotlin") + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("org.springframework.boot:spring-boot-starter-aspectj") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-data-redis") + implementation("org.springframework.boot:spring-boot-starter-flyway") + implementation("org.springframework.boot:spring-boot-starter-mail") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springframework.boot:spring-boot-starter-thymeleaf") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-webmvc") + implementation("org.springframework.cloud:spring-cloud-starter-openfeign") + implementation("org.flywaydb:flyway-mysql") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springdoc") { + exclude(group = "org.webjars", module = "swagger-ui") + } + implementation("org.redisson:redisson-spring-boot-starter:$redisson") + implementation("com.github.f4b6a3:tsid-creator:$tsid") + implementation("io.jsonwebtoken:jjwt-api:$jjwt") + implementation("io.github.openfeign.querydsl:querydsl-core:$queryDSL") + implementation("io.github.openfeign.querydsl:querydsl-jpa:$queryDSL") + implementation("org.apache.pdfbox:pdfbox:$pdfbox") + implementation("com.sksamuel.scrimage:scrimage-core:$scrimage") + implementation("com.sksamuel.scrimage:scrimage-webp:$scrimage") + implementation("com.oracle.oci.sdk:oci-java-sdk-common:$oci") + implementation("com.oracle.oci.sdk:oci-java-sdk-objectstorage:$oci") + implementation("com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey3:$oci") -tasks.withType { - useJUnitPlatform() - mockitoAgent?.let { jvmArgs("-javaagent:${it.asPath}", "-Xshare:off") } -} + compileOnly("org.projectlombok:lombok") -spotbugs { - excludeFilter.set(file("${projectDir}/spotbugs-exclude.xml")) + runtimeOnly("com.mysql:mysql-connector-j") + runtimeOnly("io.micrometer:micrometer-registry-prometheus") + runtimeOnly("io.jsonwebtoken:jjwt-impl:$jjwt") + runtimeOnly("io.jsonwebtoken:jjwt-jackson:$jjwt") + + annotationProcessor("org.projectlombok:lombok") + annotationProcessor("io.github.openfeign.querydsl:querydsl-apt:$queryDSL:jpa") + annotationProcessor("jakarta.annotation:jakarta.annotation-api") + annotationProcessor("jakarta.persistence:jakarta.persistence-api") + + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("org.springframework.boot:spring-boot-starter-actuator-test") + testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test") + testImplementation("org.springframework.boot:spring-boot-starter-data-redis-test") + testImplementation("org.springframework.boot:spring-boot-starter-flyway-test") + testImplementation("org.springframework.boot:spring-boot-starter-mail-test") + testImplementation("org.springframework.boot:spring-boot-starter-security-test") + testImplementation("org.springframework.boot:spring-boot-starter-thymeleaf-test") + testImplementation("org.springframework.boot:spring-boot-starter-validation-test") + testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test") + testImplementation("org.mockito:mockito-core:$mockito") + mockitoAgent?.let { it("org.mockito:mockito-core:$mockito") { isTransitive = false } } + + testCompileOnly("org.projectlombok:lombok") + testAnnotationProcessor("org.projectlombok:lombok") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } -springBoot { - buildInfo() +tasks.withType { + useJUnitPlatform() + mockitoAgent?.let { jvmArgs("-javaagent:${it.asPath}", "-Xshare:off") } } diff --git a/docker-compose.yml b/docker-compose.yml index ecf8fda1..a49c89d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: everyonewaiter-api: container_name: everyonewaiter-api build: . - image: everyonewaiter-api:1.0.8 + image: everyonewaiter-api:1.0.9 ports: - "8081:8081" volumes: diff --git a/gradle.properties b/gradle.properties index 1dad7f4b..047314a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,13 @@ +# Configuration +org.gradle.configuration-cache=true + # Artifact appGroup=com.everyonewaiter -appVersion=1.0.8 +appVersion=1.0.9 appDescription=모두의 웨이터 API # Plugin +kotlin=2.2.21 springBoot=4.0.1 springCloud=2025.1.0 springDependencyManagement=1.1.7 diff --git a/settings.gradle.kts b/settings.gradle.kts index 58d860e5..a7928f0d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,19 +1,23 @@ rootProject.name = "everyonewaiter-api" pluginManagement { - val springBoot: String by settings - val springDependencyManagement: String by settings - val gitProperties: String by settings - val spotBugs: String by settings + val kotlin: String by settings + val springBoot: String by settings + val springDependencyManagement: String by settings + val gitProperties: String by settings + val spotBugs: String by settings - resolutionStrategy { - eachPlugin { - when (requested.id.id) { - "org.springframework.boot" -> useVersion(springBoot) - "io.spring.dependency-management" -> useVersion(springDependencyManagement) - "com.gorylenko.gradle-git-properties" -> useVersion(gitProperties) - "com.github.spotbugs" -> useVersion(spotBugs) - } - } + resolutionStrategy { + eachPlugin { + when (requested.id.id) { + "org.jetbrains.kotlin.jvm" -> useVersion(kotlin) + "org.jetbrains.kotlin.plugin.spring" -> useVersion(kotlin) + "org.jetbrains.kotlin.plugin.jpa" -> useVersion(kotlin) + "org.springframework.boot" -> useVersion(springBoot) + "io.spring.dependency-management" -> useVersion(springDependencyManagement) + "com.gorylenko.gradle-git-properties" -> useVersion(gitProperties) + "com.github.spotbugs" -> useVersion(spotBugs) + } } + } } diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 1b0a4f43..72415b6f 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -1,19 +1,24 @@ + + + + - + - + + @@ -32,13 +37,16 @@ + + + - + @@ -57,10 +65,12 @@ + + + - diff --git a/src/main/java/com/everyonewaiter/EveryonewaiterApiConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/integration/AsyncConfiguration.java similarity index 88% rename from src/main/java/com/everyonewaiter/EveryonewaiterApiConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/integration/AsyncConfiguration.java index dad3f1a7..286a50cd 100644 --- a/src/main/java/com/everyonewaiter/EveryonewaiterApiConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/integration/AsyncConfiguration.java @@ -1,4 +1,4 @@ -package com.everyonewaiter; +package com.everyonewaiter.adapter.config.integration; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -10,7 +10,7 @@ @Configuration @EnableAsync -class EveryonewaiterApiConfiguration { +class AsyncConfiguration { @Bean(name = "eventTaskExecutor") public TaskExecutor eventTaskExecutor() { diff --git a/src/main/java/com/everyonewaiter/adapter/integration/OpenFeignConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/integration/OpenFeignConfiguration.java similarity index 88% rename from src/main/java/com/everyonewaiter/adapter/integration/OpenFeignConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/integration/OpenFeignConfiguration.java index 437e3b2e..f7b4d6bf 100644 --- a/src/main/java/com/everyonewaiter/adapter/integration/OpenFeignConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/integration/OpenFeignConfiguration.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.integration; +package com.everyonewaiter.adapter.config.integration; import feign.Logger; import org.springframework.cloud.openfeign.EnableFeignClients; diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/JpaConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/persistence/JpaConfiguration.java similarity index 90% rename from src/main/java/com/everyonewaiter/adapter/persistence/JpaConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/persistence/JpaConfiguration.java index d93b822b..0990b9a6 100644 --- a/src/main/java/com/everyonewaiter/adapter/persistence/JpaConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/persistence/JpaConfiguration.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.persistence; +package com.everyonewaiter.adapter.config.persistence; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/RedisConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/persistence/RedisConfiguration.java similarity index 96% rename from src/main/java/com/everyonewaiter/adapter/persistence/RedisConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/persistence/RedisConfiguration.java index f5532f21..d1b55051 100644 --- a/src/main/java/com/everyonewaiter/adapter/persistence/RedisConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/persistence/RedisConfiguration.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.persistence; +package com.everyonewaiter.adapter.config.persistence; import lombok.RequiredArgsConstructor; import org.redisson.Redisson; diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/RedisProperties.java b/src/main/java/com/everyonewaiter/adapter/config/persistence/RedisProperties.java similarity index 86% rename from src/main/java/com/everyonewaiter/adapter/persistence/RedisProperties.java rename to src/main/java/com/everyonewaiter/adapter/config/persistence/RedisProperties.java index 71e293a5..ddcdcd41 100644 --- a/src/main/java/com/everyonewaiter/adapter/persistence/RedisProperties.java +++ b/src/main/java/com/everyonewaiter/adapter/config/persistence/RedisProperties.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.persistence; +package com.everyonewaiter.adapter.config.persistence; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/everyonewaiter/adapter/security/CorsAllowOrigins.java b/src/main/java/com/everyonewaiter/adapter/config/security/CorsAllowOrigins.java similarity index 88% rename from src/main/java/com/everyonewaiter/adapter/security/CorsAllowOrigins.java rename to src/main/java/com/everyonewaiter/adapter/config/security/CorsAllowOrigins.java index 92dccf01..fd9d55cc 100644 --- a/src/main/java/com/everyonewaiter/adapter/security/CorsAllowOrigins.java +++ b/src/main/java/com/everyonewaiter/adapter/config/security/CorsAllowOrigins.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.security; +package com.everyonewaiter.adapter.config.security; import java.util.Collections; import java.util.List; diff --git a/src/main/java/com/everyonewaiter/adapter/security/SecurityConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/security/SecurityConfiguration.java similarity index 97% rename from src/main/java/com/everyonewaiter/adapter/security/SecurityConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/security/SecurityConfiguration.java index ea7fffc0..b5fbc3a6 100644 --- a/src/main/java/com/everyonewaiter/adapter/security/SecurityConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/security/SecurityConfiguration.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.security; +package com.everyonewaiter.adapter.config.security; import java.util.List; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/InstantJacksonSerializer.java b/src/main/java/com/everyonewaiter/adapter/config/web/InstantJacksonSerializer.java similarity index 81% rename from src/main/java/com/everyonewaiter/adapter/web/config/InstantJacksonSerializer.java rename to src/main/java/com/everyonewaiter/adapter/config/web/InstantJacksonSerializer.java index 7bc8c756..2de8b0ab 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/InstantJacksonSerializer.java +++ b/src/main/java/com/everyonewaiter/adapter/config/web/InstantJacksonSerializer.java @@ -1,9 +1,11 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.config.web; import static com.everyonewaiter.domain.support.TimeZone.ASIA_SEOUL; +import static lombok.AccessLevel.PRIVATE; import com.everyonewaiter.domain.support.DateFormatter; import java.time.Instant; +import lombok.NoArgsConstructor; import org.springframework.boot.jackson.JacksonComponent; import tools.jackson.core.JacksonException; import tools.jackson.core.JsonGenerator; @@ -11,8 +13,10 @@ import tools.jackson.databind.ValueSerializer; @JacksonComponent +@NoArgsConstructor(access = PRIVATE) class InstantJacksonSerializer { + @SuppressWarnings("unused") static class Serializer extends ValueSerializer { @Override diff --git a/src/main/java/com/everyonewaiter/adapter/web/docs/SwaggerConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/web/SwaggerConfiguration.java similarity index 97% rename from src/main/java/com/everyonewaiter/adapter/web/docs/SwaggerConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/web/SwaggerConfiguration.java index 77aaf3bb..d27d3f89 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/docs/SwaggerConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/web/SwaggerConfiguration.java @@ -1,6 +1,8 @@ -package com.everyonewaiter.adapter.web.docs; +package com.everyonewaiter.adapter.config.web; import com.everyonewaiter.adapter.web.api.ErrorResponse; +import com.everyonewaiter.adapter.web.docs.ApiErrorResponse; +import com.everyonewaiter.adapter.web.docs.ApiErrorResponses; import com.everyonewaiter.domain.shared.ErrorCode; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/WebConfiguration.java b/src/main/java/com/everyonewaiter/adapter/config/web/WebConfiguration.java similarity index 78% rename from src/main/java/com/everyonewaiter/adapter/web/config/WebConfiguration.java rename to src/main/java/com/everyonewaiter/adapter/config/web/WebConfiguration.java index 0890bfc9..962407e9 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/WebConfiguration.java +++ b/src/main/java/com/everyonewaiter/adapter/config/web/WebConfiguration.java @@ -1,5 +1,7 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.config.web; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccountResolver; +import com.everyonewaiter.adapter.web.auth.AuthenticationDeviceResolver; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializer.java b/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializer.java new file mode 100644 index 00000000..b8891ab5 --- /dev/null +++ b/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializer.java @@ -0,0 +1,866 @@ +package com.everyonewaiter.adapter.persistence; + +import static com.everyonewaiter.domain.account.QAccount.account; +import static com.everyonewaiter.domain.device.QDevice.device; + +import com.everyonewaiter.application.account.required.AccountRepository; +import com.everyonewaiter.application.device.required.DeviceRepository; +import com.everyonewaiter.application.health.provided.HealthCheckCreator; +import com.everyonewaiter.application.health.required.ApkVersionRepository; +import com.everyonewaiter.application.menu.required.CategoryRepository; +import com.everyonewaiter.application.menu.required.MenuRepository; +import com.everyonewaiter.application.store.required.RegistrationRepository; +import com.everyonewaiter.application.store.required.StoreRepository; +import com.everyonewaiter.domain.account.Account; +import com.everyonewaiter.domain.account.AccountCreateRequest; +import com.everyonewaiter.domain.account.AccountPermission; +import com.everyonewaiter.domain.account.PasswordEncoder; +import com.everyonewaiter.domain.device.Device; +import com.everyonewaiter.domain.device.DeviceCreateRequest; +import com.everyonewaiter.domain.device.DevicePaymentType; +import com.everyonewaiter.domain.device.DevicePurpose; +import com.everyonewaiter.domain.menu.Category; +import com.everyonewaiter.domain.menu.CategoryCreateRequest; +import com.everyonewaiter.domain.menu.Menu; +import com.everyonewaiter.domain.menu.MenuCreateRequest; +import com.everyonewaiter.domain.menu.MenuLabel; +import com.everyonewaiter.domain.menu.MenuOptionGroupModifyRequest; +import com.everyonewaiter.domain.menu.MenuOptionGroupType; +import com.everyonewaiter.domain.menu.MenuOptionModifyRequest; +import com.everyonewaiter.domain.menu.MenuState; +import com.everyonewaiter.domain.shared.Email; +import com.everyonewaiter.domain.store.Registration; +import com.everyonewaiter.domain.store.RegistrationApplyRequest; +import com.everyonewaiter.domain.store.Store; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.NonNull; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Profile("!prod") +@Component +@RequiredArgsConstructor +class DataInitializer implements ApplicationRunner { + + private static final String ADMIN_EMAIL = "admin@everyonewaiter.com"; + private static final String ADMIN_PASSWORD = "@password1"; + private static final String ADMIN_PHONE = "01044591812"; + + private final DataInitializerTransactionSupporter tx; + private final PasswordEncoder passwordEncoder; + private final JPAQueryFactory queryFactory; + private final HealthCheckCreator healthCheckCreator; + private final ApkVersionRepository apkVersionRepository; + private final AccountRepository accountRepository; + private final RegistrationRepository registrationRepository; + private final StoreRepository storeRepository; + private final CategoryRepository categoryRepository; + private final MenuRepository menuRepository; + private final DeviceRepository deviceRepository; + + @Override + public void run(@NonNull ApplicationArguments args) throws Exception { + createApkVersion(); + + boolean existsDefaultAdmin = accountRepository.exists(new Email(ADMIN_EMAIL)); + if (existsDefaultAdmin) { + return; + } + + // Account + var adminAccount = createAdminAccount(); + authorizeAdminAccount(adminAccount); + + // Store + createStoreRegistration(adminAccount); + Store store = storeRepository.findAll(adminAccount.getId()).getFirst(); + + // Menu + List categories = createCategories(store); + createMenus(categories); + + // Device + List devices = createDevices(store); + setSimpleSecretKey(devices); + } + + private void createApkVersion() { + if (apkVersionRepository.findLatest().isEmpty()) { + healthCheckCreator.createApkVersion(); + } + } + + private Account createAdminAccount() { + var request = new AccountCreateRequest(ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_PHONE); + var adminAccount = Account.create(request, passwordEncoder); + adminAccount.activate(); + return accountRepository.save(adminAccount); + } + + private void authorizeAdminAccount(Account adminAccount) { + tx.executeInTransaction(() -> queryFactory + .update(account) + .set(account.permission, AccountPermission.ADMIN) + .where(account.id.eq(adminAccount.getId())) + .execute() + ); + } + + private void createStoreRegistration(Account account) { + var request = new RegistrationApplyRequest( + "모두의 웨이터", "손정웅", "경상남도 창원시 의창구 도계로 135", "0551234567", "443-60-00875", null + ); + var registration = Registration.apply(account, request, "test/license.webp"); + registration.approve(); + registrationRepository.save(registration); + } + + private List createCategories(Store store) { + var categories = new ArrayList(); + var categoryNames = new String[]{"Steak", "Pasta", "Rice", "Pizza", "Salad", "Drink", "Etc"}; + for (int i = 0; i < categoryNames.length; i++) { + var category = Category.create(store, new CategoryCreateRequest(categoryNames[i]), i); + categories.add(category); + categoryRepository.save(category); + } + return Collections.unmodifiableList(categories); + } + + private Category findCategoryByName(List categories, String name) { + for (Category category : categories) { + if (category.getName().equalsIgnoreCase(name)) { + return category; + } + } + throw new IllegalArgumentException("카테고리 " + name + " 을(를) 찾을 수 없습니다."); + } + + private void createMenus(List categories) { + var menus = new ArrayList(); + var position = 0; + + var steak = findCategoryByName(categories, "Steak"); + var steak1 = new MenuCreateRequest( + "수비드 소고기 스테이크", + "굽기: 미디움 / 변경 불가능", + 35000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("레드 와인 1잔", 6000) + ) + ) + ) + ); + menus.add(Menu.create(steak, steak1, "test/steak.webp", position)); + position++; + + var pasta = findCategoryByName(categories, "Pasta"); + var pasta1 = new MenuCreateRequest( + "라구 볼로네제 파스타", + "", + 19500, + 1, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta1, "test/pasta1.webp", position)); + position++; + + var pasta2 = new MenuCreateRequest( + "새우품은 빠네 크림 파스타", + "", + 19800, + 0, + MenuState.DEFAULT, + MenuLabel.RECOMMEND, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta2, "test/pasta2.webp", position)); + position++; + + var pasta3 = new MenuCreateRequest( + "새우품은 빠네 로제 파스타", + "", + 19900, + 1, + MenuState.DEFAULT, + MenuLabel.RECOMMEND, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta3, "test/pasta3.webp", position)); + position++; + + var pasta4 = new MenuCreateRequest( + "통통새우 크림 파스타", + "", + 18500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pasta, pasta4, "test/pasta4.webp", position)); + position++; + + var pasta5 = new MenuCreateRequest( + "통통새우 로제 파스타", + "", + 18900, + 1, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta5, "test/pasta5.webp", position)); + position++; + + var pasta6 = new MenuCreateRequest( + "까르보나라", + "", + 14900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta6, "test/pasta6.webp", position)); + position++; + + var pasta7 = new MenuCreateRequest( + "매운 크림 파스타", + "", + 18900, + 2, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("더 맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta7, "test/pasta7.webp", position)); + position++; + + var pasta8 = new MenuCreateRequest( + "씨푸드 토마토 파스타", + "", + 19900, + 1, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta8, "test/pasta8.webp", position)); + position++; + + var pasta9 = new MenuCreateRequest( + "빠쉐", + "", + 19900, + 2, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta9, "test/pasta9.webp", position)); + position++; + + var pasta10 = new MenuCreateRequest( + "통통새우 알리오올리오", + "", + 17500, + 1, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("안맵게", 0), + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(pasta, pasta10, "test/pasta10.webp", position)); + position++; + + var rice = findCategoryByName(categories, "Rice"); + var rice1 = new MenuCreateRequest( + "씨푸드 크림 리조또", + "", + 18900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of( + new MenuOptionGroupModifyRequest( + "맵기", + MenuOptionGroupType.OPTIONAL, + true, + List.of( + new MenuOptionModifyRequest("맵게", 0) + ) + ) + ) + ); + menus.add(Menu.create(rice, rice1, "test/rice1.webp", position)); + position++; + + var rice2 = new MenuCreateRequest( + "참숯 불고기 필라프(돈육)", + "", + 16900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(rice, rice2, "test/rice2.webp", position)); + position++; + + var pizza = findCategoryByName(categories, "Pizza"); + var pizza1 = new MenuCreateRequest( + "고르곤졸라 피자", + "", + 12900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza1, "test/pizza1.webp", position)); + position++; + + var pizza2 = new MenuCreateRequest( + "알알이 옥수수 피자", + "", + 15500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza2, "test/pizza2.webp", position)); + position++; + + var pizza3 = new MenuCreateRequest( + "아몬드 고구마 피자", + "", + 15500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza3, "test/pizza3.webp", position)); + position++; + + var pizza4 = new MenuCreateRequest( + "샐러드 피자", + "", + 25000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza4, "test/pizza4.webp", position)); + position++; + + var pizza5 = new MenuCreateRequest( + "불고기 피자", + "", + 26000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza5, "test/pizza5.webp", position)); + position++; + + var pizza6 = new MenuCreateRequest( + "루꼴라 불고기 피자", + "토핑에 레드페퍼 포함", + 26000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(pizza, pizza6, "test/pizza6.webp", position)); + position++; + + var salad = findCategoryByName(categories, "Salad"); + var salad1 = new MenuCreateRequest( + "클래식 샐러드", + "", + 16900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(salad, salad1, "test/salad1.webp", position)); + position++; + + var salad2 = new MenuCreateRequest( + "하우스 샐러드", + "", + 14900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(salad, salad2, "test/salad2.webp", position)); + position++; + + var salad3 = new MenuCreateRequest( + "연어 샐러드", + "", + 17900, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(salad, salad3, "test/salad3.webp", position)); + position++; + + var drink = findCategoryByName(categories, "Drink"); + var drink1 = new MenuCreateRequest( + "아메리카노(HOT)", + "식사 메뉴(스테이크, 파스타, 라이스) 주문 시 1메뉴 1잔 3000원", + 4500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "할인", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("식사 메뉴 주문 시", -1500) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink1, "test/drink1.webp", position)); + position++; + + var drink2 = new MenuCreateRequest( + "아메리카노(ICE)", + "식사 메뉴(스테이크, 파스타, 라이스) 주문 시 1메뉴 1잔 3000원", + 4500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "할인", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("식사 메뉴 주문 시", -1500) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink2, "test/drink2.webp", position)); + position++; + + var drink3 = new MenuCreateRequest( + "레몬에이드(ICE)", + "", + 5000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink3, "test/drink3.webp", position)); + position++; + + var drink4 = new MenuCreateRequest( + "자몽에이드(ICE)", + "", + 5000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink4, "test/drink4.webp", position)); + position++; + + var drink5 = new MenuCreateRequest( + "아이스티(복숭아)", + "", + 3500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink5, "test/drink5.webp", position)); + position++; + + var drink6 = new MenuCreateRequest( + "콜라", + "", + 2500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("얼음컵", 0) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink6, "test/drink6.webp", position)); + position++; + + var drink7 = new MenuCreateRequest( + "제로 콜라", + "", + 2500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("얼음컵", 0) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink7, "test/drink7.webp", position)); + position++; + + var drink8 = new MenuCreateRequest( + "사이다", + "", + 2500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("얼음컵", 0) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink8, "test/drink8.webp", position)); + position++; + + var drink9 = new MenuCreateRequest( + "환타(파인애플)", + "", + 2500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("얼음컵", 0) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink9, "test/drink9.webp", position)); + position++; + + var drink10 = new MenuCreateRequest( + "수제 레몬차", + "", + 5500, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of( + new MenuOptionGroupModifyRequest( + "선택 옵션", + MenuOptionGroupType.OPTIONAL, + false, + List.of( + new MenuOptionModifyRequest("ICE", 500) + ) + ) + ) + ); + menus.add(Menu.create(drink, drink10, "test/drink10.webp", position)); + position++; + + var drink11 = new MenuCreateRequest( + "레드 와인 1잔", + "", + 8000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink11, "test/drink11.webp", position)); + position++; + + var drink12 = new MenuCreateRequest( + "레드 와인 1병", + "", + 40000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink12, "test/drink12.webp", position)); + position++; + + var drink13 = new MenuCreateRequest( + "일품진로", + "", + 25000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink13, "test/drink13.webp", position)); + position++; + + var drink14 = new MenuCreateRequest( + "켈리", + "", + 5000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink14, "test/drink14.webp", position)); + position++; + + var drink15 = new MenuCreateRequest( + "카스", + "", + 5000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + false, + List.of() + ); + menus.add(Menu.create(drink, drink15, "test/drink15.webp", position)); + position++; + + var etc = findCategoryByName(categories, "Etc"); + var etc1 = new MenuCreateRequest( + "공기밥", + "", + 1000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(etc, etc1, "test/etc1.webp", position)); + position++; + + var etc2 = new MenuCreateRequest( + "빵 추가", + "", + 3000, + 0, + MenuState.DEFAULT, + MenuLabel.DEFAULT, + true, + List.of() + ); + menus.add(Menu.create(etc, etc2, "test/etc2.webp", position)); + + for (Menu menu : menus) { + menuRepository.save(menu); + } + } + + private List createDevices(Store store) { + var devices = new ArrayList(); + DevicePurpose[] purposes = DevicePurpose.values(); + for (DevicePurpose purpose : purposes) { + var tableNo = purpose == DevicePurpose.TABLE ? 100 : 0; + var tableName = purpose == DevicePurpose.TABLE + ? purpose.name() + "-" + tableNo + : purpose.name(); + var request = new DeviceCreateRequest( + ADMIN_PHONE, + "TEST-" + tableName, + purpose, + tableNo, + DevicePaymentType.POSTPAID + ); + var device = Device.create(store, request); + devices.add(deviceRepository.save(device)); + deviceRepository.save(device); + } + return Collections.unmodifiableList(devices); + } + + private void setSimpleSecretKey(List devices) { + for (Device d : devices) { + tx.executeInTransaction(() -> queryFactory + .update(device) + .set(device.secretKey, d.getName()) + .where(device.id.eq(d.getId())) + .execute()); + } + } + +} diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializerTransactionSupporter.java b/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializerTransactionSupporter.java new file mode 100644 index 00000000..c985e04d --- /dev/null +++ b/src/main/java/com/everyonewaiter/adapter/persistence/DataInitializerTransactionSupporter.java @@ -0,0 +1,14 @@ +package com.everyonewaiter.adapter.persistence; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +class DataInitializerTransactionSupporter { + + @Transactional + public void executeInTransaction(Runnable runnable) { + runnable.run(); + } + +} diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenJpaRepository.java b/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenJpaRepository.java deleted file mode 100644 index 5b43f23e..00000000 --- a/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenJpaRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.everyonewaiter.adapter.persistence.auth; - -import com.everyonewaiter.domain.auth.RefreshToken; -import org.springframework.data.jpa.repository.JpaRepository; - -interface RefreshTokenJpaRepository extends JpaRepository { - -} diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenRepositoryImpl.java b/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenRepositoryImpl.java deleted file mode 100644 index f357e3e3..00000000 --- a/src/main/java/com/everyonewaiter/adapter/persistence/auth/RefreshTokenRepositoryImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.everyonewaiter.adapter.persistence.auth; - -import com.everyonewaiter.application.auth.required.RefreshTokenRepository; -import com.everyonewaiter.domain.auth.RefreshToken; -import com.everyonewaiter.domain.shared.AuthenticationException; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -class RefreshTokenRepositoryImpl implements RefreshTokenRepository { - - private final RefreshTokenJpaRepository refreshTokenJpaRepository; - - @Override - public RefreshToken findByIdOrThrow(Long refreshTokenId) { - return refreshTokenJpaRepository.findById(refreshTokenId) - .orElseThrow(AuthenticationException::new); - } - - @Override - public RefreshToken save(RefreshToken refreshToken) { - return refreshTokenJpaRepository.save(refreshToken); - } - - @Override - public void delete(RefreshToken refreshToken) { - refreshTokenJpaRepository.delete(refreshToken); - } - -} diff --git a/src/main/java/com/everyonewaiter/adapter/persistence/health/ApkVersionRepositoryImpl.java b/src/main/java/com/everyonewaiter/adapter/persistence/health/ApkVersionRepositoryImpl.java index be8c1042..83e8a1d0 100644 --- a/src/main/java/com/everyonewaiter/adapter/persistence/health/ApkVersionRepositoryImpl.java +++ b/src/main/java/com/everyonewaiter/adapter/persistence/health/ApkVersionRepositoryImpl.java @@ -18,15 +18,19 @@ class ApkVersionRepositoryImpl implements ApkVersionRepository { private final ApkVersionJpaRepository apkVersionJpaRepository; @Override - public ApkVersion findLatest() { + public Optional findLatest() { return Optional.ofNullable( - queryFactory - .select(apkVersion) - .from(apkVersion) - .orderBy(apkVersion.id.desc()) - .fetchFirst() - ) - .orElseThrow(ApkVersionNotFoundException::new); + queryFactory + .select(apkVersion) + .from(apkVersion) + .orderBy(apkVersion.id.desc()) + .fetchFirst() + ); + } + + @Override + public ApkVersion findLatestOrThrow() { + return findLatest().orElseThrow(ApkVersionNotFoundException::new); } @Override diff --git a/src/main/java/com/everyonewaiter/adapter/security/JwtProviderImpl.java b/src/main/java/com/everyonewaiter/adapter/security/JwtProviderImpl.java index 024e0ac9..155f2335 100644 --- a/src/main/java/com/everyonewaiter/adapter/security/JwtProviderImpl.java +++ b/src/main/java/com/everyonewaiter/adapter/security/JwtProviderImpl.java @@ -37,7 +37,7 @@ public String encode(JwtPayload payload, Duration expiration) { Date now = new Date(); return Jwts.builder() - .id(payload.id().toString()) + .id(payload.id()) .subject(payload.subject()) .issuedAt(now) .expiration(new Date(now.getTime() + expiration.toMillis())) @@ -55,7 +55,7 @@ public Optional decode(String token) { .parseSignedClaims(token) .getPayload(); - return Optional.of(new JwtPayload(Long.parseLong(payload.getId()), payload.getSubject())); + return Optional.of(new JwtPayload(payload.getId(), payload.getSubject())); } catch (Exception exception) { return Optional.empty(); } diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/WebApiExceptionLogger.java b/src/main/java/com/everyonewaiter/adapter/web/api/WebApiExceptionLogger.java index 1c0eecf5..0d780d93 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/WebApiExceptionLogger.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/WebApiExceptionLogger.java @@ -1,6 +1,6 @@ package com.everyonewaiter.adapter.web.api; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseXRequestId; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseXRequestId; import static lombok.AccessLevel.PRIVATE; import static org.slf4j.LoggerFactory.getLogger; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/admin/AccountAdminApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/admin/AccountAdminApi.java index b79bd4db..b5f00bd3 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/admin/AccountAdminApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/admin/AccountAdminApi.java @@ -2,13 +2,13 @@ import com.everyonewaiter.adapter.web.api.dto.AccountAdminPageResponse; import com.everyonewaiter.adapter.web.api.dto.AccountDetailResponse; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.account.provided.AccountFinder; import com.everyonewaiter.application.account.provided.AccountUpdater; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountAdminPageRequest; import com.everyonewaiter.domain.account.AccountAdminUpdateRequest; import com.everyonewaiter.domain.account.AccountPermission; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.shared.Paging; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApi.java index 431f20a8..4003496f 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApi.java @@ -1,9 +1,9 @@ package com.everyonewaiter.adapter.web.api.admin; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.health.provided.HealthCheckCreator; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountPermission; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.health.ApkVersion; import com.everyonewaiter.domain.health.ApkVersionCreateRequest; import jakarta.validation.Valid; @@ -22,6 +22,16 @@ class HealthCheckAdminApi implements HealthCheckAdminApiSpecification { private final HealthCheckCreator healthCheckCreator; + @Override + @PostMapping("/auto") + public ResponseEntity createApkVersion( + @AuthenticationAccount(permission = AccountPermission.ADMIN) Account account + ) { + ApkVersion apkVersion = healthCheckCreator.createApkVersion(); + + return ResponseEntity.created(URI.create(String.valueOf(apkVersion.getId()))).build(); + } + @Override @PostMapping public ResponseEntity createApkVersion( diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApiSpecification.java b/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApiSpecification.java index 36ca1a52..94b333a0 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApiSpecification.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/admin/HealthCheckAdminApiSpecification.java @@ -15,6 +15,25 @@ @Tag(name = "헬스 체크") interface HealthCheckAdminApiSpecification { + @Operation(summary = "APK 버전 생성", description = "APK 버전 생성 API") + @ApiResponse(responseCode = "201", description = "APK 버전 생성 성공") + @ApiErrorResponses( + summary = "APK 버전 생성 실패", + value = { + @ApiErrorResponse( + code = ErrorCode.UNAUTHORIZED, + exampleName = "액세스 토큰이 유효하지 않은 경우" + ), + @ApiErrorResponse( + code = ErrorCode.FORBIDDEN, + exampleName = "관리자 권한이 없는 경우" + ), + } + ) + ResponseEntity createApkVersion( + @Parameter(hidden = true) Account account + ); + @Operation(summary = "APK 버전 생성", description = "APK 버전 생성 API") @ApiResponse(responseCode = "201", description = "APK 버전 생성 성공") @ApiErrorResponses( diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/admin/RegistrationAdminApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/admin/RegistrationAdminApi.java index 0f0027f6..affd7f78 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/admin/RegistrationAdminApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/admin/RegistrationAdminApi.java @@ -2,11 +2,11 @@ import com.everyonewaiter.adapter.web.api.dto.RegistrationAdminDetailResponse; import com.everyonewaiter.adapter.web.api.dto.RegistrationAdminPageResponse; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.store.provided.RegistrationAdministrator; import com.everyonewaiter.application.store.provided.RegistrationFinder; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountPermission; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.shared.Paging; import com.everyonewaiter.domain.store.Registration; import com.everyonewaiter.domain.store.RegistrationAdminPageRequest; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/DeviceApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/DeviceApi.java index 2f973f9f..8c01caf5 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/DeviceApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/DeviceApi.java @@ -1,7 +1,7 @@ package com.everyonewaiter.adapter.web.api.device; import com.everyonewaiter.adapter.web.api.dto.DeviceDetailResponse; -import com.everyonewaiter.domain.auth.AuthenticationDevice; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderApi.java index 4b07981f..3b9d0fe3 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderApi.java @@ -2,9 +2,9 @@ import com.everyonewaiter.adapter.web.api.dto.OrderDetailResponses; import com.everyonewaiter.adapter.web.api.dto.OrderHallResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.order.provided.OrderFinder; import com.everyonewaiter.application.order.provided.OrderServer; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import com.everyonewaiter.domain.order.Order; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderPaymentApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderPaymentApi.java index d560ad43..37df7be2 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderPaymentApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/OrderPaymentApi.java @@ -1,9 +1,9 @@ package com.everyonewaiter.adapter.web.api.device; import com.everyonewaiter.adapter.web.api.dto.OrderPaymentDetailResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.order.provided.OrderPaymentCreator; import com.everyonewaiter.application.order.provided.OrderPaymentFinder; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import com.everyonewaiter.domain.order.OrderPayment; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/PosApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/PosApi.java index 7dd197b6..e66fddc5 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/PosApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/PosApi.java @@ -1,11 +1,11 @@ package com.everyonewaiter.adapter.web.api.device; import com.everyonewaiter.adapter.web.api.dto.PosTableDetailResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.pos.provided.PosTableActivityFinder; import com.everyonewaiter.application.pos.provided.PosTableFinder; import com.everyonewaiter.application.pos.provided.PosTableManager; import com.everyonewaiter.application.pos.provided.PosTableOrderManager; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import com.everyonewaiter.domain.order.OrderMemoUpdateRequest; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/SseApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/SseApi.java index 84302458..20ebdf68 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/SseApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/SseApi.java @@ -5,8 +5,8 @@ import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.sse.provided.SseConnector; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/StaffCallApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/StaffCallApi.java index 4d71ae0b..04e01b0f 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/StaffCallApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/StaffCallApi.java @@ -1,8 +1,8 @@ package com.everyonewaiter.adapter.web.api.device; import com.everyonewaiter.adapter.web.api.dto.StaffCallDetailResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.staffcall.provided.StaffCallManager; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import com.everyonewaiter.domain.staffcall.StaffCall; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/StoreApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/StoreApi.java index b989ff5c..a1e0b683 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/StoreApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/StoreApi.java @@ -1,7 +1,7 @@ package com.everyonewaiter.adapter.web.api.device; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.store.provided.StoreManager; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/device/WaitingApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/device/WaitingApi.java index 288a6fb7..80645fd2 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/device/WaitingApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/device/WaitingApi.java @@ -2,9 +2,9 @@ import com.everyonewaiter.adapter.web.api.dto.WaitingCountResponse; import com.everyonewaiter.adapter.web.api.dto.WaitingDetailResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationDevice; import com.everyonewaiter.application.waiting.provided.WaitingAdministrator; import com.everyonewaiter.application.waiting.provided.WaitingCustomer; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DevicePurpose; import com.everyonewaiter.domain.store.StoreOpen; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApi.java index 4b69f72f..08843b34 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApi.java @@ -1,20 +1,22 @@ package com.everyonewaiter.adapter.web.api.owner; import com.everyonewaiter.adapter.web.api.dto.AccountProfileResponse; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.account.provided.AccountFinder; import com.everyonewaiter.application.account.provided.AccountRegister; import com.everyonewaiter.application.account.provided.AccountSignInHandler; +import com.everyonewaiter.application.account.provided.AccountUpdater; import com.everyonewaiter.application.auth.provided.Authenticator; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountCreateRequest; +import com.everyonewaiter.domain.account.AccountPasswordChangeRequest; import com.everyonewaiter.domain.account.AccountSignInRequest; +import com.everyonewaiter.domain.account.SignInToken; import com.everyonewaiter.domain.auth.AuthPurpose; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.auth.SendAuthCodeRequest; import com.everyonewaiter.domain.auth.SendAuthMailRequest; -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; import com.everyonewaiter.domain.auth.VerifyAuthCodeRequest; +import com.everyonewaiter.domain.shared.Email; import com.everyonewaiter.domain.shared.PhoneNumber; import jakarta.validation.Valid; import java.net.URI; @@ -23,6 +25,7 @@ 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -37,6 +40,7 @@ class AccountApi implements AccountApiSpecification { private final AccountFinder accountFinder; private final AccountRegister accountRegister; private final AccountSignInHandler accountSignInHandler; + private final AccountUpdater accountUpdater; @Override @GetMapping("/me") @@ -57,6 +61,11 @@ public ResponseEntity getProfile(@PathVariable String ph @Override @PostMapping public ResponseEntity signUp(@RequestBody @Valid AccountCreateRequest createRequest) { + authenticator.checkAuthSuccess( + AuthPurpose.SIGN_UP, + new PhoneNumber(createRequest.phoneNumber()) + ); + Account account = accountRegister.register(createRequest); return ResponseEntity.created(URI.create(String.valueOf(account.getId()))).build(); @@ -105,17 +114,22 @@ public ResponseEntity sendAuthMail( @Override @PostMapping("/verify-auth-mail") public ResponseEntity verifyEmail(@RequestParam String token) { - accountRegister.activate(token); + Email email = authenticator.verifyAuthMail(token); + + accountRegister.activate(email); return ResponseEntity.noContent().build(); } @Override - @PostMapping("/renew-token") - public ResponseEntity renewToken( - @RequestBody @Valid SignInTokenRenewRequest signInTokenRenewRequest + @PutMapping("/change-password") + public ResponseEntity changePassword( + @RequestBody @Valid AccountPasswordChangeRequest changeRequest, + @AuthenticationAccount Account account ) { - return ResponseEntity.ok(accountSignInHandler.renew(signInTokenRenewRequest)); + accountUpdater.changePassword(account.getId(), changeRequest); + + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApiSpecification.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApiSpecification.java index eb1278f0..2312b317 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApiSpecification.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/AccountApiSpecification.java @@ -5,11 +5,11 @@ import com.everyonewaiter.adapter.web.docs.ApiErrorResponses; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountCreateRequest; +import com.everyonewaiter.domain.account.AccountPasswordChangeRequest; import com.everyonewaiter.domain.account.AccountSignInRequest; +import com.everyonewaiter.domain.account.SignInToken; import com.everyonewaiter.domain.auth.SendAuthCodeRequest; import com.everyonewaiter.domain.auth.SendAuthMailRequest; -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; import com.everyonewaiter.domain.auth.VerifyAuthCodeRequest; import com.everyonewaiter.domain.shared.ErrorCode; import io.swagger.v3.oas.annotations.Operation; @@ -195,24 +195,24 @@ interface AccountApiSpecification { ) ResponseEntity verifyEmail(String token); - @SecurityRequirements - @Operation(summary = "토큰 갱신", description = "토큰 갱신 API") - @ApiResponse(responseCode = "200", description = "토큰 갱신 성공") + @Operation(summary = "비밀번호 변경", description = "비밀번호 변경 API") + @ApiResponse(responseCode = "204", description = "비밀번호 변경 성공") @ApiErrorResponses( - summary = "토큰 갱신 실패", + summary = "비밀번호 변경 실패", value = { @ApiErrorResponse( code = ErrorCode.UNAUTHORIZED, - exampleName = "리프레시 토큰이 유효하지 않은 경우" + exampleName = "액세스 토큰이 유효하지 않은 경우" ), @ApiErrorResponse( - code = ErrorCode.FORBIDDEN, - exampleName = "토큰이 탈취된것으로 의심되는 경우" + code = ErrorCode.MISMATCHED_CURRENT_PASSWORD, + exampleName = "현재 비밀번호가 일치하지 않는 경우" ), } ) - ResponseEntity renewToken( - @RequestBody SignInTokenRenewRequest signInTokenRenewRequest + ResponseEntity changePassword( + @RequestBody AccountPasswordChangeRequest changeRequest, + @Parameter(hidden = true) Account account ); } diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/CategoryManagementApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/CategoryManagementApi.java index 3f9fa268..c3227849 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/CategoryManagementApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/CategoryManagementApi.java @@ -1,11 +1,11 @@ package com.everyonewaiter.adapter.web.api.owner; import com.everyonewaiter.adapter.web.api.dto.CategorySimpleResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.menu.provided.CategoryFinder; import com.everyonewaiter.application.menu.provided.CategoryManager; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountPermission; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.menu.Category; import com.everyonewaiter.domain.menu.CategoryCreateRequest; import com.everyonewaiter.domain.menu.CategoryMovePositionRequest; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/DeviceManagementApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/DeviceManagementApi.java index e3c43e99..d1001061 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/DeviceManagementApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/DeviceManagementApi.java @@ -4,6 +4,7 @@ import com.everyonewaiter.adapter.web.api.dto.DeviceDetailResponse; import com.everyonewaiter.adapter.web.api.dto.DevicePageResponse; import com.everyonewaiter.adapter.web.api.dto.StoreSimpleResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.auth.provided.Authenticator; import com.everyonewaiter.application.device.provided.DeviceFinder; import com.everyonewaiter.application.device.provided.DeviceManager; @@ -11,7 +12,6 @@ import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountPermission; import com.everyonewaiter.domain.auth.AuthPurpose; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.auth.SendAuthCodeRequest; import com.everyonewaiter.domain.auth.VerifyAuthCodeRequest; import com.everyonewaiter.domain.device.Device; @@ -79,6 +79,11 @@ public ResponseEntity create( @PathVariable Long storeId, @RequestBody @Valid DeviceCreateRequest createRequest ) { + authenticator.checkAuthSuccess( + AuthPurpose.CREATE_DEVICE, + new PhoneNumber(createRequest.phoneNumber()) + ); + Device device = deviceManager.create(storeId, createRequest); return ResponseEntity.created(URI.create(String.valueOf(device.getId()))) diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/MenuManagementApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/MenuManagementApi.java index 71f52611..72755655 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/MenuManagementApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/MenuManagementApi.java @@ -1,11 +1,11 @@ package com.everyonewaiter.adapter.web.api.owner; import com.everyonewaiter.adapter.web.api.dto.MenuSimpleResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.menu.provided.MenuFinder; import com.everyonewaiter.application.menu.provided.MenuManager; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountPermission; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.menu.Menu; import com.everyonewaiter.domain.menu.MenuCreateRequest; import com.everyonewaiter.domain.menu.MenuDeleteRequest; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/RegistrationApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/RegistrationApi.java index 3c3a0f30..cd048475 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/RegistrationApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/RegistrationApi.java @@ -1,10 +1,10 @@ package com.everyonewaiter.adapter.web.api.owner; import com.everyonewaiter.adapter.web.api.dto.RegistrationDetailResponse; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.store.provided.RegistrationApplier; import com.everyonewaiter.application.store.provided.RegistrationFinder; import com.everyonewaiter.domain.account.Account; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.shared.Paging; import com.everyonewaiter.domain.store.Registration; import com.everyonewaiter.domain.store.RegistrationApplyRequest; diff --git a/src/main/java/com/everyonewaiter/adapter/web/api/owner/StoreManagementApi.java b/src/main/java/com/everyonewaiter/adapter/web/api/owner/StoreManagementApi.java index 66b6d891..2a203173 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/api/owner/StoreManagementApi.java +++ b/src/main/java/com/everyonewaiter/adapter/web/api/owner/StoreManagementApi.java @@ -2,10 +2,10 @@ import com.everyonewaiter.adapter.web.api.dto.StoreDetailResponse; import com.everyonewaiter.adapter.web.api.dto.StoreSimpleResponses; +import com.everyonewaiter.adapter.web.auth.AuthenticationAccount; import com.everyonewaiter.application.store.provided.StoreFinder; import com.everyonewaiter.application.store.provided.StoreManager; import com.everyonewaiter.domain.account.Account; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.store.Store; import com.everyonewaiter.domain.store.StoreUpdateRequest; import jakarta.validation.Valid; diff --git a/src/main/java/com/everyonewaiter/domain/auth/AuthenticationAccount.java b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccount.java similarity index 89% rename from src/main/java/com/everyonewaiter/domain/auth/AuthenticationAccount.java rename to src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccount.java index 0152471a..0147f764 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/AuthenticationAccount.java +++ b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccount.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.domain.auth; +package com.everyonewaiter.adapter.web.auth; import com.everyonewaiter.domain.account.AccountPermission; import java.lang.annotation.ElementType; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationAccountResolver.java b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccountResolver.java similarity index 77% rename from src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationAccountResolver.java rename to src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccountResolver.java index bc778efd..c83fd911 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationAccountResolver.java +++ b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationAccountResolver.java @@ -1,11 +1,10 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.web.auth; import static java.util.Objects.requireNonNull; import com.everyonewaiter.application.account.provided.AccountFinder; import com.everyonewaiter.application.auth.required.JwtProvider; import com.everyonewaiter.domain.account.Account; -import com.everyonewaiter.domain.auth.AuthenticationAccount; import com.everyonewaiter.domain.auth.JwtPayload; import com.everyonewaiter.domain.shared.AccessDeniedException; import com.everyonewaiter.domain.shared.AuthenticationException; @@ -22,7 +21,7 @@ @Component @RequiredArgsConstructor -class AuthenticationAccountResolver implements HandlerMethodArgumentResolver { +public class AuthenticationAccountResolver implements HandlerMethodArgumentResolver { private static final String BEARER_PREFIX = "Bearer "; @@ -47,19 +46,22 @@ public Account resolveArgument( String accessToken = extractToken(webRequest); JwtPayload payload = jwtProvider.decode(accessToken).orElseThrow(AuthenticationException::new); - return accountFinder.find(payload.id()) - .map(account -> { - AuthenticationAccount annotation = requireNonNull( - parameter.getParameterAnnotation(AuthenticationAccount.class) - ); + try { + Account account = accountFinder.find(payload.getLongId()) + .orElseThrow(AuthenticationException::new); - if (!account.isActive() || !account.hasPermission(annotation.permission())) { - throw new AccessDeniedException(); - } + AuthenticationAccount annotation = requireNonNull( + parameter.getParameterAnnotation(AuthenticationAccount.class) + ); - return account; - }) - .orElseThrow(AuthenticationException::new); + if (!account.isActive() || !account.hasPermission(annotation.permission())) { + throw new AccessDeniedException(); + } + + return account; + } catch (NumberFormatException exception) { + throw new AuthenticationException(); + } } private String extractToken(NativeWebRequest request) { diff --git a/src/main/java/com/everyonewaiter/domain/auth/AuthenticationDevice.java b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDevice.java similarity index 91% rename from src/main/java/com/everyonewaiter/domain/auth/AuthenticationDevice.java rename to src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDevice.java index c6b8b174..00af5294 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/AuthenticationDevice.java +++ b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDevice.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.domain.auth; +package com.everyonewaiter.adapter.web.auth; import com.everyonewaiter.domain.device.DevicePurpose; import java.lang.annotation.ElementType; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationDeviceResolver.java b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDeviceResolver.java similarity index 94% rename from src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationDeviceResolver.java rename to src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDeviceResolver.java index 27787215..fcc6f287 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/AuthenticationDeviceResolver.java +++ b/src/main/java/com/everyonewaiter/adapter/web/auth/AuthenticationDeviceResolver.java @@ -1,13 +1,12 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.web.auth; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseRequestUri; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseRequestUri; import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; import static org.slf4j.LoggerFactory.getLogger; import com.everyonewaiter.application.auth.required.SignatureEncoder; import com.everyonewaiter.application.device.provided.DeviceFinder; -import com.everyonewaiter.domain.auth.AuthenticationDevice; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.shared.AccessDeniedException; import com.everyonewaiter.domain.shared.AuthenticationException; @@ -28,7 +27,7 @@ @Component @RequiredArgsConstructor -class AuthenticationDeviceResolver implements HandlerMethodArgumentResolver { +public class AuthenticationDeviceResolver implements HandlerMethodArgumentResolver { private static final Logger LOGGER = getLogger(AuthenticationDeviceResolver.class); diff --git a/src/main/java/com/everyonewaiter/adapter/web/HttpRequestParser.java b/src/main/java/com/everyonewaiter/adapter/web/utils/HttpRequestParser.java similarity index 97% rename from src/main/java/com/everyonewaiter/adapter/web/HttpRequestParser.java rename to src/main/java/com/everyonewaiter/adapter/web/utils/HttpRequestParser.java index 88c72c53..89ba9e19 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/HttpRequestParser.java +++ b/src/main/java/com/everyonewaiter/adapter/web/utils/HttpRequestParser.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.web; +package com.everyonewaiter.adapter.web.utils; import static java.util.Objects.requireNonNullElse; import static lombok.AccessLevel.PRIVATE; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/MDCLoggingFilter.java b/src/main/java/com/everyonewaiter/adapter/web/utils/MDCLoggingFilter.java similarity index 76% rename from src/main/java/com/everyonewaiter/adapter/web/config/MDCLoggingFilter.java rename to src/main/java/com/everyonewaiter/adapter/web/utils/MDCLoggingFilter.java index bbda5953..698c0046 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/MDCLoggingFilter.java +++ b/src/main/java/com/everyonewaiter/adapter/web/utils/MDCLoggingFilter.java @@ -1,9 +1,9 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.web.utils; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseHeaders; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseParameters; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseRequestUri; -import static com.everyonewaiter.adapter.web.HttpRequestParser.parseXRequestId; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseHeaders; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseParameters; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseRequestUri; +import static com.everyonewaiter.adapter.web.utils.HttpRequestParser.parseXRequestId; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; diff --git a/src/main/java/com/everyonewaiter/adapter/web/config/MultipartHttpMessageConverter.java b/src/main/java/com/everyonewaiter/adapter/web/utils/MultipartHttpMessageConverter.java similarity index 95% rename from src/main/java/com/everyonewaiter/adapter/web/config/MultipartHttpMessageConverter.java rename to src/main/java/com/everyonewaiter/adapter/web/utils/MultipartHttpMessageConverter.java index e01b37be..dfdf9980 100644 --- a/src/main/java/com/everyonewaiter/adapter/web/config/MultipartHttpMessageConverter.java +++ b/src/main/java/com/everyonewaiter/adapter/web/utils/MultipartHttpMessageConverter.java @@ -1,4 +1,4 @@ -package com.everyonewaiter.adapter.web.config; +package com.everyonewaiter.adapter.web.utils; import lombok.NonNull; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/com/everyonewaiter/application/account/AccountModifyService.java b/src/main/java/com/everyonewaiter/application/account/AccountModifyService.java index 243bad9a..8e5b3456 100644 --- a/src/main/java/com/everyonewaiter/application/account/AccountModifyService.java +++ b/src/main/java/com/everyonewaiter/application/account/AccountModifyService.java @@ -5,13 +5,12 @@ import com.everyonewaiter.application.account.provided.AccountUpdater; import com.everyonewaiter.application.account.provided.AccountValidator; import com.everyonewaiter.application.account.required.AccountRepository; -import com.everyonewaiter.application.auth.provided.Authenticator; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountAdminUpdateRequest; import com.everyonewaiter.domain.account.AccountCreateRequest; +import com.everyonewaiter.domain.account.AccountPasswordChangeRequest; import com.everyonewaiter.domain.account.AccountPermission; import com.everyonewaiter.domain.account.PasswordEncoder; -import com.everyonewaiter.domain.auth.AuthPurpose; import com.everyonewaiter.domain.shared.Email; import com.everyonewaiter.domain.shared.PhoneNumber; import lombok.RequiredArgsConstructor; @@ -25,7 +24,6 @@ @RequiredArgsConstructor class AccountModifyService implements AccountRegister, AccountUpdater { - private final Authenticator authenticator; private final AccountFinder accountFinder; private final AccountValidator accountValidator; private final AccountRepository accountRepository; @@ -44,15 +42,12 @@ private void validateAccountCreate(AccountCreateRequest createRequest) { Email email = new Email(createRequest.email()); PhoneNumber phoneNumber = new PhoneNumber(createRequest.phoneNumber()); - authenticator.checkAuthSuccess(AuthPurpose.SIGN_UP, phoneNumber); accountValidator.checkDuplicateEmail(email); accountValidator.checkDuplicatePhone(phoneNumber); } @Override - public Account activate(String authMailToken) { - Email email = authenticator.verifyAuthMail(authMailToken); - + public Account activate(Email email) { Account account = accountFinder.findOrThrow(email); account.activate(); @@ -69,6 +64,15 @@ public Account authorize(Long accountId, AccountPermission permission) { return accountRepository.save(account); } + @Override + public Account changePassword(Long accountId, AccountPasswordChangeRequest request) { + Account account = accountFinder.findOrThrow(accountId); + + account.changePassword(request, passwordEncoder); + + return accountRepository.save(account); + } + @Override public Account updateByAdmin( Account adminAccount, diff --git a/src/main/java/com/everyonewaiter/application/account/AccountSignInService.java b/src/main/java/com/everyonewaiter/application/account/AccountSignInService.java index c4abe8fb..158009f2 100644 --- a/src/main/java/com/everyonewaiter/application/account/AccountSignInService.java +++ b/src/main/java/com/everyonewaiter/application/account/AccountSignInService.java @@ -3,15 +3,15 @@ import com.everyonewaiter.application.account.provided.AccountFinder; import com.everyonewaiter.application.account.provided.AccountSignInHandler; import com.everyonewaiter.application.account.required.AccountRepository; -import com.everyonewaiter.application.auth.provided.SignInTokenProvider; +import com.everyonewaiter.application.auth.required.JwtProvider; import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountSignInRequest; import com.everyonewaiter.domain.account.FailedSignInException; import com.everyonewaiter.domain.account.PasswordEncoder; -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; -import com.everyonewaiter.domain.shared.AccessDeniedException; +import com.everyonewaiter.domain.account.SignInToken; +import com.everyonewaiter.domain.auth.JwtPayload; import com.everyonewaiter.domain.shared.Email; +import java.time.Duration; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,7 +26,7 @@ class AccountSignInService implements AccountSignInHandler { private final AccountFinder accountFinder; private final AccountRepository accountRepository; private final PasswordEncoder passwordEncoder; - private final SignInTokenProvider signInTokenProvider; + private final JwtProvider jwtProvider; @Override public SignInToken signIn(AccountSignInRequest signInRequest) { @@ -38,13 +38,10 @@ public SignInToken signIn(AccountSignInRequest signInRequest) { accountRepository.save(account); - return signInTokenProvider.createToken(account.getId()); - } + JwtPayload payload = new JwtPayload(account.getId(), account.getEmail().address()); + String accessToken = jwtProvider.encode(payload, Duration.ofHours(3)); - @Override - public SignInToken renew(SignInTokenRenewRequest signInTokenRenewRequest) { - return signInTokenProvider.renewToken(signInTokenRenewRequest) - .orElseThrow(AccessDeniedException::new); + return new SignInToken(accessToken); } } diff --git a/src/main/java/com/everyonewaiter/application/account/provided/AccountRegister.java b/src/main/java/com/everyonewaiter/application/account/provided/AccountRegister.java index 25695702..f7fd0a1d 100644 --- a/src/main/java/com/everyonewaiter/application/account/provided/AccountRegister.java +++ b/src/main/java/com/everyonewaiter/application/account/provided/AccountRegister.java @@ -2,12 +2,13 @@ import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountCreateRequest; +import com.everyonewaiter.domain.shared.Email; import jakarta.validation.Valid; public interface AccountRegister { Account register(@Valid AccountCreateRequest createRequest); - Account activate(String authMailToken); + Account activate(Email email); } diff --git a/src/main/java/com/everyonewaiter/application/account/provided/AccountSignInHandler.java b/src/main/java/com/everyonewaiter/application/account/provided/AccountSignInHandler.java index edb1b554..0a093526 100644 --- a/src/main/java/com/everyonewaiter/application/account/provided/AccountSignInHandler.java +++ b/src/main/java/com/everyonewaiter/application/account/provided/AccountSignInHandler.java @@ -1,14 +1,11 @@ package com.everyonewaiter.application.account.provided; import com.everyonewaiter.domain.account.AccountSignInRequest; -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; +import com.everyonewaiter.domain.account.SignInToken; import jakarta.validation.Valid; public interface AccountSignInHandler { SignInToken signIn(@Valid AccountSignInRequest signInRequest); - SignInToken renew(@Valid SignInTokenRenewRequest signInTokenRenewRequest); - } diff --git a/src/main/java/com/everyonewaiter/application/account/provided/AccountUpdater.java b/src/main/java/com/everyonewaiter/application/account/provided/AccountUpdater.java index 2de66c2c..9dcb86c5 100644 --- a/src/main/java/com/everyonewaiter/application/account/provided/AccountUpdater.java +++ b/src/main/java/com/everyonewaiter/application/account/provided/AccountUpdater.java @@ -2,6 +2,7 @@ import com.everyonewaiter.domain.account.Account; import com.everyonewaiter.domain.account.AccountAdminUpdateRequest; +import com.everyonewaiter.domain.account.AccountPasswordChangeRequest; import com.everyonewaiter.domain.account.AccountPermission; import jakarta.validation.Valid; @@ -9,6 +10,8 @@ public interface AccountUpdater { Account authorize(Long accountId, AccountPermission permission); + Account changePassword(Long accountId, @Valid AccountPasswordChangeRequest request); + Account updateByAdmin( Account adminAccount, Long userAccountId, diff --git a/src/main/java/com/everyonewaiter/application/auth/AuthService.java b/src/main/java/com/everyonewaiter/application/auth/AuthService.java index 63dc6f6c..c592a300 100644 --- a/src/main/java/com/everyonewaiter/application/auth/AuthService.java +++ b/src/main/java/com/everyonewaiter/application/auth/AuthService.java @@ -107,7 +107,7 @@ public Email verifyAuthMail(String authMailToken) { JwtPayload payload = jwtProvider.decode(authMailToken) .orElseThrow(ExpiredVerificationEmailException::new); - if (!JwtFixedId.VERIFICATION_EMAIL_ID.equals(payload.id())) { + if (!JwtFixedId.VERIFICATION_EMAIL.equals(payload.id())) { throw new ExpiredVerificationEmailException(); } diff --git a/src/main/java/com/everyonewaiter/application/auth/SignInTokenService.java b/src/main/java/com/everyonewaiter/application/auth/SignInTokenService.java deleted file mode 100644 index 787e555b..00000000 --- a/src/main/java/com/everyonewaiter/application/auth/SignInTokenService.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.everyonewaiter.application.auth; - - -import static org.slf4j.LoggerFactory.getLogger; - -import com.everyonewaiter.application.auth.provided.SignInTokenProvider; -import com.everyonewaiter.application.auth.required.JwtProvider; -import com.everyonewaiter.application.auth.required.RefreshTokenRepository; -import com.everyonewaiter.application.support.DistributedLock; -import com.everyonewaiter.domain.auth.JwtPayload; -import com.everyonewaiter.domain.auth.RefreshToken; -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; -import com.everyonewaiter.domain.shared.AuthenticationException; -import java.time.Duration; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.slf4j.Logger; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -@Validated -@Service -@Transactional -@RequiredArgsConstructor -class SignInTokenService implements SignInTokenProvider { - - private static final Logger LOGGER = getLogger(SignInTokenService.class); - - private final JwtProvider jwtProvider; - private final RefreshTokenRepository refreshTokenRepository; - - @Override - public SignInToken createToken(Long accountId) { - RefreshToken refreshToken = RefreshToken.create(accountId); - - refreshTokenRepository.save(refreshToken); - - return createTokenResponse(refreshToken); - } - - @Override - @DistributedLock(key = "#signInTokenRenewRequest.refreshToken") - public Optional renewToken(SignInTokenRenewRequest signInTokenRenewRequest) { - JwtPayload payload = jwtProvider.decode(signInTokenRenewRequest.refreshToken()) - .orElseThrow(AuthenticationException::new); - - RefreshToken refreshToken = refreshTokenRepository.findByIdOrThrow(payload.id()); - - try { - refreshToken.renew(payload.parseLongSubject()); - - refreshTokenRepository.save(refreshToken); - - return Optional.of(createTokenResponse(refreshToken)); - } catch (AuthenticationException | NumberFormatException exception) { - LOGGER.warn("토큰 탈취가 의심되어 로그아웃을 진행합니다. accountId: {}", refreshToken.getAccountId()); - - refreshTokenRepository.delete(refreshToken); - - return Optional.empty(); - } - } - - private SignInToken createTokenResponse(RefreshToken refreshToken) { - JwtPayload acc = new JwtPayload(refreshToken.getAccountId(), refreshToken.getCurrentTokenId()); - JwtPayload ref = new JwtPayload(refreshToken.getId(), refreshToken.getCurrentTokenId()); - - String accessToken = jwtProvider.encode(acc, Duration.ofHours(12)); - String refToken = jwtProvider.encode(ref, Duration.ofDays(14)); - - return new SignInToken(accessToken, refToken); - } - -} diff --git a/src/main/java/com/everyonewaiter/application/auth/provided/SignInTokenProvider.java b/src/main/java/com/everyonewaiter/application/auth/provided/SignInTokenProvider.java deleted file mode 100644 index fca0721d..00000000 --- a/src/main/java/com/everyonewaiter/application/auth/provided/SignInTokenProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.everyonewaiter.application.auth.provided; - -import com.everyonewaiter.domain.auth.SignInToken; -import com.everyonewaiter.domain.auth.SignInTokenRenewRequest; -import jakarta.validation.Valid; -import java.util.Optional; - -public interface SignInTokenProvider { - - SignInToken createToken(Long accountId); - - Optional renewToken(@Valid SignInTokenRenewRequest signInTokenRenewRequest); - -} diff --git a/src/main/java/com/everyonewaiter/application/auth/required/RefreshTokenRepository.java b/src/main/java/com/everyonewaiter/application/auth/required/RefreshTokenRepository.java deleted file mode 100644 index f7cc5b98..00000000 --- a/src/main/java/com/everyonewaiter/application/auth/required/RefreshTokenRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.everyonewaiter.application.auth.required; - -import com.everyonewaiter.domain.auth.RefreshToken; - -public interface RefreshTokenRepository { - - RefreshToken findByIdOrThrow(Long refreshTokenId); - - RefreshToken save(RefreshToken refreshToken); - - void delete(RefreshToken refreshToken); - -} diff --git a/src/main/java/com/everyonewaiter/application/device/DeviceModifyService.java b/src/main/java/com/everyonewaiter/application/device/DeviceModifyService.java index be1fd4d6..3933574b 100644 --- a/src/main/java/com/everyonewaiter/application/device/DeviceModifyService.java +++ b/src/main/java/com/everyonewaiter/application/device/DeviceModifyService.java @@ -1,12 +1,10 @@ package com.everyonewaiter.application.device; -import com.everyonewaiter.application.auth.provided.Authenticator; import com.everyonewaiter.application.device.provided.DeviceFinder; import com.everyonewaiter.application.device.provided.DeviceManager; +import com.everyonewaiter.application.device.provided.DeviceValidator; import com.everyonewaiter.application.device.required.DeviceRepository; import com.everyonewaiter.application.store.provided.StoreFinder; -import com.everyonewaiter.domain.auth.AuthPurpose; -import com.everyonewaiter.domain.device.AlreadyUseDeviceNameException; import com.everyonewaiter.domain.device.Device; import com.everyonewaiter.domain.device.DeviceCreateRequest; import com.everyonewaiter.domain.device.DeviceUpdateRequest; @@ -23,63 +21,33 @@ @RequiredArgsConstructor class DeviceModifyService implements DeviceManager { - private final Authenticator authenticator; private final StoreFinder storeFinder; private final DeviceFinder deviceFinder; + private final DeviceValidator deviceValidator; private final DeviceRepository deviceRepository; @Override public Device create(Long storeId, DeviceCreateRequest createRequest) { - validateDeviceCreate(storeId, createRequest); + deviceValidator.checkDuplicateName(storeId, createRequest.name()); Store store = storeFinder.findOrThrow(storeId, new PhoneNumber(createRequest.phoneNumber())); - Device device = switch (createRequest.purpose()) { - case POS -> Device.createPos(store, createRequest); - case HALL -> Device.createHall(store, createRequest); - case TABLE -> Device.createTable(store, createRequest); - case WAITING -> Device.createWaiting(store, createRequest); - }; + Device device = Device.create(store, createRequest); return deviceRepository.save(device); } - private void validateDeviceCreate(Long storeId, DeviceCreateRequest createRequest) { - PhoneNumber phoneNumber = new PhoneNumber(createRequest.phoneNumber()); - - authenticator.checkAuthSuccess(AuthPurpose.CREATE_DEVICE, phoneNumber); - - if (deviceRepository.exists(storeId, createRequest.name())) { - throw new AlreadyUseDeviceNameException(); - } - } - @Override public Device update(Long deviceId, Long storeId, DeviceUpdateRequest updateRequest) { - validateDeviceUpdate(deviceId, storeId, updateRequest); + deviceValidator.checkDuplicateNameExcludeId(deviceId, storeId, updateRequest.name()); Device device = deviceFinder.findOrThrow(deviceId, storeId); - switch (updateRequest.purpose()) { - case POS -> device.updatePos(updateRequest); - case HALL -> device.updateHall(updateRequest); - case TABLE -> device.updateTable(updateRequest); - case WAITING -> device.updateWaiting(updateRequest); - } + device.update(updateRequest); return deviceRepository.save(device); } - private void validateDeviceUpdate( - Long deviceId, - Long storeId, - DeviceUpdateRequest updateRequest - ) { - if (deviceRepository.existsExcludeId(deviceId, storeId, updateRequest.name())) { - throw new AlreadyUseDeviceNameException(); - } - } - @Override public void delete(Long deviceId, Long storeId) { Device device = deviceFinder.findOrThrow(deviceId, storeId); diff --git a/src/main/java/com/everyonewaiter/application/device/DeviceValidateService.java b/src/main/java/com/everyonewaiter/application/device/DeviceValidateService.java new file mode 100644 index 00000000..0cb2e400 --- /dev/null +++ b/src/main/java/com/everyonewaiter/application/device/DeviceValidateService.java @@ -0,0 +1,31 @@ +package com.everyonewaiter.application.device; + +import com.everyonewaiter.application.device.provided.DeviceValidator; +import com.everyonewaiter.application.device.required.DeviceRepository; +import com.everyonewaiter.application.support.ReadOnlyTransactional; +import com.everyonewaiter.domain.device.AlreadyUseDeviceNameException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@ReadOnlyTransactional +@RequiredArgsConstructor +class DeviceValidateService implements DeviceValidator { + + private final DeviceRepository deviceRepository; + + @Override + public void checkDuplicateName(Long storeId, String name) { + if (deviceRepository.exists(storeId, name)) { + throw new AlreadyUseDeviceNameException(); + } + } + + @Override + public void checkDuplicateNameExcludeId(Long deviceId, Long storeId, String name) { + if (deviceRepository.existsExcludeId(deviceId, storeId, name)) { + throw new AlreadyUseDeviceNameException(); + } + } + +} diff --git a/src/main/java/com/everyonewaiter/application/device/provided/DeviceValidator.java b/src/main/java/com/everyonewaiter/application/device/provided/DeviceValidator.java new file mode 100644 index 00000000..67add644 --- /dev/null +++ b/src/main/java/com/everyonewaiter/application/device/provided/DeviceValidator.java @@ -0,0 +1,9 @@ +package com.everyonewaiter.application.device.provided; + +public interface DeviceValidator { + + void checkDuplicateName(Long storeId, String name); + + void checkDuplicateNameExcludeId(Long deviceId, Long storeId, String name); + +} diff --git a/src/main/java/com/everyonewaiter/application/health/HealthCheckModifyService.java b/src/main/java/com/everyonewaiter/application/health/HealthCheckModifyService.java index c73fa72d..92459033 100644 --- a/src/main/java/com/everyonewaiter/application/health/HealthCheckModifyService.java +++ b/src/main/java/com/everyonewaiter/application/health/HealthCheckModifyService.java @@ -1,13 +1,21 @@ package com.everyonewaiter.application.health; +import static com.everyonewaiter.domain.shared.ErrorCode.FAILED_EXTERNAL_SERVER_COMMUNICATION; + import com.everyonewaiter.application.health.provided.HealthCheckCreator; import com.everyonewaiter.application.health.required.ApkVersionRepository; import com.everyonewaiter.domain.health.ApkVersion; import com.everyonewaiter.domain.health.ApkVersionCreateRequest; +import com.everyonewaiter.domain.shared.BusinessException; +import com.everyonewaiter.domain.shared.GithubRelease; import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.RestClientResponseException; @Validated @Service @@ -15,8 +23,39 @@ @RequiredArgsConstructor class HealthCheckModifyService implements HealthCheckCreator { + private static final String STORE_APP_REPO = "https://api.github.com/repos/everyonewaiter/everyonewaiter-store-app"; + private static final RestClient STORE_APP_REPO_CLIENT = RestClient.create(STORE_APP_REPO); + private final ApkVersionRepository apkVersionRepository; + @Override + public ApkVersion createApkVersion() { + try { + GithubRelease latestRelease = STORE_APP_REPO_CLIENT.get() + .uri("/releases/latest") + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .body(GithubRelease.class); + + Assert.notNull(latestRelease, "매장 앱 릴리즈 정보는 null일 수 없습니다."); + Assert.notEmpty(latestRelease.assets(), "매장 앱 APK 에셋을 찾을 수 없습니다."); + + String[] versionTokens = latestRelease.tag_name().split("\\."); + ApkVersionCreateRequest createRequest = new ApkVersionCreateRequest( + Integer.parseInt(versionTokens[0].replace("v", "")), + Integer.parseInt(versionTokens[1]), + Integer.parseInt(versionTokens[2]), + latestRelease.assets().getFirst().browser_download_url() + ); + + ApkVersion apkVersion = ApkVersion.create(createRequest); + + return apkVersionRepository.save(apkVersion); + } catch (RestClientResponseException exception) { + throw new BusinessException(FAILED_EXTERNAL_SERVER_COMMUNICATION); + } + } + @Override public ApkVersion createApkVersion(ApkVersionCreateRequest createRequest) { ApkVersion apkVersion = ApkVersion.create(createRequest); diff --git a/src/main/java/com/everyonewaiter/application/health/HealthCheckQueryService.java b/src/main/java/com/everyonewaiter/application/health/HealthCheckQueryService.java index 3eadab62..1109d485 100644 --- a/src/main/java/com/everyonewaiter/application/health/HealthCheckQueryService.java +++ b/src/main/java/com/everyonewaiter/application/health/HealthCheckQueryService.java @@ -24,7 +24,7 @@ public ServerInfo findServerInfo() { @Override public ApkVersion findLatestApkVersion() { - return apkVersionRepository.findLatest(); + return apkVersionRepository.findLatestOrThrow(); } } diff --git a/src/main/java/com/everyonewaiter/application/health/provided/HealthCheckCreator.java b/src/main/java/com/everyonewaiter/application/health/provided/HealthCheckCreator.java index 72db4e24..50dbf3be 100644 --- a/src/main/java/com/everyonewaiter/application/health/provided/HealthCheckCreator.java +++ b/src/main/java/com/everyonewaiter/application/health/provided/HealthCheckCreator.java @@ -6,6 +6,8 @@ public interface HealthCheckCreator { + ApkVersion createApkVersion(); + ApkVersion createApkVersion(@Valid ApkVersionCreateRequest createRequest); } diff --git a/src/main/java/com/everyonewaiter/application/health/required/ApkVersionRepository.java b/src/main/java/com/everyonewaiter/application/health/required/ApkVersionRepository.java index 14a82fc6..b4f7cde5 100644 --- a/src/main/java/com/everyonewaiter/application/health/required/ApkVersionRepository.java +++ b/src/main/java/com/everyonewaiter/application/health/required/ApkVersionRepository.java @@ -1,10 +1,13 @@ package com.everyonewaiter.application.health.required; import com.everyonewaiter.domain.health.ApkVersion; +import java.util.Optional; public interface ApkVersionRepository { - ApkVersion findLatest(); + Optional findLatest(); + + ApkVersion findLatestOrThrow(); ApkVersion save(ApkVersion apkVersion); diff --git a/src/main/java/com/everyonewaiter/application/notification/EmailNotificationEventHandler.java b/src/main/java/com/everyonewaiter/application/notification/EmailNotificationEventHandler.java index 8f7d63e8..2f842c6e 100644 --- a/src/main/java/com/everyonewaiter/application/notification/EmailNotificationEventHandler.java +++ b/src/main/java/com/everyonewaiter/application/notification/EmailNotificationEventHandler.java @@ -53,7 +53,7 @@ public void handle(AuthMailSendEvent event) { private void sendAuthMail(Email email) { LOGGER.info("[이메일 인증 메일 전송 이벤트] email: {}", email.address()); - JwtPayload payload = new JwtPayload(JwtFixedId.VERIFICATION_EMAIL_ID, email.address()); + JwtPayload payload = new JwtPayload(JwtFixedId.VERIFICATION_EMAIL, email.address()); String authToken = jwtProvider.encode(payload, Duration.ofDays(1)); String authUri = BASE_URL + AUTH_EMAIL.formatted(email.address(), authToken); diff --git a/src/main/java/com/everyonewaiter/application/pos/PosTableOrderManagementService.java b/src/main/java/com/everyonewaiter/application/pos/PosTableOrderManagementService.java index 364b95c8..d57aaa04 100644 --- a/src/main/java/com/everyonewaiter/application/pos/PosTableOrderManagementService.java +++ b/src/main/java/com/everyonewaiter/application/pos/PosTableOrderManagementService.java @@ -3,12 +3,10 @@ import com.everyonewaiter.application.pos.provided.PosTableFinder; import com.everyonewaiter.application.pos.provided.PosTableOrderManager; import com.everyonewaiter.application.pos.required.PosTableRepository; -import com.everyonewaiter.application.receipt.provided.ReceiptCreator; import com.everyonewaiter.application.support.DistributedLock; import com.everyonewaiter.domain.order.OrderMemoUpdateRequest; import com.everyonewaiter.domain.order.OrderUpdateRequests; import com.everyonewaiter.domain.pos.PosTable; -import com.everyonewaiter.domain.receipt.Receipt; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,7 +18,6 @@ @RequiredArgsConstructor class PosTableOrderManagementService implements PosTableOrderManager { - private final ReceiptCreator receiptCreator; private final PosTableFinder posTableFinder; private final PosTableRepository posTableRepository; @@ -39,14 +36,7 @@ public PosTable cancel(Long storeId, int tableNo, Long orderId) { public PosTable update(Long storeId, int tableNo, OrderUpdateRequests updateRequests) { PosTable posTable = posTableFinder.findActiveOrThrow(storeId, tableNo); - Receipt diff = receiptCreator.createDiff( - storeId, - tableNo, - posTable.getOrderedOrders(), - updateRequests - ); - - posTable.updateOrder(updateRequests, diff); + posTable.updateOrder(updateRequests); return posTableRepository.save(posTable); } diff --git a/src/main/java/com/everyonewaiter/application/receipt/ReceiptCreateService.java b/src/main/java/com/everyonewaiter/application/receipt/ReceiptCreateService.java index 5bc93e4f..2a55c7c0 100644 --- a/src/main/java/com/everyonewaiter/application/receipt/ReceiptCreateService.java +++ b/src/main/java/com/everyonewaiter/application/receipt/ReceiptCreateService.java @@ -49,6 +49,12 @@ public Receipt create(Long storeId, int tableNo, List orderIds) { List orders, OrderUpdateRequests updateRequests ) { + if (!Receipt.hasDiff(orders, updateRequests)) { + return null; + } + + receiptRepository.increment(storeId); + return Receipt.diff(tableNo, orders, updateRequests, receiptRepository.get(storeId)); } diff --git a/src/main/java/com/everyonewaiter/application/receipt/ReceiptSendEventHandler.java b/src/main/java/com/everyonewaiter/application/receipt/ReceiptSendEventHandler.java index 4145f231..dd0fb533 100644 --- a/src/main/java/com/everyonewaiter/application/receipt/ReceiptSendEventHandler.java +++ b/src/main/java/com/everyonewaiter/application/receipt/ReceiptSendEventHandler.java @@ -47,7 +47,16 @@ public void handle(OrderCreateEvent event) { public void handle(OrderUpdateEvent event) { LOGGER.info("[주문 수정 빌지 전송 이벤트] 매장 ID: {}, 테이블 번호: {}", event.storeId(), event.tableNo()); - publishSseEvent(event.storeId(), event.receipt()); + Receipt diff = receiptCreator.createDiff( + event.storeId(), + event.tableNo(), + event.orders(), + event.updateRequests() + ); + + if (diff != null) { + publishSseEvent(event.storeId(), diff); + } } @Async("eventTaskExecutor") diff --git a/src/main/java/com/everyonewaiter/domain/account/Account.java b/src/main/java/com/everyonewaiter/domain/account/Account.java index a81eb6e4..e70fdcf6 100644 --- a/src/main/java/com/everyonewaiter/domain/account/Account.java +++ b/src/main/java/com/everyonewaiter/domain/account/Account.java @@ -118,9 +118,20 @@ public void signIn(AccountSignInRequest signInRequest, PasswordEncoder passwordE if (isInactive() && isMatched) { throw new NotCompleteEmailVerificationException(); - } else { - throw new FailedSignInException(); } + + throw new FailedSignInException(); + } + + public void changePassword( + AccountPasswordChangeRequest changeRequest, + PasswordEncoder passwordEncoder + ) { + boolean isMatched = passwordEncoder.matches(changeRequest.currentPassword(), password); + if (!isMatched) { + throw new MismatchedCurrentPasswordException(); + } + this.password = requireNonNull(passwordEncoder.encode(changeRequest.newPassword())); } public void update(Account userAccount, AccountAdminUpdateRequest updateRequest) { diff --git a/src/main/java/com/everyonewaiter/domain/account/AccountPasswordChangeRequest.java b/src/main/java/com/everyonewaiter/domain/account/AccountPasswordChangeRequest.java new file mode 100644 index 00000000..512f1f1c --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/account/AccountPasswordChangeRequest.java @@ -0,0 +1,37 @@ +package com.everyonewaiter.domain.account; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import org.jspecify.annotations.NonNull; + +@Schema(name = "AccountSignInRequest") +public record AccountPasswordChangeRequest( + @Schema(description = "비밀번호: 영문, 숫자, 특수문자 조합 8자리 이상", example = "@password1", requiredMode = REQUIRED) + @NotBlank(message = "현재 비밀번호를 확인해 주세요.") + @Pattern( + regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@#$%^&*()\\-+=`~])[\\w!@#$%^&*()\\-+=`~]{8,}$", + message = "현재 비밀번호를 확인해 주세요." + ) + String currentPassword, + + @Schema(description = "비밀번호: 영문, 숫자, 특수문자 조합 8자리 이상", example = "@password1", requiredMode = REQUIRED) + @NotBlank(message = "새 비밀번호를 확인해 주세요.") + @Pattern( + regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@#$%^&*()\\-+=`~])[\\w!@#$%^&*()\\-+=`~]{8,}$", + message = "새 비밀번호를 확인해 주세요." + ) + String newPassword +) { + + @Override + public @NonNull String toString() { + return "AccountPasswordChangeRequest(" + + "currentPassword='BLIND'" + + ", newPassword='BLIND'" + + ')'; + } + +} diff --git a/src/main/java/com/everyonewaiter/domain/account/MismatchedCurrentPasswordException.java b/src/main/java/com/everyonewaiter/domain/account/MismatchedCurrentPasswordException.java new file mode 100644 index 00000000..1e8f29ae --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/account/MismatchedCurrentPasswordException.java @@ -0,0 +1,12 @@ +package com.everyonewaiter.domain.account; + +import com.everyonewaiter.domain.shared.BusinessException; +import com.everyonewaiter.domain.shared.ErrorCode; + +public class MismatchedCurrentPasswordException extends BusinessException { + + public MismatchedCurrentPasswordException() { + super(ErrorCode.MISMATCHED_CURRENT_PASSWORD); + } + +} diff --git a/src/main/java/com/everyonewaiter/domain/account/SignInToken.java b/src/main/java/com/everyonewaiter/domain/account/SignInToken.java new file mode 100644 index 00000000..5c6bb282 --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/account/SignInToken.java @@ -0,0 +1,11 @@ +package com.everyonewaiter.domain.account; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "SignInToken") +public record SignInToken( + @Schema(description = "액세스 토큰 (3시간)", example = "abcdefghijklmnopqrstuvwxyz") + String accessToken +) { + +} diff --git a/src/main/java/com/everyonewaiter/domain/auth/Auth.java b/src/main/java/com/everyonewaiter/domain/auth/Auth.java index f59ba5e5..19d38175 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/Auth.java +++ b/src/main/java/com/everyonewaiter/domain/auth/Auth.java @@ -4,10 +4,12 @@ public interface Auth { - String key(); + Duration expiration(); - int value(); + String key(); - Duration expiration(); + default int value() { + return -2; + } } diff --git a/src/main/java/com/everyonewaiter/domain/auth/AuthAttempt.java b/src/main/java/com/everyonewaiter/domain/auth/AuthAttempt.java index ee2033ab..a6ebc77d 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/AuthAttempt.java +++ b/src/main/java/com/everyonewaiter/domain/auth/AuthAttempt.java @@ -24,9 +24,4 @@ public String key() { return KEY_PREFIX + purpose.getLowerCaseName() + ":" + phoneNumber.value(); } - @Override - public int value() { - return -2; - } - } diff --git a/src/main/java/com/everyonewaiter/domain/auth/AuthSuccess.java b/src/main/java/com/everyonewaiter/domain/auth/AuthSuccess.java index 4fe295c6..ab7258f9 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/AuthSuccess.java +++ b/src/main/java/com/everyonewaiter/domain/auth/AuthSuccess.java @@ -20,9 +20,4 @@ public String key() { return KEY_PREFIX + phoneNumber.value(); } - @Override - public int value() { - return -2; - } - } diff --git a/src/main/java/com/everyonewaiter/domain/auth/JwtFixedId.java b/src/main/java/com/everyonewaiter/domain/auth/JwtFixedId.java index 0b8d271b..bc973087 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/JwtFixedId.java +++ b/src/main/java/com/everyonewaiter/domain/auth/JwtFixedId.java @@ -7,6 +7,6 @@ @NoArgsConstructor(access = PRIVATE) public final class JwtFixedId { - public static final Long VERIFICATION_EMAIL_ID = 1000L; + public static final String VERIFICATION_EMAIL = "VERIFICATION_EMAIL"; } diff --git a/src/main/java/com/everyonewaiter/domain/auth/JwtPayload.java b/src/main/java/com/everyonewaiter/domain/auth/JwtPayload.java index 0f2d21b7..3a47f9ec 100644 --- a/src/main/java/com/everyonewaiter/domain/auth/JwtPayload.java +++ b/src/main/java/com/everyonewaiter/domain/auth/JwtPayload.java @@ -2,14 +2,14 @@ import static java.util.Objects.requireNonNull; -public record JwtPayload(Long id, String subject) { +public record JwtPayload(String id, String subject) { - public JwtPayload(Long id, Long subject) { - this(id, requireNonNull(subject).toString()); + public JwtPayload(Long id, String subject) { + this(requireNonNull(id).toString(), subject); } - public Long parseLongSubject() throws NumberFormatException { - return Long.parseLong(subject()); + public Long getLongId() throws NumberFormatException { + return Long.parseLong(id); } } diff --git a/src/main/java/com/everyonewaiter/domain/auth/RefreshToken.java b/src/main/java/com/everyonewaiter/domain/auth/RefreshToken.java deleted file mode 100644 index 7a8204fd..00000000 --- a/src/main/java/com/everyonewaiter/domain/auth/RefreshToken.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static java.util.Objects.requireNonNull; -import static lombok.AccessLevel.PROTECTED; - -import com.everyonewaiter.domain.AggregateRootEntity; -import com.everyonewaiter.domain.shared.AuthenticationException; -import com.everyonewaiter.domain.support.Tsid; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Entity -@Table(name = "refresh_token") -@Getter -@ToString(callSuper = true) -@NoArgsConstructor(access = PROTECTED) -public class RefreshToken extends AggregateRootEntity { - - @Column(name = "account_id", nullable = false, updatable = false) - private Long accountId; - - @Column(name = "current_token_id", nullable = false) - private Long currentTokenId; - - public static RefreshToken create(Long accountId) { - RefreshToken refreshToken = new RefreshToken(); - - refreshToken.accountId = requireNonNull(accountId); - refreshToken.currentTokenId = refreshToken.getId(); - - return refreshToken; - } - - public void renew(Long currentTokenId) { - if (this.currentTokenId.equals(currentTokenId)) { - this.currentTokenId = Tsid.nextLong(); - } else { - throw new AuthenticationException(); - } - } - -} diff --git a/src/main/java/com/everyonewaiter/domain/auth/SignInToken.java b/src/main/java/com/everyonewaiter/domain/auth/SignInToken.java deleted file mode 100644 index e9fdf164..00000000 --- a/src/main/java/com/everyonewaiter/domain/auth/SignInToken.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import io.swagger.v3.oas.annotations.media.Schema; - -@Schema(name = "SignInToken") -public record SignInToken( - @Schema(description = "액세스 토큰 (12시간)", example = "abcdefghijklmnopqrstuvwxyz") - String accessToken, - - @Schema(description = "리프레시 토큰 (2주)", example = "abcdefghijklmnopqrstuvwxyz") - String refreshToken -) { - -} diff --git a/src/main/java/com/everyonewaiter/domain/auth/SignInTokenRenewRequest.java b/src/main/java/com/everyonewaiter/domain/auth/SignInTokenRenewRequest.java deleted file mode 100644 index f92152f1..00000000 --- a/src/main/java/com/everyonewaiter/domain/auth/SignInTokenRenewRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.NonNull; - -@Schema(name = "SignInTokenRenewRequest") -public record SignInTokenRenewRequest( - @Schema(description = "리프레시 토큰", example = "abcdefghijklmnopqrstuvwxyz", requiredMode = REQUIRED) - @NotBlank(message = "리프레시 토큰을 입력해 주세요.") - String refreshToken -) { - - @Override - public @NonNull String toString() { - return "SignInTokenRenewRequest(" + - "refreshToken='BLIND'" + - ')'; - } - -} diff --git a/src/main/java/com/everyonewaiter/domain/device/Device.java b/src/main/java/com/everyonewaiter/domain/device/Device.java index b003957b..aa4ed4d8 100644 --- a/src/main/java/com/everyonewaiter/domain/device/Device.java +++ b/src/main/java/com/everyonewaiter/domain/device/Device.java @@ -72,22 +72,13 @@ public class Device extends AggregateRootEntity { @Column(name = "secret_key", nullable = false, updatable = false, length = 30) private String secretKey; - public static Device createPos(Store store, DeviceCreateRequest request) { - return create(store, request.name(), POS); - } - - public static Device createHall(Store store, DeviceCreateRequest request) { - return create(store, request.name(), HALL); - } - - public static Device createTable(Store store, DeviceCreateRequest request) { - validateTableNo(request.tableNo()); - - return create(store, request.name(), TABLE, request.tableNo(), request.paymentType()); - } - - public static Device createWaiting(Store store, DeviceCreateRequest request) { - return create(store, request.name(), WAITING); + public static Device create(Store store, DeviceCreateRequest request) { + return switch (request.purpose()) { + case POS -> createPos(store, request); + case HALL -> createHall(store, request); + case TABLE -> createTable(store, request); + case WAITING -> createWaiting(store, request); + }; } private static Device create(Store store, String name, DevicePurpose purpose) { @@ -116,6 +107,24 @@ private static Device create( return device; } + private static Device createPos(Store store, DeviceCreateRequest request) { + return create(store, request.name(), POS); + } + + private static Device createHall(Store store, DeviceCreateRequest request) { + return create(store, request.name(), HALL); + } + + private static Device createTable(Store store, DeviceCreateRequest request) { + validateTableNo(request.tableNo()); + + return create(store, request.name(), TABLE, request.tableNo(), request.paymentType()); + } + + private static Device createWaiting(Store store, DeviceCreateRequest request) { + return create(store, request.name(), WAITING); + } + private static void validateTableNo(int tableNo) { isTrue(tableNo >= 1 && tableNo <= 100, "테이블 번호는 1이상 100이하이어야 합니다."); } @@ -132,22 +141,13 @@ public boolean hasPurpose(DevicePurpose purpose) { return this.purpose == purpose; } - public void updatePos(DeviceUpdateRequest request) { - update(request.name(), POS); - } - - public void updateHall(DeviceUpdateRequest request) { - update(request.name(), HALL); - } - - public void updateTable(DeviceUpdateRequest request) { - validateTableNo(request.tableNo()); - - update(request.name(), TABLE, request.tableNo(), request.paymentType()); - } - - public void updateWaiting(DeviceUpdateRequest request) { - update(request.name(), WAITING); + public void update(DeviceUpdateRequest request) { + switch (request.purpose()) { + case POS -> this.updatePos(request); + case HALL -> this.updateHall(request); + case TABLE -> this.updateTable(request); + case WAITING -> this.updateWaiting(request); + } } private void update(String name, DevicePurpose purpose) { @@ -168,6 +168,24 @@ private void update( registerEvent(new SseEvent(getStoreId(), DEVICE, UPDATE, getStringId())); } + private void updatePos(DeviceUpdateRequest request) { + update(request.name(), POS); + } + + private void updateHall(DeviceUpdateRequest request) { + update(request.name(), HALL); + } + + private void updateTable(DeviceUpdateRequest request) { + validateTableNo(request.tableNo()); + + update(request.name(), TABLE, request.tableNo(), request.paymentType()); + } + + private void updateWaiting(DeviceUpdateRequest request) { + update(request.name(), WAITING); + } + public void delete() { registerEvent(new SseEvent(getStoreId(), DEVICE, DELETE, getStringId())); } diff --git a/src/main/java/com/everyonewaiter/domain/notification/AlimTalkTemplate.java b/src/main/java/com/everyonewaiter/domain/notification/AlimTalkTemplate.java index 8a4843a5..0910f330 100644 --- a/src/main/java/com/everyonewaiter/domain/notification/AlimTalkTemplate.java +++ b/src/main/java/com/everyonewaiter/domain/notification/AlimTalkTemplate.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +@Getter @RequiredArgsConstructor public enum AlimTalkTemplate { @@ -67,7 +68,6 @@ public enum AlimTalkTemplate { ), ; - @Getter private final String templateCode; private final String templateContent; diff --git a/src/main/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplate.java b/src/main/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplate.java index 5837001d..6f37e8a8 100644 --- a/src/main/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplate.java +++ b/src/main/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplate.java @@ -5,8 +5,10 @@ import static org.springframework.util.Assert.isTrue; import com.everyonewaiter.domain.support.ClientUri; +import lombok.Getter; import lombok.RequiredArgsConstructor; +@Getter @RequiredArgsConstructor public enum AlimTalkWeblinkButtonTemplate implements AlimTalkButtonTemplate { @@ -20,13 +22,9 @@ public enum AlimTalkWeblinkButtonTemplate implements AlimTalkButtonTemplate { @Override public AlimTalkButton createButton(Object... arguments) { - return new AlimTalkWeblinkButton(name, getUri(arguments)); - } - - private String getUri(Object... arguments) { isTrue(arguments.length == count("%s", uri), "알림톡 버튼 템플릿 링크 설정이 옳바르지 않습니다."); - return BASE_URL + uri.formatted(arguments); + return new AlimTalkWeblinkButton(name, BASE_URL + uri.formatted(arguments)); } } diff --git a/src/main/java/com/everyonewaiter/domain/notification/DiscordEmbeds.java b/src/main/java/com/everyonewaiter/domain/notification/DiscordEmbeds.java index 38088419..45a39f6f 100644 --- a/src/main/java/com/everyonewaiter/domain/notification/DiscordEmbeds.java +++ b/src/main/java/com/everyonewaiter/domain/notification/DiscordEmbeds.java @@ -17,6 +17,7 @@ public DiscordEmbeds(DiscordEmbed... embeds) { } @Override + @SuppressWarnings("unused") public List embeds() { return Collections.unmodifiableList(embeds); } diff --git a/src/main/java/com/everyonewaiter/domain/notification/EmailTemplate.java b/src/main/java/com/everyonewaiter/domain/notification/EmailTemplate.java index d4a2c4b3..c626783b 100644 --- a/src/main/java/com/everyonewaiter/domain/notification/EmailTemplate.java +++ b/src/main/java/com/everyonewaiter/domain/notification/EmailTemplate.java @@ -3,8 +3,10 @@ import static org.springframework.util.Assert.isTrue; import java.util.Map; +import lombok.Getter; import lombok.RequiredArgsConstructor; +@Getter @RequiredArgsConstructor public enum EmailTemplate { diff --git a/src/main/java/com/everyonewaiter/domain/order/OrderPaymentApproveRequest.java b/src/main/java/com/everyonewaiter/domain/order/OrderPaymentApproveRequest.java index 98f4e6ad..1f4e3310 100644 --- a/src/main/java/com/everyonewaiter/domain/order/OrderPaymentApproveRequest.java +++ b/src/main/java/com/everyonewaiter/domain/order/OrderPaymentApproveRequest.java @@ -3,10 +3,12 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import com.everyonewaiter.domain.support.DateFormatter; +import com.everyonewaiter.domain.support.TimeZone; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; @Schema(name = "OrderPaymentApproveRequest") public record OrderPaymentApproveRequest( @@ -116,7 +118,10 @@ public String merchantNo() { @Override public String tradeTime() { - return isPureCash() ? DateFormatter.getSimpleKstDate() : tradeTime; + return isPureCash() + ? LocalDateTime.now(TimeZone.ASIA_SEOUL.zoneId()) + .format(DateFormatter.SIMPLE_YEAR_MONTH_DAY) + : tradeTime; } @Override diff --git a/src/main/java/com/everyonewaiter/domain/order/OrderPaymentCancelRequest.java b/src/main/java/com/everyonewaiter/domain/order/OrderPaymentCancelRequest.java index 8408c910..54d85f49 100644 --- a/src/main/java/com/everyonewaiter/domain/order/OrderPaymentCancelRequest.java +++ b/src/main/java/com/everyonewaiter/domain/order/OrderPaymentCancelRequest.java @@ -3,9 +3,11 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import com.everyonewaiter.domain.support.DateFormatter; +import com.everyonewaiter.domain.support.TimeZone; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; @Schema(name = "OrderPaymentCancelRequest") public record OrderPaymentCancelRequest( @@ -28,7 +30,10 @@ public String approvalNo(boolean isPureCash) { } public String tradeTime(boolean isPureCash) { - return isPureCash ? DateFormatter.getSimpleKstDate() : tradeTime; + return isPureCash + ? LocalDateTime.now(TimeZone.ASIA_SEOUL.zoneId()) + .format(DateFormatter.SIMPLE_YEAR_MONTH_DAY) + : tradeTime; } public String tradeUniqueNo(boolean isPureCash) { diff --git a/src/main/java/com/everyonewaiter/domain/order/OrderUpdateEvent.java b/src/main/java/com/everyonewaiter/domain/order/OrderUpdateEvent.java index 1536aa24..1af8d80d 100644 --- a/src/main/java/com/everyonewaiter/domain/order/OrderUpdateEvent.java +++ b/src/main/java/com/everyonewaiter/domain/order/OrderUpdateEvent.java @@ -1,7 +1,12 @@ package com.everyonewaiter.domain.order; -import com.everyonewaiter.domain.receipt.Receipt; +import java.util.List; -public record OrderUpdateEvent(Long storeId, int tableNo, Receipt receipt) { +public record OrderUpdateEvent( + Long storeId, + int tableNo, + List orders, + OrderUpdateRequests updateRequests +) { } diff --git a/src/main/java/com/everyonewaiter/domain/pos/PosTable.java b/src/main/java/com/everyonewaiter/domain/pos/PosTable.java index 40208157..195dbaab 100644 --- a/src/main/java/com/everyonewaiter/domain/pos/PosTable.java +++ b/src/main/java/com/everyonewaiter/domain/pos/PosTable.java @@ -14,11 +14,9 @@ import com.everyonewaiter.domain.order.OrderUpdateEvent; import com.everyonewaiter.domain.order.OrderUpdateRequest; import com.everyonewaiter.domain.order.OrderUpdateRequests; -import com.everyonewaiter.domain.receipt.Receipt; import com.everyonewaiter.domain.receipt.ReceiptResendEvent; import com.everyonewaiter.domain.sse.SseEvent; import com.everyonewaiter.domain.store.Store; -import jakarta.annotation.Nullable; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -128,16 +126,14 @@ public void cancelOrder(Long orderId) { registerEvent(new SseEvent(store.getId(), POS, UPDATE, getTableNo())); } - public void updateOrder(OrderUpdateRequests updateRequests, @Nullable Receipt diff) { + public void updateOrder(OrderUpdateRequests updateRequests) { PosTableActivity posTableActivity = getActiveActivityOrThrow(); for (OrderUpdateRequest updateRequest : updateRequests.orders()) { posTableActivity.updateOrder(updateRequest); } - if (diff != null) { - registerEvent(new OrderUpdateEvent(store.getId(), tableNo, diff)); - } + registerEvent(new OrderUpdateEvent(store.getId(), tableNo, getOrderedOrders(), updateRequests)); registerEvent(new SseEvent(store.getId(), ORDER, UPDATE, getTableNo())); registerEvent(new SseEvent(store.getId(), POS, UPDATE, getTableNo())); } diff --git a/src/main/java/com/everyonewaiter/domain/receipt/Receipt.java b/src/main/java/com/everyonewaiter/domain/receipt/Receipt.java index 3b67574a..05f562b0 100644 --- a/src/main/java/com/everyonewaiter/domain/receipt/Receipt.java +++ b/src/main/java/com/everyonewaiter/domain/receipt/Receipt.java @@ -36,6 +36,33 @@ public static Receipt of(int tableNo, List orders, int printNo) { ); } + public static boolean hasDiff(List orders, OrderUpdateRequests updateRequests) { + Map beforeOrderMenus = orders.stream() + .flatMap(order -> order.getPrintEnabledOrderMenus().stream()) + .collect(Collectors.toMap(OrderMenu::getId, orderMenu -> orderMenu)); + + List afterOrderMenus = updateRequests.orders() + .stream() + .flatMap(order -> order.orderMenus().stream()) + .toList(); + + for (OrderMenuQuantityUpdateRequest afterOrderMenu : afterOrderMenus) { + if (!beforeOrderMenus.containsKey(afterOrderMenu.orderMenuId())) { + continue; + } + + OrderMenu orderMenu = beforeOrderMenus.get(afterOrderMenu.orderMenuId()); + + int updatedQuantity = afterOrderMenu.quantity() - orderMenu.getQuantity(); + + if (updatedQuantity != 0) { + return true; + } + } + + return false; + } + public static @Nullable Receipt diff( int tableNo, List orders, diff --git a/src/main/java/com/everyonewaiter/domain/shared/ErrorCode.java b/src/main/java/com/everyonewaiter/domain/shared/ErrorCode.java index fba55a25..ca59b98f 100644 --- a/src/main/java/com/everyonewaiter/domain/shared/ErrorCode.java +++ b/src/main/java/com/everyonewaiter/domain/shared/ErrorCode.java @@ -39,6 +39,7 @@ public enum ErrorCode { // ACCOUNT ALREADY_USE_EMAIL(BAD_REQUEST, "입력하신 이메일은 이미 사용 중이에요. 다른 이메일을 입력해 주세요."), ALREADY_USE_PHONE_NUMBER(BAD_REQUEST, "입력하신 휴대폰 번호는 이미 사용 중이에요. 다른 휴대폰 번호를 입력해 주세요."), + MISMATCHED_CURRENT_PASSWORD(BAD_REQUEST, "현재 비밀번호가 일치하지 않아요. 입력하신 비밀번호를 확인해 주세요."), DISABLED_ACCOUNT(BAD_REQUEST, "해당 계정은 비활성 또는 탈퇴된 계정이에요."), ACCOUNT_NOT_FOUND(NOT_FOUND, "계정을 찾지 못했어요."), diff --git a/src/main/java/com/everyonewaiter/domain/shared/GithubAsset.java b/src/main/java/com/everyonewaiter/domain/shared/GithubAsset.java new file mode 100644 index 00000000..6e86fa1f --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/shared/GithubAsset.java @@ -0,0 +1,22 @@ +package com.everyonewaiter.domain.shared; + +import jakarta.annotation.Nullable; + +public record GithubAsset( + String url, + Long id, + String node_id, + String name, + @Nullable String label, + GithubAuthor uploader, + String content_type, + String state, + Long size, + String digest, + Long download_count, + String created_at, + String updated_at, + String browser_download_url +) { + +} diff --git a/src/main/java/com/everyonewaiter/domain/shared/GithubAuthor.java b/src/main/java/com/everyonewaiter/domain/shared/GithubAuthor.java new file mode 100644 index 00000000..f67f2c9c --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/shared/GithubAuthor.java @@ -0,0 +1,25 @@ +package com.everyonewaiter.domain.shared; + +public record GithubAuthor( + String login, + Long id, + String node_id, + String avatar_url, + String gravatar_id, + String url, + String html_url, + String followers_url, + String following_url, + String gists_url, + String starred_url, + String subscriptions_url, + String organizations_url, + String repos_url, + String events_url, + String received_events_url, + String type, + String user_view_type, + boolean site_admin +) { + +} diff --git a/src/main/java/com/everyonewaiter/domain/shared/GithubRelease.java b/src/main/java/com/everyonewaiter/domain/shared/GithubRelease.java new file mode 100644 index 00000000..1e308d19 --- /dev/null +++ b/src/main/java/com/everyonewaiter/domain/shared/GithubRelease.java @@ -0,0 +1,28 @@ +package com.everyonewaiter.domain.shared; + +import java.util.List; + +public record GithubRelease( + String url, + String assets_url, + String upload_url, + String html_url, + Long id, + GithubAuthor author, + String node_id, + String tag_name, + String target_commitish, + String name, + boolean draft, + boolean immutable, + boolean prerelease, + String created_at, + String updated_at, + String published_at, + List assets, + String tarball_url, + String zipball_url, + String body +) { + +} diff --git a/src/main/java/com/everyonewaiter/domain/support/DateFormatter.java b/src/main/java/com/everyonewaiter/domain/support/DateFormatter.java index aab5b6f4..650b5332 100644 --- a/src/main/java/com/everyonewaiter/domain/support/DateFormatter.java +++ b/src/main/java/com/everyonewaiter/domain/support/DateFormatter.java @@ -2,7 +2,6 @@ import static lombok.AccessLevel.PRIVATE; -import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import lombok.NoArgsConstructor; @@ -16,10 +15,4 @@ public final class DateFormatter { public static final DateTimeFormatter YEAR_MONTH = DateTimeFormatter.ofPattern("yyyyMM"); public static final DateTimeFormatter YEAR_MONTH_DAY = DateTimeFormatter.ofPattern("yyyyMMdd"); - public static String getSimpleKstDate() { - LocalDateTime now = LocalDateTime.now(TimeZone.ASIA_SEOUL.zoneId()); - - return now.format(SIMPLE_YEAR_MONTH_DAY); - } - } diff --git a/src/main/resources/db/migration/V2__drop_refresh_token.sql b/src/main/resources/db/migration/V2__drop_refresh_token.sql new file mode 100644 index 00000000..13f97ffe --- /dev/null +++ b/src/main/resources/db/migration/V2__drop_refresh_token.sql @@ -0,0 +1 @@ +drop table refresh_token; diff --git a/src/test/java/com/everyonewaiter/domain/TestFixture.kt b/src/test/java/com/everyonewaiter/domain/TestFixture.kt new file mode 100644 index 00000000..9a596b42 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/TestFixture.kt @@ -0,0 +1,5 @@ +package com.everyonewaiter.domain + +const val ADMIN_EMAIL = "admin@everyonewaiter.com" +const val ADMIN_PASSWORD = "@password1" +const val ADMIN_PHONE_NUMBER = "01012345678" diff --git a/src/test/java/com/everyonewaiter/domain/account/AccountFixture.java b/src/test/java/com/everyonewaiter/domain/account/AccountFixture.java deleted file mode 100644 index 88de433c..00000000 --- a/src/test/java/com/everyonewaiter/domain/account/AccountFixture.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.everyonewaiter.domain.account; - -import static lombok.AccessLevel.PRIVATE; - -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = PRIVATE) -public class AccountFixture { - - public static PasswordEncoder createPasswordEncoder() { - return new PasswordEncoder() { - @Override - public String encode(String rawPassword) { - return "(encoded) " + rawPassword; - } - - @Override - public boolean matches(String rawPassword, String encodedPassword) { - return encode(rawPassword).equals(encodedPassword); - } - }; - } - - public static AccountCreateRequest createAccountCreateRequest() { - return createAccountCreateRequest("admin@everyonewaiter.com"); - } - - public static AccountCreateRequest createAccountCreateRequest(String email) { - return createAccountCreateRequest(email, "01012345678"); - } - - public static AccountCreateRequest createAccountCreateRequest(String email, String phoneNumber) { - return new AccountCreateRequest(email, "@password1", phoneNumber); - } - - public static AccountSignInRequest createAccountSignInRequest() { - return createAccountSignInRequest("@password1"); - } - - public static AccountSignInRequest createAccountSignInRequest(String password) { - return new AccountSignInRequest("admin@everyonewaiter.com", password); - } - - public static AccountAdminUpdateRequest createAccountAdminUpdateRequest() { - return new AccountAdminUpdateRequest(AccountState.ACTIVE, AccountPermission.OWNER); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/account/AccountFixture.kt b/src/test/java/com/everyonewaiter/domain/account/AccountFixture.kt new file mode 100644 index 00000000..06b097ab --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/account/AccountFixture.kt @@ -0,0 +1,61 @@ +package com.everyonewaiter.domain.account + +import com.everyonewaiter.domain.ADMIN_EMAIL +import com.everyonewaiter.domain.ADMIN_PASSWORD +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import org.springframework.test.util.ReflectionTestUtils + +fun createPasswordEncoder(): PasswordEncoder { + return object : PasswordEncoder { + override fun encode(rawPassword: String): String { + return "(encoded) $rawPassword" + } + + override fun matches(rawPassword: String, encodedPassword: String): Boolean { + return encode(rawPassword) == encodedPassword + } + } +} + +fun createAccount( + createRequest: AccountCreateRequest = createAccountCreateRequest(), + passwordEncoder: PasswordEncoder = createPasswordEncoder(), +): Account { + return Account.create(createRequest, passwordEncoder) +} + +fun createActiveAccount(permission: AccountPermission = AccountPermission.USER): Account { + val account = createAccount() + account.activate() + ReflectionTestUtils.setField(account, "permission", permission) + return account +} + +fun createAccountCreateRequest( + email: String = ADMIN_EMAIL, + password: String = ADMIN_PASSWORD, + phoneNumber: String = ADMIN_PHONE_NUMBER, +): AccountCreateRequest { + return AccountCreateRequest(email, password, phoneNumber) +} + +fun createAccountSignInRequest( + email: String = ADMIN_EMAIL, + password: String = ADMIN_PASSWORD, +): AccountSignInRequest { + return AccountSignInRequest(email, password) +} + +fun createAccountPasswordChangeRequest( + currentPassword: String = ADMIN_PASSWORD, + newPassword: String = "@password2", +): AccountPasswordChangeRequest { + return AccountPasswordChangeRequest(currentPassword, newPassword) +} + +fun createAccountAdminUpdateRequest( + state: AccountState = AccountState.ACTIVE, + permission: AccountPermission = AccountPermission.OWNER, +): AccountAdminUpdateRequest { + return AccountAdminUpdateRequest(state, permission) +} diff --git a/src/test/java/com/everyonewaiter/domain/account/AccountTest.java b/src/test/java/com/everyonewaiter/domain/account/AccountTest.java deleted file mode 100644 index 901a041d..00000000 --- a/src/test/java/com/everyonewaiter/domain/account/AccountTest.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.everyonewaiter.domain.account; - -import static com.everyonewaiter.domain.account.AccountFixture.createAccountAdminUpdateRequest; -import static com.everyonewaiter.domain.account.AccountFixture.createAccountCreateRequest; -import static com.everyonewaiter.domain.account.AccountFixture.createAccountSignInRequest; -import static com.everyonewaiter.domain.account.AccountFixture.createPasswordEncoder; -import static com.everyonewaiter.domain.account.AccountPermission.ADMIN; -import static com.everyonewaiter.domain.account.AccountPermission.OWNER; -import static com.everyonewaiter.domain.account.AccountPermission.USER; -import static com.everyonewaiter.domain.account.AccountState.ACTIVE; -import static com.everyonewaiter.domain.account.AccountState.INACTIVE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import java.time.Instant; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; - -class AccountTest { - - @Test - void create() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThat(account.getState()).isEqualTo(INACTIVE); - assertThat(account.getPermission()).isEqualTo(USER); - assertThat(account.getLastSignIn()).isEqualTo(Instant.ofEpochMilli(0)); - - Object domainEvents = ReflectionTestUtils.invokeGetterMethod(account, "domainEvents"); - assertThat(domainEvents).isInstanceOf(List.class); - assertThat((List) domainEvents).hasSize(1); - } - - @Test - void isInactive() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThat(account.isInactive()).isTrue(); - - ReflectionTestUtils.setField(account, "state", ACTIVE); - - assertThat(account.isInactive()).isFalse(); - } - - @Test - void isActive() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThat(account.isActive()).isFalse(); - - ReflectionTestUtils.setField(account, "state", ACTIVE); - - assertThat(account.isActive()).isTrue(); - } - - @Test - void hasPermission() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThat(account.hasPermission(USER)).isTrue(); - assertThat(account.hasPermission(OWNER)).isFalse(); - assertThat(account.hasPermission(ADMIN)).isFalse(); - - ReflectionTestUtils.setField(account, "permission", OWNER); - - assertThat(account.hasPermission(USER)).isTrue(); - assertThat(account.hasPermission(OWNER)).isTrue(); - assertThat(account.hasPermission(ADMIN)).isFalse(); - - ReflectionTestUtils.setField(account, "permission", ADMIN); - - assertThat(account.hasPermission(USER)).isTrue(); - assertThat(account.hasPermission(OWNER)).isTrue(); - assertThat(account.hasPermission(ADMIN)).isTrue(); - } - - @Test - void activate() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThat(account.getState()).isEqualTo(INACTIVE); - - account.activate(); - - assertThat(account.getState()).isEqualTo(ACTIVE); - - assertThatThrownBy(account::activate).isInstanceOf(AlreadyVerifiedEmailException.class); - } - - @Test - void authorize() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - account.activate(); - account.authorize(OWNER); - - assertThat(account.getPermission()).isEqualTo(OWNER); - } - - @Test - void authorizeFail() { - Account account = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - assertThatThrownBy(() -> account.authorize(OWNER)).isInstanceOf(DisabledAccountException.class); - - account.activate(); - - assertThatThrownBy(() -> account.authorize(ADMIN)).isInstanceOf(IllegalStateException.class); - } - - @Test - void signIn() { - PasswordEncoder passwordEncoder = createPasswordEncoder(); - - Account account = Account.create(createAccountCreateRequest(), passwordEncoder); - - account.activate(); - account.signIn(createAccountSignInRequest(), passwordEncoder); - - assertThat(account.getLastSignIn()).isNotEqualTo(Instant.ofEpochMilli(0)); - } - - @Test - void signInFail() { - PasswordEncoder passwordEncoder = createPasswordEncoder(); - - Account account = Account.create(createAccountCreateRequest(), passwordEncoder); - - assertThatThrownBy(() -> account.signIn(createAccountSignInRequest(), passwordEncoder)) - .isInstanceOf(NotCompleteEmailVerificationException.class); - assertThatThrownBy(() -> account.signIn(createAccountSignInRequest("invalid"), passwordEncoder)) - .isInstanceOf(FailedSignInException.class); - - account.activate(); - - assertThatThrownBy(() -> account.signIn(createAccountSignInRequest("invalid"), passwordEncoder)) - .isInstanceOf(FailedSignInException.class); - } - - @Test - void update() { - Account adminAccount = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - ReflectionTestUtils.setField(adminAccount, "state", ACTIVE); - ReflectionTestUtils.setField(adminAccount, "permission", ADMIN); - - Account userAccount = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - adminAccount.update(userAccount, createAccountAdminUpdateRequest()); - - assertThat(userAccount.getState()).isEqualTo(ACTIVE); - assertThat(userAccount.getPermission()).isEqualTo(OWNER); - } - - @Test - void updateFail() { - Account adminAccount = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - Account userAccount = Account.create(createAccountCreateRequest(), createPasswordEncoder()); - - ReflectionTestUtils.setField(adminAccount, "state", INACTIVE); - ReflectionTestUtils.setField(adminAccount, "permission", ADMIN); - - assertThatThrownBy(() -> adminAccount.update(userAccount, createAccountAdminUpdateRequest())) - .isInstanceOf(IllegalStateException.class); - - ReflectionTestUtils.setField(adminAccount, "state", ACTIVE); - ReflectionTestUtils.setField(adminAccount, "permission", OWNER); - - assertThatThrownBy(() -> adminAccount.update(userAccount, createAccountAdminUpdateRequest())) - .isInstanceOf(IllegalStateException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/account/AccountTest.kt b/src/test/java/com/everyonewaiter/domain/account/AccountTest.kt new file mode 100644 index 00000000..b37dbfd1 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/account/AccountTest.kt @@ -0,0 +1,227 @@ +package com.everyonewaiter.domain.account + +import com.everyonewaiter.domain.ADMIN_PASSWORD +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.springframework.test.util.ReflectionTestUtils +import java.time.Instant + +class AccountTest { + + @Test + fun `계정 생성`() { + val account = createAccount() + + assertThat(account.state).isEqualTo(AccountState.INACTIVE) + assertThat(account.permission).isEqualTo(AccountPermission.USER) + assertThat(account.lastSignIn).isEqualTo(Instant.ofEpochMilli(0)) + + val domainEvents = ReflectionTestUtils.invokeGetterMethod(account, "domainEvents") + assertThat(domainEvents as MutableList<*>).hasSize(1) + assertThat(domainEvents[0]).isInstanceOf(AccountCreateEvent::class.java) + } + + @Test + fun `계정이 비활성 상태인지 여부 반환`() { + val account = createAccount() + + assertThat(account.isInactive).isTrue + + ReflectionTestUtils.setField(account, "state", AccountState.ACTIVE) + + assertThat(account.isInactive).isFalse + } + + @Test + fun `계정이 활성 상태인지 여부 반환`() { + val account = createAccount() + + assertThat(account.isActive).isFalse + + ReflectionTestUtils.setField(account, "state", AccountState.ACTIVE) + + assertThat(account.isActive).isTrue + } + + @Test + fun `계정이 계층형 권한을 가지고 있는지 여부 반환`() { + val account = createAccount() + + assertThat(account.hasPermission(AccountPermission.USER)).isTrue + assertThat(account.hasPermission(AccountPermission.OWNER)).isFalse + assertThat(account.hasPermission(AccountPermission.ADMIN)).isFalse + + ReflectionTestUtils.setField(account, "permission", AccountPermission.OWNER) + + assertThat(account.hasPermission(AccountPermission.USER)).isTrue + assertThat(account.hasPermission(AccountPermission.OWNER)).isTrue + assertThat(account.hasPermission(AccountPermission.ADMIN)).isFalse + + ReflectionTestUtils.setField(account, "permission", AccountPermission.ADMIN) + + assertThat(account.hasPermission(AccountPermission.USER)).isTrue + assertThat(account.hasPermission(AccountPermission.OWNER)).isTrue + assertThat(account.hasPermission(AccountPermission.ADMIN)).isTrue + } + + @Test + fun `계정 활성화`() { + val account = createAccount() + + assertThat(account.state).isEqualTo(AccountState.INACTIVE) + + account.activate() + + assertThat(account.state).isEqualTo(AccountState.ACTIVE) + + assertThatThrownBy { account.activate() } + .isInstanceOf(AlreadyVerifiedEmailException::class.java) + } + + @Test + fun `계정 권한 부여`() { + val account = createActiveAccount() + + assertThat(account.permission).isEqualTo(AccountPermission.USER) + + account.authorize(AccountPermission.OWNER) + + assertThat(account.permission).isEqualTo(AccountPermission.OWNER) + } + + @Test + fun `계정이 이미 해당 권한을 가지고 있다면 권한 부여 X`() { + val account = createActiveAccount(permission = AccountPermission.ADMIN) + + assertThat(account.permission).isEqualTo(AccountPermission.ADMIN) + + account.authorize(AccountPermission.OWNER) + + assertThat(account.permission).isEqualTo(AccountPermission.ADMIN) + } + + @Test + fun `계정이 활성 상태가 아닌 경우 권한 부여 실패`() { + val account = createAccount() + + AccountState.entries + .stream() + .filter { it != AccountState.ACTIVE } + .forEach { + ReflectionTestUtils.setField(account, "state", it) + assertThatThrownBy { account.authorize(AccountPermission.OWNER) } + .isInstanceOf(DisabledAccountException::class.java) + } + } + + @Test + fun `계정에 관리자 권한을 부여하려는 경우 권한 부여 실패`() { + val account = createActiveAccount() + + assertThatThrownBy { account.authorize(AccountPermission.ADMIN) } + .isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun `계정 로그인`() { + val account = createActiveAccount() + + assertThat(account.lastSignIn).isEqualTo(Instant.ofEpochMilli(0)) + + account.signIn(createAccountSignInRequest(), createPasswordEncoder()) + + assertThat(account.lastSignIn).isNotEqualTo(Instant.ofEpochMilli(0)) + } + + @Test + fun `이메일 인증이 완료되지 않은 경우 로그인 실패`() { + val account = createAccount() + + assertThatThrownBy { account.signIn(createAccountSignInRequest(), createPasswordEncoder()) } + .isInstanceOf(NotCompleteEmailVerificationException::class.java) + } + + @Test + fun `비밀번호가 일치하지 않는 경우 로그인 실패`() { + val account1 = createAccount() + val account2 = createActiveAccount() + + assertThatThrownBy { + account1.signIn( + createAccountSignInRequest(password = "invalid"), + createPasswordEncoder() + ) + }.isInstanceOf(FailedSignInException::class.java) + + assertThatThrownBy { + account2.signIn( + createAccountSignInRequest(password = "invalid"), + createPasswordEncoder() + ) + }.isInstanceOf(FailedSignInException::class.java) + } + + @Test + fun `비밀번호 변경`() { + val passwordEncoder = createPasswordEncoder() + val account = createAccount() + + assertThat(passwordEncoder.matches(ADMIN_PASSWORD, account.password)).isTrue + + account.changePassword(createAccountPasswordChangeRequest(), passwordEncoder) + + assertThat(passwordEncoder.matches("@password2", account.password)).isTrue + } + + @Test + fun `현재 비밀번호가 일치하지 않는 경우 비밀번호 변경 실패`() { + val passwordEncoder = createPasswordEncoder() + val account = createAccount() + + assertThatThrownBy { + account.changePassword( + createAccountPasswordChangeRequest(currentPassword = "@invalid123"), + passwordEncoder + ) + }.isInstanceOf(MismatchedCurrentPasswordException::class.java) + } + + @Test + fun `관리자가 사용자 계정 업데이트`() { + val adminAccount = createActiveAccount(permission = AccountPermission.ADMIN) + val userAccount = createAccount() + + assertThat(userAccount.state).isEqualTo(AccountState.INACTIVE) + assertThat(userAccount.permission).isEqualTo(AccountPermission.USER) + + adminAccount.update(userAccount, createAccountAdminUpdateRequest()) + + assertThat(userAccount.state).isEqualTo(AccountState.ACTIVE) + assertThat(userAccount.permission).isEqualTo(AccountPermission.OWNER) + } + + @Test + fun `관리자 계정이 활성 상태가 아니라면 사용자 계정 업데이트 실패`() { + val adminAccount = createActiveAccount(permission = AccountPermission.ADMIN) + val userAccount = createAccount() + + ReflectionTestUtils.setField(adminAccount, "state", AccountState.INACTIVE) + + assertThatThrownBy { adminAccount.update(userAccount, createAccountAdminUpdateRequest()) } + .isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun `관리자 권한이 없는 계정이라면 사용자 계정 업데이트 실패`() { + val fakeAdminAccount = createActiveAccount(permission = AccountPermission.OWNER) + val userAccount = createAccount() + + assertThatThrownBy { + fakeAdminAccount.update( + userAccount, + createAccountAdminUpdateRequest() + ) + }.isInstanceOf(IllegalStateException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptFixture.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptFixture.kt new file mode 100644 index 00000000..4dd58520 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptFixture.kt @@ -0,0 +1,11 @@ +package com.everyonewaiter.domain.auth + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import com.everyonewaiter.domain.shared.PhoneNumber + +fun createAuthAttempt( + purpose: AuthPurpose = AuthPurpose.SIGN_UP, + phoneNumber: String = ADMIN_PHONE_NUMBER +): AuthAttempt { + return AuthAttempt(purpose, PhoneNumber(phoneNumber)) +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.java b/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.java deleted file mode 100644 index 2b8b21c0..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static com.everyonewaiter.domain.auth.AuthFixture.createAuthAttempt; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; -import org.junit.jupiter.api.Test; - -class AuthAttemptTest { - - @Test - void isExceed() { - AuthAttempt authAttempt = createAuthAttempt(); - - assertThat(authAttempt.isExceed(0)).isFalse(); - assertThat(authAttempt.isExceed(4)).isFalse(); - assertThat(authAttempt.isExceed(5)).isTrue(); - } - - @Test - void key() { - AuthAttempt authAttempt = createAuthAttempt(); - - assertThat(authAttempt.key()).isEqualTo("auth:attempt:sign_up:01012345678"); - } - - @Test - void value() { - AuthAttempt authAttempt = createAuthAttempt(); - - assertThat(authAttempt.value()).isEqualTo(-2); - } - - @Test - void expiration() { - AuthAttempt authAttempt = createAuthAttempt(); - - assertThat(authAttempt.expiration()).isEqualTo(Duration.ofDays(1)); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.kt new file mode 100644 index 00000000..c0205840 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthAttemptTest.kt @@ -0,0 +1,39 @@ +package com.everyonewaiter.domain.auth + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.Duration + +class AuthAttemptTest { + + @Test + fun `인증 시도 횟수 초과 여부 반환`() { + AuthPurpose.entries.forEach { + val attempt = createAuthAttempt(purpose = it) + assertThat(attempt.isExceed(it.maxAttempt - 1)).isFalse + assertThat(attempt.isExceed(it.maxAttempt)).isTrue + } + } + + @Test + fun `인증 시도 횟수 저장용 키`() { + AuthPurpose.entries.forEach { + val attempt = createAuthAttempt(purpose = it) + assertThat(attempt.key()).isEqualTo("auth:attempt:${it.lowerCaseName}:${attempt.phoneNumber.value}") + } + } + + @Test + fun `인증 시도 횟수 저장용 값은 사용하지 않음`() { + val attempt = createAuthAttempt() + + assertThat(attempt.value()).isEqualTo(-2) + } + + @Test + fun `인증 시도 횟수의 유효기간은 1일`() { + val attempt = createAuthAttempt() + + assertThat(attempt.expiration).isEqualTo(Duration.ofDays(1)) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthCodeFixture.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthCodeFixture.kt new file mode 100644 index 00000000..0585c7fc --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthCodeFixture.kt @@ -0,0 +1,8 @@ +package com.everyonewaiter.domain.auth + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import com.everyonewaiter.domain.shared.PhoneNumber + +fun createAuthCode(phoneNumber: String = ADMIN_PHONE_NUMBER, code: Int = 123456): AuthCode { + return AuthCode(PhoneNumber(phoneNumber), code) +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.java b/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.java deleted file mode 100644 index 03799d99..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static com.everyonewaiter.domain.auth.AuthFixture.createAuthCode; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import java.time.Duration; -import org.junit.jupiter.api.Test; - -class AuthCodeTest { - - @Test - void verify() { - AuthCode authCode = createAuthCode(); - - assertThatCode(() -> authCode.verify(123456)).doesNotThrowAnyException(); - } - - @Test - void verifyFail() { - AuthCode authCode = createAuthCode(); - - assertThatThrownBy(() -> authCode.verify(0)) - .isInstanceOf(ExpiredVerificationCodeException.class); - assertThatThrownBy(() -> authCode.verify(999999)) - .isInstanceOf(UnmatchedVerificationCodeException.class); - } - - @Test - void key() { - AuthCode authCode = createAuthCode(); - - assertThat(authCode.key()).isEqualTo("auth:code:01012345678"); - } - - @Test - void value() { - AuthCode authCode = createAuthCode(); - - assertThat(authCode.value()).isEqualTo(123456); - } - - @Test - void expiration() { - AuthCode authCode = createAuthCode(); - - assertThat(authCode.expiration()).isEqualTo(Duration.ofMinutes(5)); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.kt new file mode 100644 index 00000000..76f81026 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthCodeTest.kt @@ -0,0 +1,59 @@ +package com.everyonewaiter.domain.auth + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import com.everyonewaiter.domain.shared.PhoneNumber +import org.assertj.core.api.Assertions.* +import org.junit.jupiter.api.Test +import java.time.Duration + +class AuthCodeTest { + + @Test + fun `인증 번호 생성`() { + val code = AuthCode(PhoneNumber(ADMIN_PHONE_NUMBER)) + + assertThat(code.code.toString().length).isEqualTo(6) + } + + @Test + fun `인증 코드 검증`() { + val code = createAuthCode() + + assertThatCode { code.verify(code.code) }.doesNotThrowAnyException() + } + + @Test + fun `인증 코드가 만료된 경우 검증 실패`() { + val code = createAuthCode() + + assertThatThrownBy { code.verify(0) }.isInstanceOf(ExpiredVerificationCodeException::class.java) + } + + @Test + fun `인증 코드가 일치하지 않는 경우 검증 실패`() { + val code = createAuthCode() + + assertThatThrownBy { code.verify(999999) }.isInstanceOf(UnmatchedVerificationCodeException::class.java) + } + + @Test + fun `인증 코드 저장용 키`() { + val code = createAuthCode() + + assertThat(code.key()).isEqualTo("auth:code:${code.phoneNumber.value}") + } + + @Test + fun `인증 코드 저장용 값`() { + val code = createAuthCode() + + assertThat(code.value()).isEqualTo(code.code) + } + + @Test + fun `인증 코드의 유효기간은 5분`() { + val code = createAuthCode() + + assertThat(code.expiration).isEqualTo(Duration.ofMinutes(5)) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthFixture.java b/src/test/java/com/everyonewaiter/domain/auth/AuthFixture.java deleted file mode 100644 index 064454fb..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/AuthFixture.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static lombok.AccessLevel.PRIVATE; - -import com.everyonewaiter.domain.shared.PhoneNumber; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = PRIVATE) -public class AuthFixture { - - public static AuthAttempt createAuthAttempt() { - return new AuthAttempt(AuthPurpose.SIGN_UP, new PhoneNumber("01012345678")); - } - - public static AuthCode createAuthCode() { - return new AuthCode(new PhoneNumber("01012345678"), 123456); - } - - public static AuthSuccess createAuthSuccess() { - return createAuthSuccess(AuthPurpose.SIGN_UP); - } - - public static AuthSuccess createAuthSuccess(AuthPurpose authPurpose) { - return new AuthSuccess(authPurpose, new PhoneNumber("01012345678")); - } - - public static SendAuthCodeRequest createSendAuthCodeRequest() { - return new SendAuthCodeRequest("01012345678"); - } - - public static VerifyAuthCodeRequest createVerifyAuthCodeRequest() { - return new VerifyAuthCodeRequest("01012345678", 123456); - } - - public static SendAuthMailRequest createSendAuthMailRequest() { - return new SendAuthMailRequest("admin@everyonewaiter.com"); - } - - public static SignInTokenRenewRequest createSignInTokenRenewRequest(String refreshToken) { - return new SignInTokenRenewRequest(refreshToken); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessFixture.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessFixture.kt new file mode 100644 index 00000000..3ae5083d --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessFixture.kt @@ -0,0 +1,11 @@ +package com.everyonewaiter.domain.auth + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import com.everyonewaiter.domain.shared.PhoneNumber + +fun createAuthSuccess( + purpose: AuthPurpose = AuthPurpose.SIGN_UP, + phoneNumber: String = ADMIN_PHONE_NUMBER +): AuthSuccess { + return AuthSuccess(purpose, PhoneNumber(phoneNumber)) +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.java b/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.java deleted file mode 100644 index 82f3df76..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static com.everyonewaiter.domain.auth.AuthFixture.createAuthSuccess; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class AuthSuccessTest { - - @Test - void key() { - AuthSuccess authSuccess = createAuthSuccess(); - - assertThat(authSuccess.key()).isEqualTo("auth:success:01012345678"); - } - - @Test - void value() { - AuthSuccess authSuccess = createAuthSuccess(); - - assertThat(authSuccess.value()).isEqualTo(-2); - } - - @Test - void expiration() { - AuthSuccess authSuccess1 = createAuthSuccess(AuthPurpose.SIGN_UP); - AuthSuccess authSuccess2 = createAuthSuccess(AuthPurpose.CREATE_DEVICE); - - assertThat(authSuccess1.expiration()).isEqualTo(AuthPurpose.SIGN_UP.getExpiration()); - assertThat(authSuccess2.expiration()).isEqualTo(AuthPurpose.CREATE_DEVICE.getExpiration()); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.kt b/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.kt new file mode 100644 index 00000000..b733952d --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/AuthSuccessTest.kt @@ -0,0 +1,29 @@ +package com.everyonewaiter.domain.auth + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AuthSuccessTest { + + @Test + fun `인증 성공 저장용 키`() { + val success = createAuthSuccess() + + assertThat(success.key()).isEqualTo("auth:success:${success.phoneNumber.value}") + } + + @Test + fun `인증 성공 저장용 값은 사용하지 않음`() { + val success = createAuthSuccess() + + assertThat(success.value()).isEqualTo(-2) + } + + @Test + fun `인증 성공의 유효기간은 인증 목적 마다 다름`() { + AuthPurpose.entries.forEach { + val success = createAuthSuccess(purpose = it) + assertThat(success.expiration).isEqualTo(it.expiration) + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.java b/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.java deleted file mode 100644 index 7963cf6f..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class JwtPayloadTest { - - @Test - void constructor() { - JwtPayload payload1 = new JwtPayload(1L, "1"); - JwtPayload payload2 = new JwtPayload(2L, 2L); - - assertThat(payload1.id()).isEqualTo(1L); - assertThat(payload1.subject()).isEqualTo("1"); - assertThat(payload2.id()).isEqualTo(2L); - assertThat(payload2.subject()).isEqualTo("2"); - } - - @Test - void parseLongSubject() { - JwtPayload payload = new JwtPayload(1L, "1"); - - Long subject = payload.parseLongSubject(); - - assertThat(subject).isEqualTo(1L); - } - - @Test - void parseLongSubjectFail() { - JwtPayload payload = new JwtPayload(1L, "string"); - - assertThatThrownBy(payload::parseLongSubject) - .isInstanceOf(NumberFormatException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.kt b/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.kt new file mode 100644 index 00000000..447d07c1 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/auth/JwtPayloadTest.kt @@ -0,0 +1,24 @@ +package com.everyonewaiter.domain.auth + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class JwtPayloadTest { + + @Test + fun `JWT 페이로드의 ID가 숫자인 경우 파싱`() { + val payload1 = JwtPayload("1", "subject") + val payload2 = JwtPayload(2L, "subject") + + assertThat(payload1.longId).isEqualTo(1) + assertThat(payload2.longId).isEqualTo(2) + } + + @Test + fun `JWT 페이로드의 ID가 숫자가 아닌 경우 파싱 실패`() { + val payload = JwtPayload("id", "subject") + + assertThatThrownBy { payload.longId }.isInstanceOf(NumberFormatException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/auth/RefreshTokenTest.java b/src/test/java/com/everyonewaiter/domain/auth/RefreshTokenTest.java deleted file mode 100644 index c06f3c4c..00000000 --- a/src/test/java/com/everyonewaiter/domain/auth/RefreshTokenTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.everyonewaiter.domain.auth; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import com.everyonewaiter.domain.shared.AuthenticationException; -import org.junit.jupiter.api.Test; - -class RefreshTokenTest { - - @Test - void create() { - RefreshToken refreshToken = RefreshToken.create(1L); - - assertThat(refreshToken.getAccountId()).isEqualTo(1L); - assertThat(refreshToken.getCurrentTokenId()).isEqualTo(refreshToken.getId()); - } - - @Test - void renew() { - RefreshToken refreshToken = RefreshToken.create(1L); - - assertThat(refreshToken.getCurrentTokenId()).isEqualTo(refreshToken.getId()); - - refreshToken.renew(refreshToken.getCurrentTokenId()); - - assertThat(refreshToken.getCurrentTokenId()).isNotEqualTo(refreshToken.getId()); - } - - @Test - void renewFail() { - RefreshToken refreshToken = RefreshToken.create(1L); - - assertThatThrownBy(() -> refreshToken.renew(999L)) - .isInstanceOf(AuthenticationException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.java b/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.java deleted file mode 100644 index be07fe7e..00000000 --- a/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.everyonewaiter.domain.health; - -import static lombok.AccessLevel.PRIVATE; - -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = PRIVATE) -public final class ApkVersionFixture { - - public static ApkVersionCreateRequest createApkVersionCreateRequest() { - return new ApkVersionCreateRequest(1, 0, 0, "https://cdn.everyonewaiter.com/release.apk"); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.kt b/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.kt new file mode 100644 index 00000000..e87ba54e --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/health/ApkVersionFixture.kt @@ -0,0 +1,10 @@ +package com.everyonewaiter.domain.health + +fun createApkVersionCreateRequest( + majorVersion: Int = 1, + minorVersion: Int = 0, + patchVersion: Int = 0, + downloadUri: String = "https://github.com/everyonewaiter/everyonewaiter-store-app/releases" +): ApkVersionCreateRequest { + return ApkVersionCreateRequest(majorVersion, minorVersion, patchVersion, downloadUri) +} diff --git a/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.java b/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.java deleted file mode 100644 index 052ab708..00000000 --- a/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.everyonewaiter.domain.health; - -import static com.everyonewaiter.domain.health.ApkVersionFixture.createApkVersionCreateRequest; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class ApkVersionTest { - - @Test - void create() { - ApkVersionCreateRequest createRequest = createApkVersionCreateRequest(); - - ApkVersion apkVersion = ApkVersion.create(createRequest); - - assertThat(apkVersion.getMajorVersion()).isEqualTo(createRequest.majorVersion()); - assertThat(apkVersion.getMinorVersion()).isEqualTo(createRequest.minorVersion()); - assertThat(apkVersion.getPatchVersion()).isEqualTo(createRequest.patchVersion()); - assertThat(apkVersion.getDownloadUri()).isEqualTo(createRequest.downloadUri()); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.kt b/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.kt new file mode 100644 index 00000000..2b5630bc --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/health/ApkVersionTest.kt @@ -0,0 +1,19 @@ +package com.everyonewaiter.domain.health + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class ApkVersionTest { + + @Test + fun `APK 버전 생성`() { + val createRequest = createApkVersionCreateRequest() + + val apkVersion = ApkVersion.create(createRequest) + + Assertions.assertThat(apkVersion.majorVersion).isEqualTo(createRequest.majorVersion) + Assertions.assertThat(apkVersion.minorVersion).isEqualTo(createRequest.minorVersion) + Assertions.assertThat(apkVersion.patchVersion).isEqualTo(createRequest.patchVersion) + Assertions.assertThat(apkVersion.downloadUri).isEqualTo(createRequest.downloadUri) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkFixture.kt b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkFixture.kt new file mode 100644 index 00000000..b960a9a9 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkFixture.kt @@ -0,0 +1,12 @@ +package com.everyonewaiter.domain.notification + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import com.everyonewaiter.domain.shared.PhoneNumber + +fun createAlimTalkMessage( + template: AlimTalkTemplate = AlimTalkTemplate.AUTHENTICATION_CODE, + phoneNumber: String = ADMIN_PHONE_NUMBER, + vararg variables: Any = Array(size = 1) { 123456 } +): AlimTalkMessage { + return AlimTalkMessage(template, PhoneNumber(phoneNumber), *variables) +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.java b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.java deleted file mode 100644 index 0a728092..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.AlimTalkWeblinkButtonTemplate.MENU_PREVIEW; -import static com.everyonewaiter.domain.notification.NotificationFixture.createAlimTalkMessage; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class AlimTalkMessageTest { - - @Test - void addButton() { - AlimTalkMessage alimTalkMessage = createAlimTalkMessage(); - - alimTalkMessage.addButton(MENU_PREVIEW, 1L); - - assertThat(alimTalkMessage.getButtons()).hasSize(1); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.kt b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.kt new file mode 100644 index 00000000..3847474d --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkMessageTest.kt @@ -0,0 +1,16 @@ +package com.everyonewaiter.domain.notification + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AlimTalkMessageTest { + + @Test + fun `알림톡 버튼 추가`() { + val message = createAlimTalkMessage() + + message.addButton(AlimTalkWeblinkButtonTemplate.MENU_PREVIEW, 1L) + + assertThat(message.buttons).hasSize(1) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.java b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.java deleted file mode 100644 index 3d2e4424..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class AlimTalkTemplateTest { - - @Test - void createContent() { - AlimTalkTemplate template = AlimTalkTemplate.AUTHENTICATION_CODE; // 필요 변수 1개 - - assertThatCode(() -> template.createContent("value")) - .doesNotThrowAnyException(); - } - - @Test - void createContentFail() { - AlimTalkTemplate template = AlimTalkTemplate.AUTHENTICATION_CODE; // 필요 변수 1개 - - assertThatThrownBy(() -> template.createContent("value1", "value2")) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.kt b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.kt new file mode 100644 index 00000000..fcada5e6 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkTemplateTest.kt @@ -0,0 +1,33 @@ +package com.everyonewaiter.domain.notification + +import com.everyonewaiter.domain.support.WordCounter +import org.assertj.core.api.Assertions.assertThatCode +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class AlimTalkTemplateTest { + + @Test + fun `알림톡 컨텐츠 생성`() { + AlimTalkTemplate.entries.forEach { + val parameterCount = WordCounter.count("%s", it.templateContent) + if (parameterCount > 0) { + assertThatCode { + it.createContent(*Array(parameterCount) { "parameter" }) + }.doesNotThrowAnyException() + } else { + assertThatCode { it.createContent() }.doesNotThrowAnyException() + } + } + } + + @Test + fun `매개변수 개수가 옳바르지 않은 경우 알림톡 컨텐츠 생성 실패`() { + AlimTalkTemplate.entries.forEach { + val parameterCount = WordCounter.count("%s", it.templateContent) + assertThatThrownBy { + it.createContent(*Array(parameterCount + 1) { "parameter" }) + }.isInstanceOf(IllegalArgumentException::class.java) + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.java b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.java deleted file mode 100644 index 3cd3023b..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class AlimTalkWeblinkButtonTemplateTest { - - @Test - void getUri() { - var template = AlimTalkWeblinkButtonTemplate.MENU_PREVIEW; // 필요 변수 1개 - - assertThatCode(() -> template.createButton("value1")) - .doesNotThrowAnyException(); - } - - @Test - void getUriFail() { - var template = AlimTalkWeblinkButtonTemplate.MENU_PREVIEW; // 필요 변수 1개 - - assertThatThrownBy(() -> template.createButton("value1", "value2")) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.kt b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.kt new file mode 100644 index 00000000..89f98226 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/AlimTalkWeblinkButtonTemplateTest.kt @@ -0,0 +1,33 @@ +package com.everyonewaiter.domain.notification + +import com.everyonewaiter.domain.support.WordCounter +import org.assertj.core.api.Assertions.assertThatCode +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class AlimTalkWeblinkButtonTemplateTest { + + @Test + fun `알림톡 버튼 생성`() { + AlimTalkWeblinkButtonTemplate.entries.forEach { + val parameterCount = WordCounter.count("%s", it.uri) + if (parameterCount > 0) { + assertThatCode { + it.createButton(*Array(parameterCount) { "parameter" }) + }.doesNotThrowAnyException() + } else { + assertThatCode { it.createButton() }.doesNotThrowAnyException() + } + } + } + + @Test + fun `매개변수 개수가 옳바르지 않은 경우 알림톡 버튼 생성 실패`() { + AlimTalkWeblinkButtonTemplate.entries.forEach { + val parameterCount = WordCounter.count("%s", it.uri) + assertThatThrownBy { + it.createButton(*Array(parameterCount + 1) { "parameter" }) + }.isInstanceOf(IllegalArgumentException::class.java) + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.java b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.java deleted file mode 100644 index dcf08eef..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.NotificationFixture.createDiscordEmbed; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class DiscordEmbedTest { - - @Test - void addField() { - DiscordEmbed embed = createDiscordEmbed(); - - embed.addField(new DiscordField("이름", "내용")); - - assertThat(embed.getFields()).hasSize(1); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.kt b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.kt new file mode 100644 index 00000000..b68663dd --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedTest.kt @@ -0,0 +1,16 @@ +package com.everyonewaiter.domain.notification + +import org.assertj.core.api.Assertions.assertThat +import kotlin.test.Test + +class DiscordEmbedTest { + + @Test + fun `디스코드 필드 추가`() { + val embed = createDiscordEmbed() + + embed.addField(DiscordField("이름", "내용")) + + assertThat(embed.fields).hasSize(1) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.java b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.java deleted file mode 100644 index c7fba542..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.NotificationFixture.createDiscordEmbed; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Test; - -class DiscordEmbedsTest { - - @Test - void constructor() { - List embeds = new ArrayList<>(); - embeds.add(createDiscordEmbed()); - embeds.add(createDiscordEmbed()); - - DiscordEmbeds embeds1 = new DiscordEmbeds(embeds); - DiscordEmbeds embeds2 = new DiscordEmbeds(createDiscordEmbed()); - - assertThat(embeds1.embeds()).hasSize(2); - assertThat(embeds2.embeds()).hasSize(1); - } - - @Test - void constructorFail() { - List embeds = new ArrayList<>(); - for (int i = 0; i < 11; i++) { - embeds.add(createDiscordEmbed()); - } - - assertThatThrownBy(() -> new DiscordEmbeds(List.of())) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> new DiscordEmbeds(embeds)) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.kt b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.kt new file mode 100644 index 00000000..51b5bde4 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/DiscordEmbedsTest.kt @@ -0,0 +1,26 @@ +package com.everyonewaiter.domain.notification + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class DiscordEmbedsTest { + + @Test + fun `디스코드 임베드 생성`() { + val embeds1 = DiscordEmbeds(createDiscordEmbed(), createDiscordEmbed()) + + assertThat(embeds1.embeds).hasSize(2) + } + + @Test + fun `디스코드 임베드의 개수가 0이거나 10을 초과하면 생성 실패`() { + assertThatThrownBy { + DiscordEmbeds(emptyList()) + }.isInstanceOf(IllegalArgumentException::class.java) + + assertThatThrownBy { + DiscordEmbeds(List(11) { createDiscordEmbed() }) + }.isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/DiscordFixture.kt b/src/test/java/com/everyonewaiter/domain/notification/DiscordFixture.kt new file mode 100644 index 00000000..175dd8b1 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/DiscordFixture.kt @@ -0,0 +1,9 @@ +package com.everyonewaiter.domain.notification + +fun createDiscordEmbed( + color: DiscordColor = DiscordColor.GREEN, + title: String = "제목", + description: String = "설명" +): DiscordEmbed { + return DiscordEmbed(color, title, description) +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/EmailFixture.kt b/src/test/java/com/everyonewaiter/domain/notification/EmailFixture.kt new file mode 100644 index 00000000..d91e199e --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/EmailFixture.kt @@ -0,0 +1,22 @@ +package com.everyonewaiter.domain.notification + +import com.everyonewaiter.domain.ADMIN_EMAIL +import com.everyonewaiter.domain.shared.Email + +fun createEmailTemplateReader(): EmailTemplateReader { + return EmailTemplateReader { templateName, variables -> + val contents = variables.entries.map { it.toString() }.toList() + "${templateName}: ${contents.joinToString(separator = ",")}" + } +} + +fun createTemplateEmail( + template: EmailTemplate = EmailTemplate.EMAIL_AUTHENTICATION, + email: String = ADMIN_EMAIL, + subject: String = "제목", + variables: Map = mutableMapOf("authenticationUrl" to "https://auth.everyonewaiter.com") +): TemplateEmail { + val templateEmail = TemplateEmail(template, Email(email), subject) + variables.forEach { (key, value) -> templateEmail.addTemplateVariable(key, value) } + return templateEmail +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.java b/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.java deleted file mode 100644 index e91fb19f..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.NotificationFixture.createEmailTemplateReader; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import java.util.HashMap; -import org.junit.jupiter.api.Test; - -class EmailTemplateTest { - - @Test - void createContent() { - EmailTemplate template = EmailTemplate.EMAIL_AUTHENTICATION; // 필요 변수 1개 - - HashMap variables = new HashMap<>(); - variables.put("key", "value"); - - assertThatCode(() -> template.createContent(createEmailTemplateReader(), variables)) - .doesNotThrowAnyException(); - } - - @Test - void createContentFail() { - EmailTemplate template = EmailTemplate.EMAIL_AUTHENTICATION; // 필요 변수 1개 - - HashMap variables = new HashMap<>(); - variables.put("key1", "value1"); - variables.put("key2", "value2"); - - assertThatThrownBy(() -> template.createContent(createEmailTemplateReader(), variables)) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.kt b/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.kt new file mode 100644 index 00000000..d3a4bafe --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/EmailTemplateTest.kt @@ -0,0 +1,40 @@ +package com.everyonewaiter.domain.notification + +import org.assertj.core.api.Assertions.assertThatCode +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class EmailTemplateTest { + + @Test + fun `이메일 컨텐츠 생성`() { + val templateReader = createEmailTemplateReader() + + EmailTemplate.entries.forEach { + val variables = mutableMapOf() + for (i in 1..it.variableCount) { + variables["key-$i"] = "value-$i" + } + + assertThatCode { + it.createContent(templateReader, variables) + }.doesNotThrowAnyException() + } + } + + @Test + fun `매개변수 개수가 옳바르지 않은 경우 이메일 컨텐츠 생성 실패`() { + val templateReader = createEmailTemplateReader() + + EmailTemplate.entries.forEach { + val variables = mutableMapOf() + for (i in 1..it.variableCount + 1) { + variables["key-$i"] = "value-$i" + } + + assertThatThrownBy { + it.createContent(templateReader, variables) + }.isInstanceOf(IllegalArgumentException::class.java) + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/notification/NotificationFixture.java b/src/test/java/com/everyonewaiter/domain/notification/NotificationFixture.java deleted file mode 100644 index 42d3a558..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/NotificationFixture.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.AlimTalkTemplate.AUTHENTICATION_CODE; -import static com.everyonewaiter.domain.notification.EmailTemplate.EMAIL_AUTHENTICATION; -import static lombok.AccessLevel.PRIVATE; - -import com.everyonewaiter.domain.shared.Email; -import com.everyonewaiter.domain.shared.PhoneNumber; -import java.util.List; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = PRIVATE) -public final class NotificationFixture { - - public static AlimTalkMessage createAlimTalkMessage() { - return new AlimTalkMessage(AUTHENTICATION_CODE, new PhoneNumber("01044591812"), 123456); - } - - public static SimpleEmail createSimpleEmail() { - return new SimpleEmail("admin@everyonewaiter.com", "handwoong@gmail.com", "제목", "본문"); - } - - public static TemplateEmail createTemplateEmail() { - TemplateEmail templateEmail = new TemplateEmail( - EMAIL_AUTHENTICATION, - new Email("admin@everyonewaiter.com"), - "제목" - ); - - templateEmail.addTemplateVariable("authenticationUrl", "https://auth.everyonewaiter.com"); - - return templateEmail; - } - - public static EmailTemplateReader createEmailTemplateReader() { - return (templateName, variables) -> { - List contents = variables.values() - .stream() - .map(Object::toString) - .toList(); - - return templateName + ": " + String.join(",", contents); - }; - } - - public static DiscordEmbeds createDiscordEmbeds() { - return createDiscordEmbeds(createDiscordEmbed()); - } - - public static DiscordEmbeds createDiscordEmbeds(DiscordEmbed... embeds) { - return new DiscordEmbeds(embeds); - } - - public static DiscordEmbed createDiscordEmbed() { - return new DiscordEmbed(DiscordColor.GREEN, "제목", "설명"); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.java b/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.java deleted file mode 100644 index 03dbdcb8..00000000 --- a/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.everyonewaiter.domain.notification; - -import static com.everyonewaiter.domain.notification.NotificationFixture.createEmailTemplateReader; -import static com.everyonewaiter.domain.notification.NotificationFixture.createTemplateEmail; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class TemplateEmailTest { - - @Test - void addTemplateVariable() { - TemplateEmail templateEmail = createTemplateEmail(); // 템플릿 변수 1개 - - templateEmail.addTemplateVariable("key", "value"); - - assertThat(templateEmail.getTemplateVariables()).hasSize(2); - } - - @Test - void toSimpleEmail() { - TemplateEmail templateEmail = createTemplateEmail(); - - SimpleEmail simpleEmail = templateEmail.toSimpleEmail(createEmailTemplateReader()); - - assertThat(simpleEmail.from()).isEqualTo(templateEmail.getFrom().address()); - assertThat(simpleEmail.to()).isEqualTo(templateEmail.getTo().address()); - assertThat(simpleEmail.subject()).isEqualTo(templateEmail.getSubject()); - - for (Object variable : templateEmail.getTemplateVariables().values()) { - assertThat(simpleEmail.content()).contains(variable.toString()); - } - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.kt b/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.kt new file mode 100644 index 00000000..eed7fe00 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/notification/TemplateEmailTest.kt @@ -0,0 +1,30 @@ +package com.everyonewaiter.domain.notification + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TemplateEmailTest { + + @Test + fun `이메일 템플릿 변수 추가`() { + val templateEmail = createTemplateEmail(variables = mutableMapOf()) + + templateEmail.addTemplateVariable("key", "value") + + assertThat(templateEmail.templateVariables).hasSize(1) + } + + @Test + fun `심플 이메일로 변환`() { + val templateEmail = createTemplateEmail() + + val simpleEmail = templateEmail.toSimpleEmail(createEmailTemplateReader()) + + assertThat(simpleEmail.subject).isEqualTo(templateEmail.subject) + assertThat(simpleEmail.from).isEqualTo(templateEmail.from.address) + assertThat(simpleEmail.to).isEqualTo(templateEmail.to.address) + templateEmail.templateVariables.forEach { (_, value) -> + assertThat(simpleEmail.content).contains(value.toString()) + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.java b/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.java deleted file mode 100644 index 251cbf4a..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class BusinessLicenseTest { - - @Test - void constructor() { - String value = "443-60-00875"; - - BusinessLicense businessLicense = new BusinessLicense(value); - - assertThat(businessLicense.value()).isEqualTo(value); - } - - @Test - void constructorFail() { - assertThatThrownBy(() -> new BusinessLicense("4436000875")) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.kt b/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.kt new file mode 100644 index 00000000..55b203ea --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/BusinessLicenseTest.kt @@ -0,0 +1,24 @@ +package com.everyonewaiter.domain.shared + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class BusinessLicenseTest { + + @Test + fun `사업자 등록 번호 생성`() { + val value = "443-60-00875" + + val businessLicense = BusinessLicense(value) + + assertThat(businessLicense.value).isEqualTo(value) + } + + @Test + fun `사업자 등록 번호의 형식이 옳바르지 않은 경우 생성 실패`() { + val value = "4436000875" + + assertThatThrownBy { BusinessLicense(value) }.isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/EmailTest.java b/src/test/java/com/everyonewaiter/domain/shared/EmailTest.java deleted file mode 100644 index 4d77e908..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/EmailTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class EmailTest { - - @Test - void constructor() { - String address = "admin@everyonewaiter.com"; - - Email email = new Email(address); - - assertThat(email.address()).isEqualTo(address); - } - - @Test - void constructorFail() { - assertThatThrownBy(() -> new Email("admin-everyonewaiter.com")) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/EmailTest.kt b/src/test/java/com/everyonewaiter/domain/shared/EmailTest.kt new file mode 100644 index 00000000..35ec6c8b --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/EmailTest.kt @@ -0,0 +1,25 @@ +package com.everyonewaiter.domain.shared + +import com.everyonewaiter.domain.ADMIN_EMAIL +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class EmailTest { + + @Test + fun `이메일 생성`() { + val address = ADMIN_EMAIL + + val email = Email(address) + + assertThat(email.address).isEqualTo(address) + } + + @Test + fun `이메일 형식이 옳바르지 않은 경우 생성 실패`() { + val address = "admin-everyonewaiter.com" + + assertThatThrownBy { Email(address) }.isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.java b/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.java deleted file mode 100644 index 3b7ce979..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class PaginationTest { - - @Test - void countLimit() { - Pagination pagination1 = new Pagination(1, 20, 5); - Pagination pagination2 = new Pagination(2, 20, 10); - - assertThat(pagination1.countLimit()).isEqualTo(101); - assertThat(pagination2.countLimit()).isEqualTo(221); - } - - @Test - void limit() { - Pagination pagination1 = new Pagination(1, 10); - Pagination pagination2 = new Pagination(1, 20); - - assertThat(pagination1.limit()).isEqualTo(10); - assertThat(pagination2.limit()).isEqualTo(20); - } - - @Test - void offset() { - Pagination pagination1 = new Pagination(1, 10); - Pagination pagination2 = new Pagination(2, 10); - Pagination pagination3 = new Pagination(5, 20); - - assertThat(pagination1.offset()).isZero(); - assertThat(pagination2.offset()).isEqualTo(10); - assertThat(pagination3.offset()).isEqualTo(80); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.kt b/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.kt new file mode 100644 index 00000000..9c772c4c --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PaginationTest.kt @@ -0,0 +1,36 @@ +package com.everyonewaiter.domain.shared + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PaginationTest { + + @Test + fun `빠른 페이지 이동 계산을 위해 조회해야 할 데이터 수`() { + val pagination1 = Pagination(1, 20, 5) + val pagination2 = Pagination(5, 20, 10) + + assertThat(pagination1.countLimit()).isEqualTo(101) + assertThat(pagination2.countLimit()).isEqualTo(281) + } + + @Test + fun `실제로 조회할 데이터 수`() { + val pagination1 = Pagination(1, 10) + val pagination2 = Pagination(1, 20) + + assertThat(pagination1.limit()).isEqualTo(pagination1.size) + assertThat(pagination2.limit()).isEqualTo(pagination2.size) + } + + @Test + fun `데이터 조회 시 건너뛸 행 수`() { + val pagination1 = Pagination(1, 10) + val pagination2 = Pagination(5, 20) + val pagination3 = Pagination(10, 100) + + assertThat(pagination1.offset()).isEqualTo(0) + assertThat(pagination2.offset()).isEqualTo(80) + assertThat(pagination3.offset()).isEqualTo(900) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PagingFixture.kt b/src/test/java/com/everyonewaiter/domain/shared/PagingFixture.kt new file mode 100644 index 00000000..c517afe8 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PagingFixture.kt @@ -0,0 +1,12 @@ +package com.everyonewaiter.domain.shared + +data class PagingExample(val subject: String, val content: String) + +fun createContents(size: Long = 10): List { + return List(size.toInt()) { PagingExample("제목$it", "본문$it") } +} + +fun createPaging(pagination: Pagination, size: Long = 10): Paging { + val contents = createContents(size) + return Paging(contents, size, pagination) +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PagingTest.java b/src/test/java/com/everyonewaiter/domain/shared/PagingTest.java deleted file mode 100644 index fc1452c9..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/PagingTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.Test; - -class PagingTest { - - Pagination firstPage = new Pagination(1, 10); - Pagination secondPage = new Pagination(2, 10); - Pagination tenthPage = new Pagination(10, 10); - - @Test - void hasNextShouldBeTrue() { - List content = createDummyContent(0, 11); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.hasNext()).isTrue(); - } - - @Test - void hasNextShouldBeFalse() { - List content = createDummyContent(0, 10); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.hasNext()).isFalse(); - } - - @Test - void hasPreviousShouldBeTrue() { - List content = createDummyContent(0, 11); - - Paging paging = new Paging<>(content, content.size(), secondPage); - - assertThat(paging.hasPrevious()).isTrue(); - } - - @Test - void hasPreviousShouldBeFalse() { - List content = createDummyContent(0, 10); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.hasPrevious()).isFalse(); - } - - @Test - void isFirstShouldBeTrue() { - List content = createDummyContent(0, 10); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.isFirst()).isTrue(); - } - - @Test - void isFirstShouldBeFalse() { - List content = createDummyContent(0, 11); - - Paging paging = new Paging<>(content, content.size(), secondPage); - - assertThat(paging.isFirst()).isFalse(); - } - - @Test - void isLastShouldBeTrue() { - List content = createDummyContent(0, 10); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.isLast()).isTrue(); - } - - @Test - void isLastShouldBeFalse() { - List content = createDummyContent(0, 11); - - Paging paging = new Paging<>(content, content.size(), firstPage); - - assertThat(paging.isLast()).isFalse(); - } - - @Test - void fastForwardPage() { - List content1 = createDummyContent(0, 30); - List content2 = createDummyContent(0, 100); - - Paging paging1 = new Paging<>(content1, content1.size(), firstPage); - Paging paging2 = new Paging<>(content2, content2.size(), secondPage); - - assertThat(paging1.getFastForwardPage()).isEqualTo(3); - assertThat(paging2.getFastForwardPage()).isEqualTo(7); - } - - @Test - void fastBackwardPage() { - List content1 = createDummyContent(0, 10); - List content2 = createDummyContent(0, 100); - - Paging paging1 = new Paging<>(content1, content1.size(), firstPage); - Paging paging2 = new Paging<>(content2, content2.size(), tenthPage); - - assertThat(paging1.getFastBackwardPage()).isEqualTo(1); - assertThat(paging2.getFastBackwardPage()).isEqualTo(5); - } - - @Test - void map() { - List content = createDummyContent("1", 10); - Paging paging = new Paging<>(content, content.size(), firstPage); - - Paging mappedPaging = paging.map(Integer::parseInt); - - for (Integer i : mappedPaging.getContent()) { - assertThat(i).isEqualTo(1); - } - } - - private List createDummyContent(T content, long size) { - List contents = new ArrayList<>(); - - for (int i = 0; i < size; i++) { - contents.add(content); - } - - return Collections.unmodifiableList(contents); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PagingTest.kt b/src/test/java/com/everyonewaiter/domain/shared/PagingTest.kt new file mode 100644 index 00000000..f87a33c1 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PagingTest.kt @@ -0,0 +1,123 @@ +package com.everyonewaiter.domain.shared + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PagingTest { + + @Test + fun `이전, 다음 페이지 여부`() { + // 현재 페이지 1, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination1 = Pagination(1, 3) + + val paging1 = createPaging(pagination1) + + assertThat(paging1.hasPrevious()).isFalse + assertThat(paging1.hasNext()).isTrue + + // 현재 페이지 1, 페이지에 보여줄 컨텐츠 사이즈 10 + val pagination2 = Pagination(1, 10) + + val paging2 = createPaging(pagination2) + + assertThat(paging2.hasPrevious()).isFalse + assertThat(paging2.hasNext()).isFalse + + // 현재 페이지 2, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination3 = Pagination(2, 3) + + val paging3 = createPaging(pagination3) + + assertThat(paging3.hasPrevious()).isTrue + assertThat(paging3.hasNext()).isTrue + + // 현재 페이지 4, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination4 = Pagination(4, 3) + + val paging4 = createPaging(pagination4) + + assertThat(paging4.hasPrevious()).isTrue + assertThat(paging4.hasNext()).isFalse + } + + @Test + fun `첫번째, 마지막 페이지 여부`() { + // 현재 페이지 1, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination1 = Pagination(1, 3) + + val paging1 = createPaging(pagination1) + + assertThat(paging1.isFirst).isTrue + assertThat(paging1.isLast).isFalse + + // 현재 페이지 1, 페이지에 보여줄 컨텐츠 사이즈 10 + val pagination2 = Pagination(1, 10) + + val paging2 = createPaging(pagination2) + + assertThat(paging2.isFirst).isTrue + assertThat(paging2.isLast).isTrue + + // 현재 페이지 2, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination3 = Pagination(2, 3) + + val paging3 = createPaging(pagination3) + + assertThat(paging3.isFirst).isFalse + assertThat(paging3.isLast).isFalse + + // 현재 페이지 4, 페이지에 보여줄 컨텐츠 사이즈 3 + val pagination4 = Pagination(4, 3) + + val paging4 = createPaging(pagination4) + + assertThat(paging4.isFirst).isFalse + assertThat(paging4.isLast).isTrue + } + + @Test + fun `이전, 다음 빠른 이동 페이지 번호`() { + // 현재 페이지 1, 페이지에 보여줄 컨텐츠 사이즈 1, 빠른 이동 시 이동할 페이지 단위 3 + val pagination1 = Pagination(1, 1, 3) + + val paging1 = createPaging(pagination1) + + assertThat(paging1.fastBackwardPage).isEqualTo(1) + assertThat(paging1.fastForwardPage).isEqualTo(4) + + // 현재 페이지 4, 페이지에 보여줄 컨텐츠 사이즈 1, 빠른 이동 시 이동할 페이지 단위 3 + val pagination2 = Pagination(4, 1, 3) + + val paging2 = createPaging(pagination2) + + assertThat(paging2.fastBackwardPage).isEqualTo(1) + assertThat(paging2.fastForwardPage).isEqualTo(7) + + // 현재 페이지 5, 페이지에 보여줄 컨텐츠 사이즈 1, 빠른 이동 시 이동할 페이지 단위 3 + val pagination3 = Pagination(5, 1, 3) + + val paging3 = createPaging(pagination3) + + assertThat(paging3.fastBackwardPage).isEqualTo(2) + assertThat(paging3.fastForwardPage).isEqualTo(8) + + // 현재 페이지 8, 페이지에 보여줄 컨텐츠 사이즈 1, 빠른 이동 시 이동할 페이지 단위 3 + val pagination4 = Pagination(8, 1, 3) + + val paging4 = createPaging(pagination4) + + assertThat(paging4.fastBackwardPage).isEqualTo(5) + assertThat(paging4.fastForwardPage).isEqualTo(10) + } + + @Test + fun `컨텐츠 타입 변환`() { + val paging = createPaging(Pagination(1, 5)) + + val converted = paging.map { it.subject } + + for ((index, subject) in converted.content.withIndex()) { + assertThat(subject).isEqualTo("제목$index") + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.java b/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.java deleted file mode 100644 index a0a3f047..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import org.junit.jupiter.api.Test; - -class PhoneNumberTest { - - @Test - void constructor() { - String value = "01012345678"; - - PhoneNumber phoneNumber = new PhoneNumber(value); - - assertThat(phoneNumber.value()).isEqualTo(value); - } - - @Test - void constructorFail() { - assertThatThrownBy(() -> new PhoneNumber("010-1234-5678")) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.kt b/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.kt new file mode 100644 index 00000000..8ad59b3d --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PhoneNumberTest.kt @@ -0,0 +1,25 @@ +package com.everyonewaiter.domain.shared + +import com.everyonewaiter.domain.ADMIN_PHONE_NUMBER +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +class PhoneNumberTest { + + @Test + fun `휴대폰 번호 생성`() { + val value = ADMIN_PHONE_NUMBER + + val phoneNumber = PhoneNumber(value) + + assertThat(phoneNumber.value).isEqualTo(value) + } + + @Test + fun `휴대폰 번호 형식이 옳바르지 않은 경우 생성 실패`() { + val value = "010-1234-5678" + + assertThatThrownBy { PhoneNumber(value) }.isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PositionFixture.kt b/src/test/java/com/everyonewaiter/domain/shared/PositionFixture.kt new file mode 100644 index 00000000..24dd3a4a --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PositionFixture.kt @@ -0,0 +1,9 @@ +package com.everyonewaiter.domain.shared + +import org.springframework.test.util.ReflectionTestUtils + +fun createPosition(value: Int = 0): Position { + val position = Position() + ReflectionTestUtils.setField(position, "value", value) + return position +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.java b/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.java deleted file mode 100644 index 0b6829ab..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class PositionMoveTest { - - @Test - void moveNext() { - int movedPosition = PositionMove.NEXT.move(1); - - assertThat(movedPosition).isEqualTo(2); - } - - @Test - void movePrev() { - int movedPosition = PositionMove.PREV.move(1); - - assertThat(movedPosition).isEqualTo(1); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.kt b/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.kt new file mode 100644 index 00000000..a8870497 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PositionMoveTest.kt @@ -0,0 +1,25 @@ +package com.everyonewaiter.domain.shared + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PositionMoveTest { + + @Test + fun `포지션을 n 다음으로 이동`() { + val n = 10 + + val movedPosition = PositionMove.NEXT.move(n) + + assertThat(movedPosition).isEqualTo(n + 1) + } + + @Test + fun `포지션을 n 으로 이동`() { + val n = 10 + + val movedPosition = PositionMove.PREV.move(n) + + assertThat(movedPosition).isEqualTo(n) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PositionTest.java b/src/test/java/com/everyonewaiter/domain/shared/PositionTest.java deleted file mode 100644 index c13d50be..00000000 --- a/src/test/java/com/everyonewaiter/domain/shared/PositionTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.everyonewaiter.domain.shared; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class PositionTest { - - @Test - void next() { - Position nextPosition = Position.next(0); - - assertThat(nextPosition.getValue()).isEqualTo(1); - } - - @Test - void copy() { - Position position = new Position(); // 0 - - Position copiedPosition = Position.copy(position); - - assertThat(copiedPosition).isEqualTo(position); - } - - @Test - void moveNext() { - Position position1 = new Position(); // 0 - Position position2 = Position.next(5);// 6 - - boolean isMoved = position1.move(position2, PositionMove.NEXT); - - assertThat(isMoved).isTrue(); - assertThat(position1.getValue()).isEqualTo(7); - } - - @Test - void movePrev() { - Position position1 = new Position(); // 0 - Position position2 = Position.next(5);// 6 - - boolean isMoved = position1.move(position2, PositionMove.PREV); - - assertThat(isMoved).isTrue(); - assertThat(position1.getValue()).isEqualTo(6); - } - - @Test - void notMove() { - Position position1 = new Position(); // 0 - Position position2 = new Position(); // 0 - - boolean isMoved = position1.move(position2, PositionMove.PREV); - - assertThat(isMoved).isFalse(); - assertThat(position1.getValue()).isZero(); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/shared/PositionTest.kt b/src/test/java/com/everyonewaiter/domain/shared/PositionTest.kt new file mode 100644 index 00000000..33b79d93 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/shared/PositionTest.kt @@ -0,0 +1,66 @@ +package com.everyonewaiter.domain.shared + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PositionTest { + + @Test + fun `n 다음 포지션 생성`() { + val n = 10 + + val position = Position.next(n) + + assertThat(position.value).isEqualTo(n + 1) + } + + @Test + fun `포지션 복사`() { + val position = createPosition(10) + + val copied = Position.copy(position) + + assertThat(copied).isEqualTo(position) + } + + @Test + fun `포지션 이동(NEXT)`() { + val position1 = createPosition(10) + val position2 = createPosition(20) + + val isMoved = position1.move(position2, PositionMove.NEXT) + + assertThat(isMoved).isTrue + assertThat(position1.value).isEqualTo(position2.value + 1) + } + + @Test + fun `포지션 이동(PREV)`() { + val position1 = createPosition(10) + val position2 = createPosition(20) + + val isMoved = position1.move(position2, PositionMove.PREV) + + assertThat(isMoved).isTrue + assertThat(position1.value).isEqualTo(position2.value) + } + + @Test + fun `이동한 포지션이 기존과 같은 경우 이동 여부 false`() { + val position1 = createPosition(10) + val position2 = createPosition(9) + + val isMoved1 = position1.move(position2, PositionMove.NEXT) + + assertThat(isMoved1).isFalse + assertThat(position1.value).isEqualTo(10) + + val position3 = createPosition(10) + val position4 = createPosition(10) + + val isMoved2 = position3.move(position4, PositionMove.PREV) + + assertThat(isMoved2).isFalse + assertThat(position3.value).isEqualTo(10) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.java b/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.java deleted file mode 100644 index f2cdc9a5..00000000 --- a/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.everyonewaiter.domain.support; - -import static com.everyonewaiter.domain.support.TimeZone.ASIA_SEOUL; -import static com.everyonewaiter.domain.support.TimeZone.UTC; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import com.everyonewaiter.domain.shared.BusinessException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import org.junit.jupiter.api.Test; - -class DateConverterTest { - - @Test - void convertToUtcStartInstant() { - String kstDate = "20250101"; - - Instant utcInstant = DateConverter.convertToUtcStartInstant(ASIA_SEOUL, kstDate); - - assertThat(utcInstant).isEqualTo(Instant.parse("2024-12-31T15:00:00Z")); - } - - @Test - void convertToUtcStartInstantInputNull() { - Instant utcInstant = DateConverter.convertToUtcStartInstant(ASIA_SEOUL, null); - - assertThat(utcInstant) - .isEqualTo( - LocalDate.now(ASIA_SEOUL.zoneId()) - .atStartOfDay(ASIA_SEOUL.zoneId()) - .withZoneSameInstant(UTC.zoneId()) - .toInstant() - ); - } - - @Test - void convertToUtcEndInstant() { - String kstDate = "20250101"; - - Instant utcInstant = DateConverter.convertToUtcEndInstant(ASIA_SEOUL, kstDate); - - assertThat(utcInstant) - .isAfter(Instant.parse("2025-01-01T14:59:59Z")) - .isBefore(Instant.parse("2025-01-01T15:00:00Z")); - } - - @Test - void convertToUtcEndInstantInputNull() { - Instant utcInstant = DateConverter.convertToUtcEndInstant(ASIA_SEOUL, null); - - assertThat(utcInstant) - .isEqualTo( - LocalDate.now(ASIA_SEOUL.zoneId()) - .atTime(LocalTime.MAX) - .atZone(ASIA_SEOUL.zoneId()) - .withZoneSameInstant(UTC.zoneId()) - .toInstant() - ); - } - - @Test - void invalidDateFormat() { - String kstDate = "2025-01-01"; - - assertThatThrownBy(() -> DateConverter.convertToUtcStartInstant(ASIA_SEOUL, kstDate)) - .isInstanceOf(BusinessException.class); - assertThatThrownBy(() -> DateConverter.convertToUtcEndInstant(ASIA_SEOUL, kstDate)) - .isInstanceOf(BusinessException.class); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.kt b/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.kt new file mode 100644 index 00000000..ef508257 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/support/DateConverterTest.kt @@ -0,0 +1,67 @@ +package com.everyonewaiter.domain.support + +import com.everyonewaiter.domain.shared.BusinessException +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import java.time.Instant +import java.time.LocalDate +import java.time.LocalTime + +class DateConverterTest { + + @Test + fun `문자열 날짜를 UTC 시작 시간으로 변환`() { + val instant = DateConverter.convertToUtcStartInstant(TimeZone.ASIA_SEOUL, "20250101") + + assertThat(instant).isEqualTo(Instant.parse("2024-12-31T15:00:00Z")) + } + + @Test + fun `문자열 날짜가 NULL이라면 오늘 날짜를 UTC 시작 시간으로 변환`() { + val instant = DateConverter.convertToUtcStartInstant(TimeZone.ASIA_SEOUL, null) + + assertThat(instant).isEqualTo( + LocalDate.now(TimeZone.ASIA_SEOUL.zoneId()) + .atStartOfDay(TimeZone.ASIA_SEOUL.zoneId()) + .withZoneSameInstant(TimeZone.UTC.zoneId()) + .toInstant() + ) + } + + @Test + fun `문자열 날짜의 형식이 옳바르지 않은 경우 UTC 시작 시간으로 변환 실패`() { + assertThatThrownBy { + DateConverter.convertToUtcStartInstant(TimeZone.ASIA_SEOUL, "2025-01-01") + }.isInstanceOf(BusinessException::class.java) + } + + @Test + fun `문자열 날짜를 UTC 마지막 시간으로 변환`() { + val instant = DateConverter.convertToUtcEndInstant(TimeZone.ASIA_SEOUL, "20250101") + + assertThat(instant) + .isAfter(Instant.parse("2025-01-01T14:59:59Z")) + .isBefore(Instant.parse("2025-01-01T15:00:00Z")) + } + + @Test + fun `문자열 날짜가 NULL이라면 오늘 날짜를 UTC 마지막 시간으로 변환`() { + val instant = DateConverter.convertToUtcEndInstant(TimeZone.ASIA_SEOUL, null) + + assertThat(instant).isEqualTo( + LocalDate.now(TimeZone.ASIA_SEOUL.zoneId()) + .atTime(LocalTime.MAX) + .atZone(TimeZone.ASIA_SEOUL.zoneId()) + .withZoneSameInstant(TimeZone.UTC.zoneId()) + .toInstant() + ) + } + + @Test + fun `문자열 날짜의 형식이 옳바르지 않은 경우 UTC 마지막 시간으로 변환 실패`() { + assertThatThrownBy { + DateConverter.convertToUtcEndInstant(TimeZone.ASIA_SEOUL, "2025-01-01") + }.isInstanceOf(BusinessException::class.java) + } +} diff --git a/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.java b/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.java deleted file mode 100644 index c87818f2..00000000 --- a/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.everyonewaiter.domain.support; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; - -import java.time.ZoneId; -import org.junit.jupiter.api.Test; - -class TimeZoneTest { - - @Test - void zoneId() { - TimeZone[] timeZones = TimeZone.values(); - - assertThatCode(() -> { - for (TimeZone timeZone : timeZones) { - ZoneId zoneId = timeZone.zoneId(); - assertThat(zoneId).isEqualTo(ZoneId.of(timeZone.getId())); - } - }).doesNotThrowAnyException(); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.kt b/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.kt new file mode 100644 index 00000000..b8d88811 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/support/TimeZoneTest.kt @@ -0,0 +1,15 @@ +package com.everyonewaiter.domain.support + +import org.assertj.core.api.Assertions.assertThatCode +import org.junit.jupiter.api.Test +import java.time.ZoneId + +class TimeZoneTest { + + @Test + fun `타임존 ID 변환`() { + TimeZone.entries.forEach { + assertThatCode { ZoneId.of(it.id) }.doesNotThrowAnyException() + } + } +} diff --git a/src/test/java/com/everyonewaiter/domain/support/TsidTest.java b/src/test/java/com/everyonewaiter/domain/support/TsidTest.java deleted file mode 100644 index 4c62198e..00000000 --- a/src/test/java/com/everyonewaiter/domain/support/TsidTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.everyonewaiter.domain.support; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; - -class TsidTest { - - private static final int REPEAT_COUNT = 1000; - private static final int CREATE_ID_COUNT = 1000; - - @Test - void nextLong() { - try (ExecutorService executorService = Executors.newFixedThreadPool(10)) { - List>> futures = new ArrayList<>(); - - for (int i = 0; i < REPEAT_COUNT; i++) { - Future> future = executorService.submit(createTsidIds(Tsid::nextLong)); - - futures.add(future); - } - - Set ids = collectFuturesToSet(futures); - assertThat(ids).hasSize(REPEAT_COUNT * CREATE_ID_COUNT); - - executorService.shutdown(); - } - } - - @Test - void nextString() { - try (ExecutorService executorService = Executors.newFixedThreadPool(10)) { - List>> futures = new ArrayList<>(); - - for (int i = 0; i < REPEAT_COUNT; i++) { - Future> future = executorService.submit(createTsidIds(Tsid::nextString)); - - futures.add(future); - } - - Set ids = collectFuturesToSet(futures); - assertThat(ids).hasSize(REPEAT_COUNT * CREATE_ID_COUNT); - - executorService.shutdown(); - } - } - - private Callable> createTsidIds(Supplier idFactory) { - return () -> { - List ids = new ArrayList<>(); - - for (int j = 0; j < CREATE_ID_COUNT; j++) { - ids.add(idFactory.get()); - } - - return ids; - }; - } - - private Set collectFuturesToSet(List>> futures) { - return futures.stream() - .flatMap(future -> { - try { - return future.get().stream(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - }) - .collect(Collectors.toSet()); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/support/TsidTest.kt b/src/test/java/com/everyonewaiter/domain/support/TsidTest.kt new file mode 100644 index 00000000..f363209e --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/support/TsidTest.kt @@ -0,0 +1,74 @@ +package com.everyonewaiter.domain.support + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.util.concurrent.Callable +import java.util.concurrent.ExecutionException +import java.util.concurrent.Executors +import java.util.concurrent.Future +import java.util.function.Supplier +import java.util.stream.Collectors + +class TsidTest { + + @Test + fun `고유 숫자 ID 생성`() { + Executors.newFixedThreadPool(10).use { executorService -> + val futures = mutableListOf>>() + repeat(REPEAT_COUNT) { + val future = executorService.submit(createTsidIds { Tsid.nextLong() }) + futures.add(future) + } + + val ids = collectFuturesToSet(futures) + assertThat(ids).hasSize(REPEAT_COUNT * ID_CREATE_COUNT) + executorService.shutdown() + } + } + + @Test + fun `고유 문자열 ID 생성`() { + Executors.newFixedThreadPool(10).use { executorService -> + val futures = mutableListOf>>() + repeat(REPEAT_COUNT) { + val future = executorService.submit(createTsidIds { Tsid.nextString() }) + futures.add(future) + } + + val ids = collectFuturesToSet(futures) + assertThat(ids).hasSize(REPEAT_COUNT * ID_CREATE_COUNT) + executorService.shutdown() + } + } + + private fun createTsidIds(idFactory: Supplier): Callable> { + return Callable { + val ids = mutableListOf() + repeat(ID_CREATE_COUNT) { + ids.add(idFactory.get()) + } + ids + } + } + + private fun collectFuturesToSet(futures: List>>): Set { + return futures.stream() + .flatMap { + try { + return@flatMap it.get().stream() + } catch (e: InterruptedException) { + throw RuntimeException(e) + } catch (e: ExecutionException) { + throw RuntimeException(e) + } + } + .collect(Collectors.toSet()) + } + + companion object { + + const val REPEAT_COUNT = 1000 + const val ID_CREATE_COUNT = 1000 + } +} + diff --git a/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.java b/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.java deleted file mode 100644 index 18d2264a..00000000 --- a/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.everyonewaiter.domain.support; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class WordCounterTest { - - @Test - void count() { - String content = "%s/menus/preview?storeId=%s"; - - int count = WordCounter.count("%s", content); - - assertThat(count).isEqualTo(2); - } - -} diff --git a/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.kt b/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.kt new file mode 100644 index 00000000..e8e73110 --- /dev/null +++ b/src/test/java/com/everyonewaiter/domain/support/WordCounterTest.kt @@ -0,0 +1,16 @@ +package com.everyonewaiter.domain.support + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class WordCounterTest { + + @Test + fun `단어 개수 찾기`() { + val content = "%s/menus/preview?storeId=%s" + + val count = WordCounter.count("%s", content) + + assertThat(count).isEqualTo(2) + } +}