chore(release): v1.0 #1

Merged
bedroomghost merged 10 commits from develop into main 2025-04-20 16:07:07 +00:00
12 changed files with 288 additions and 2 deletions
Showing only changes of commit ba13f21102 - Show all commits

View File

@ -0,0 +1,11 @@
package com.techivw.webprice.application.ports.in;
import com.techivw.webprice.domain.Price;
import java.time.LocalDateTime;
public interface PriceServicePort {
Price getPriceWithHighestPriorityByDateTimeAndProductIdAndBrandId(LocalDateTime dateTime, Long productId, Long brandId);
}

View File

@ -0,0 +1,12 @@
package com.techivw.webprice.application.ports.out;
import com.techivw.webprice.domain.Price;
import java.time.LocalDateTime;
import java.util.Optional;
public interface PriceRepositoryPort {
Optional<Price> findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(LocalDateTime dateTime, Long productId, Long brandId);
}

View File

@ -0,0 +1,21 @@
package com.techivw.webprice.application.services;
import com.techivw.webprice.application.ports.in.PriceServicePort;
import com.techivw.webprice.application.ports.out.PriceRepositoryPort;
import com.techivw.webprice.domain.Price;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
@AllArgsConstructor
public class PriceService implements PriceServicePort {
PriceRepositoryPort priceRepositoryPort;
@Override
public Price getPriceWithHighestPriorityByDateTimeAndProductIdAndBrandId(LocalDateTime dateTime, Long productId, Long brandId) {
return priceRepositoryPort.findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(dateTime, productId, brandId).orElse(null);
}
}

View File

@ -1 +1,12 @@
spring.application.name=webprice
# Database Configuration
spring.datasource.url=jdbc:h2:mem:webprice
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=admin
spring.datasource.password=admin
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# H2 console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

View File

@ -0,0 +1,23 @@
package com.techivw.webprice.domain;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
public class Price {
private Long id;
private Long brandId;
private LocalDateTime startDate;
private LocalDateTime endDate;
private Long priceList;
private Long productId;
private Integer priority;
private BigDecimal price;
private String currency;
}

View File

@ -0,0 +1,59 @@
package com.techivw.webprice.infrastructure.in.controllers.adapters;
import com.techivw.webprice.application.ports.in.PriceServicePort;
import com.techivw.webprice.domain.Price;
import com.techivw.webprice.infrastructure.in.controllers.model.PriceResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.AllArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@RestController
@AllArgsConstructor
@RequestMapping("/price")
public class PriceControllerAdapter {
private PriceServicePort priceServicePort;
@GetMapping
@Operation(
summary = "Get product price",
description = "Returns the current price for a specific product from a particular brand at a given date and time"
)
@ApiResponse(
responseCode = "200",
description = "Price information found",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = PriceResponse.class))
)
@ApiResponse(responseCode = "404", description = "Price information not found for the given parameters")
@ApiResponse(responseCode = "400", description = "Invalid request parameters")
public ResponseEntity<PriceResponse> getPrice(
@Parameter(description = "Date and time for price lookup (ISO format)", example = "2025-04-30T12:00:00")
@RequestParam(name = "dateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime,
@Parameter(description = "Product ID", example = "35455")
@RequestParam(name = "productId") Long productId,
@Parameter(description = "Brand ID", example = "1")
@RequestParam(name = "brandId") Long brandId) {
Price price = priceServicePort.getPriceWithHighestPriorityByDateTimeAndProductIdAndBrandId(dateTime, productId, brandId);
PriceResponse priceResponse = PriceResponse.builder()
.productId(price.getProductId())
.brandId(price.getBrandId())
.priceList(price.getPriceList())
.startDate(price.getStartDate())
.endDate(price.getEndDate())
.price(price.getPrice())
.build();
return ResponseEntity.ok(priceResponse);
}
}

View File

@ -0,0 +1,17 @@
package com.techivw.webprice.infrastructure.in.controllers.model;
import lombok.Builder;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Builder
public class PriceResponse {
private Long productId;
private Long brandId;
private Long priceList;
private LocalDateTime startDate;
private LocalDateTime endDate;
private BigDecimal price;
}

View File

@ -0,0 +1,20 @@
package com.techivw.webprice.infrastructure.repositories;
import com.techivw.webprice.infrastructure.repositories.model.PriceEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Optional;
@Repository
public interface PriceEntityJPARepository extends JpaRepository<PriceEntity, Long> {
@Query("SELECT p FROM PriceEntity p WHERE " +
"p.productId = :productId " +
"AND p.brandId = :brandId " +
"AND :dateTime BETWEEN p.startDate AND p.endDate " +
"ORDER BY p.priority DESC LIMIT 1")
Optional<PriceEntity> findPriceByDateTimeAndProductIdAndBrandId(LocalDateTime dateTime, Long productId, Long brandId);
}

View File

@ -0,0 +1,28 @@
package com.techivw.webprice.infrastructure.repositories.adapters;
import com.techivw.webprice.application.ports.out.PriceRepositoryPort;
import com.techivw.webprice.domain.Price;
import com.techivw.webprice.infrastructure.repositories.PriceEntityJPARepository;
import com.techivw.webprice.infrastructure.repositories.mappers.PriceEntityMapper;
import com.techivw.webprice.infrastructure.repositories.model.PriceEntity;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Optional;
@Service
@AllArgsConstructor
public class PriceRepositoryAdapter implements PriceRepositoryPort {
private final PriceEntityJPARepository repository;
@Override
public Optional<Price> findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(LocalDateTime dateTime, Long productId, Long brandId) {
Optional<PriceEntity> priceEntity =
repository.findPriceByDateTimeAndProductIdAndBrandId(dateTime, productId, brandId);
return PriceEntityMapper.toOptionalPrice(priceEntity);
}
}

View File

@ -0,0 +1,28 @@
package com.techivw.webprice.infrastructure.repositories.mappers;
import com.techivw.webprice.domain.Price;
import com.techivw.webprice.infrastructure.repositories.model.PriceEntity;
import java.util.Optional;
public class PriceEntityMapper {
public static Price toPrice(PriceEntity priceEntity) {
return Price.builder()
.id(priceEntity.getId())
.brandId(priceEntity.getBrandId())
.startDate(priceEntity.getStartDate())
.endDate(priceEntity.getEndDate())
.priceList(priceEntity.getPriceList())
.productId(priceEntity.getProductId())
.priority(priceEntity.getPriority())
.price(priceEntity.getPrice())
.currency(priceEntity.getCurrency())
.build();
}
public static Optional<Price> toOptionalPrice(Optional<PriceEntity> priceEntity) {
return priceEntity.map(PriceEntityMapper::toPrice);
}
}

View File

@ -0,0 +1,47 @@
package com.techivw.webprice.infrastructure.repositories.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@Table(name = "prices")
public class PriceEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "brand_id")
private Long brandId;
@Column(name = "start_date")
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime startDate;
@Column(name = "end_date")
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime endDate;
@Column(name = "price_list")
private Long priceList;
@Column(name = "product_id")
private Long productId;
@Column(name = "priority")
private Integer priority;
@Column(name = "price")
private BigDecimal price;
@Column(name = "currency")
private String currency;
}

13
pom.xml
View File

@ -38,13 +38,22 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
</project>