diff --git a/README.md b/README.md index e21d5be..f9cb5fc 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Add the following dependency to your `pom.xml`: com.yourcompany.library common-library - 1.0-SNAPSHOT + 1.2.10-SNAPSHOT ``` diff --git a/am-common-cache-service/README.md b/am-common-cache-service/README.md new file mode 100644 index 0000000..0f6485d --- /dev/null +++ b/am-common-cache-service/README.md @@ -0,0 +1,92 @@ +# Redis Service Module + +This module provides Redis caching functionality for the Portfolio Management System. It handles caching of portfolio data, market indices, stock prices, and other related information. + +## Features + +- Caching for Portfolio Analysis and Summary +- Stock Price Caching (Real-time and Historical) +- Market Index Data Caching +- Portfolio Holdings Cache Management +- Redis Health Monitoring +- Redis Metrics Collection + +## Configuration + +The module uses Spring Boot's configuration properties. Key configurations can be set in `application.yml`: + +```yaml +spring: + data: + redis: + redisendpoint: localhost:6379 + portfolio-mover: + ttl: 300 # 5 minutes + key-prefix: "portfolio:mover:" + portfolio-summary: + ttl: 300 # 5 minutes + key-prefix: "portfolio:summary:" + # ... other configurations +``` + +## Services + +- `StockPriceRedisService`: Manages stock price caching +- `MarketIndexIndicesRedisService`: Handles market index data caching +- `PortfolioAnalysisRedisService`: Caches portfolio analysis data +- `PortfolioHoldingsRedisService`: Manages portfolio holdings cache +- `PortfolioSummaryRedisService`: Handles portfolio summary caching + +## Health Monitoring + +The module includes a Redis health indicator that monitors: +- Redis connection status +- Redis version +- Redis mode +- Operation metrics + +## Metrics + +The following metrics are collected: +- Cache hits/misses +- Operation timing +- Redis connection status + +## Usage + +To use this module in your Spring Boot application: + +1. Add the module dependency to your `pom.xml`: +```xml + + com.portfolio + redis-service + ${project.version} + +``` + +2. The module will auto-configure itself through Spring Boot's auto-configuration mechanism. + +3. Inject and use the required services: +```java +@Autowired +private StockPriceRedisService stockPriceRedisService; + +@Autowired +private PortfolioSummaryRedisService portfolioSummaryRedisService; +``` + +## Error Handling + +The module includes comprehensive error handling and logging: +- Redis operation exceptions are wrapped in `RedisOperationException` +- All operations are logged using SLF4J +- Failed operations are tracked in metrics + +## Dependencies + +- Spring Boot +- Spring Data Redis +- Lettuce Redis Client +- Lombok +- Spring Boot Actuator (for metrics and health monitoring) diff --git a/am-common-cache-service/pom.xml b/am-common-cache-service/pom.xml new file mode 100644 index 0000000..7952779 --- /dev/null +++ b/am-common-cache-service/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + + com.portfolio + am-portfolio + 1.2.10-SNAPSHOT + + + redis-service + redis-service + Redis Service for Portfolio Management + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.am.common + am-common-investment-model + ${am.investment.version} + + + + com.am.common + am-common-data-model + ${am.common.version} + + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + true + + + + + diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/RedisServiceAutoConfiguration.java b/am-common-cache-service/src/main/java/com/portfolio/redis/RedisServiceAutoConfiguration.java new file mode 100644 index 0000000..51fd60e --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/RedisServiceAutoConfiguration.java @@ -0,0 +1,13 @@ +package com.portfolio.redis; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; + +import com.portfolio.redis.config.RedisProperties; + +@AutoConfiguration +@ComponentScan(basePackages = "com.portfolio.redis") +@EnableConfigurationProperties(RedisProperties.class) +public class RedisServiceAutoConfiguration { +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/annotation/RedisCache.java b/am-common-cache-service/src/main/java/com/portfolio/redis/annotation/RedisCache.java new file mode 100644 index 0000000..9a1eaf2 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/annotation/RedisCache.java @@ -0,0 +1,13 @@ +package com.portfolio.redis.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RedisCache { + String keyPrefix() default ""; + long ttl() default 300; // Default TTL of 5 minutes +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisConfig.java b/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisConfig.java new file mode 100644 index 0000000..e17303d --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisConfig.java @@ -0,0 +1,63 @@ +package com.portfolio.redis.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.portfolio.redis.model.MarketIndexIndicesCache; +import com.portfolio.redis.model.PortfolioAnalysis; +import com.portfolio.redis.model.PortfolioHoldings; +import com.portfolio.redis.model.PortfolioSummaryV1; +import com.portfolio.redis.model.StockPriceCache; + +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate portfolioAnalysisRedisTemplate(RedisConnectionFactory connectionFactory) { + return createRedisTemplate(connectionFactory, PortfolioAnalysis.class); + } + + @Bean + public RedisTemplate portfolioSummaryRedisTemplate(RedisConnectionFactory connectionFactory) { + return createRedisTemplate(connectionFactory, PortfolioSummaryV1.class); + } + + @Bean + public RedisTemplate portfolioHoldingsRedisTemplate(RedisConnectionFactory connectionFactory) { + return createRedisTemplate(connectionFactory, PortfolioHoldings.class); + } + + @Bean + public RedisTemplate stockPriceRedisTemplate(RedisConnectionFactory connectionFactory) { + return createRedisTemplate(connectionFactory, StockPriceCache.class); + } + + @Bean + public RedisTemplate marketIndexIndicesRedisTemplate(RedisConnectionFactory connectionFactory) { + return createRedisTemplate(connectionFactory, MarketIndexIndicesCache.class); + } + + private RedisTemplate createRedisTemplate(RedisConnectionFactory connectionFactory, Class clazz) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + // Create ObjectMapper with proper configuration + ObjectMapper mapper = new ObjectMapper(); + mapper.findAndRegisterModules(); + + // Create serializer using the recommended approach (avoiding deprecated setObjectMapper) + Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(mapper, clazz); + + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + return template; + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisProperties.java b/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisProperties.java new file mode 100644 index 0000000..8cc6d90 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/config/RedisProperties.java @@ -0,0 +1,62 @@ +package com.portfolio.redis.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import lombok.Data; + +@Data +@Configuration +@ConfigurationProperties(prefix = "spring.data.redis") +public class RedisProperties { + private String redisendpoint; + private PortfolioMover portfolioMover = new PortfolioMover(); + private PortfolioSummary portfolioSummary = new PortfolioSummary(); + private PortfolioHoldings portfolioHoldings = new PortfolioHoldings(); + private MarketIndices marketIndices = new MarketIndices(); + private Stock stock = new Stock(); + + @Data + public static class PortfolioMover { + private Integer ttl; + private String keyPrefix; + } + + @Data + public static class PortfolioSummary { + private Integer ttl; + private String keyPrefix; + } + + @Data + public static class PortfolioHoldings { + private Integer ttl; + private String keyPrefix; + } + + @Data + public static class MarketIndices { + private Integer ttl; + private String keyPrefix; + private Historical historical = new Historical(); + + @Data + public static class Historical { + private String keyPrefix; + private Integer ttl; + } + } + + @Data + public static class Stock { + private Integer ttl; + private String keyPrefix; + private Historical historical = new Historical(); + + @Data + public static class Historical { + private String keyPrefix; + private Integer ttl; + } + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/exception/RedisOperationException.java b/am-common-cache-service/src/main/java/com/portfolio/redis/exception/RedisOperationException.java new file mode 100644 index 0000000..abda03b --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/exception/RedisOperationException.java @@ -0,0 +1,15 @@ +package com.portfolio.redis.exception; + +/** + * Custom exception for Redis operation failures + */ +public class RedisOperationException extends RuntimeException { + + public RedisOperationException(String message) { + super(message); + } + + public RedisOperationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/BrokerPortfolioSummary.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/BrokerPortfolioSummary.java new file mode 100644 index 0000000..5dcf281 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/BrokerPortfolioSummary.java @@ -0,0 +1,25 @@ +package com.portfolio.redis.model; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class BrokerPortfolioSummary { + private Double investmentValue; + private Double currentValue; + private Double totalGainLoss; + private Double totalGainLossPercentage; + private Integer totalAssets; + private LocalDateTime lastUpdated; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/EquityHoldings.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/EquityHoldings.java new file mode 100644 index 0000000..dbc5beb --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/EquityHoldings.java @@ -0,0 +1,40 @@ +package com.portfolio.redis.model; + +import java.util.List; + +import com.am.common.amcommondata.model.enums.BrokerType; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class EquityHoldings { + private String isin; + private String symbol; + private String name; + private Double quantity; + private Double currentValue; + private Double investmentValue; + private Double gainLoss; + private Double gainLossPercentage; + private String sector; + private String marketCap; + private List brokerPortfolios; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class EquityBrokerHolding { + private BrokerType brokerType; + private Double quantity; + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/MarketIndexIndicesCache.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/MarketIndexIndicesCache.java new file mode 100644 index 0000000..77133d3 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/MarketIndexIndicesCache.java @@ -0,0 +1,26 @@ +package com.portfolio.redis.model; + +import java.time.Instant; +import java.time.LocalDateTime; + +import com.am.common.investment.model.equity.MarketIndexIndices; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class MarketIndexIndicesCache { + private String key; + private String indexSymbol; + private String index; + private MarketIndexIndices indexIndices; + private LocalDateTime timestamp; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioAnalysis.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioAnalysis.java new file mode 100644 index 0000000..205d9f7 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioAnalysis.java @@ -0,0 +1,26 @@ +package com.portfolio.redis.model; + +import java.time.LocalDateTime; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class PortfolioAnalysis { + private String portfolioId; + private Double totalValue; + private Double totalGainLoss; + private Double totalGainLossPercentage; + private List equityHoldings; + private LocalDateTime lastUpdated; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioHoldings.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioHoldings.java new file mode 100644 index 0000000..dbf5dfd --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioHoldings.java @@ -0,0 +1,25 @@ +package com.portfolio.redis.model; + +import java.time.LocalDateTime; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class PortfolioHoldings { + private List equityHoldings; + private LocalDateTime lastUpdated; + private Double totalValue; + private Double totalGainLoss; + private Double totalGainLossPercentage; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioSummaryV1.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioSummaryV1.java new file mode 100644 index 0000000..1536935 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/PortfolioSummaryV1.java @@ -0,0 +1,31 @@ +package com.portfolio.redis.model; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import com.am.common.amcommondata.model.enums.BrokerType; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class PortfolioSummaryV1 { + private Double investmentValue; + private Double currentValue; + private Double totalGainLoss; + private Double totalGainLossPercentage; + private Integer totalAssets; + private LocalDateTime lastUpdated; + private Map brokerPortfolios; + private Map> marketCapHoldings; + private Map> sectorialHoldings; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/StockPriceCache.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/StockPriceCache.java new file mode 100644 index 0000000..142b1a9 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/StockPriceCache.java @@ -0,0 +1,28 @@ +package com.portfolio.redis.model; + +import java.time.Instant; + +import com.am.common.investment.model.equity.EquityPrice; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class StockPriceCache { + private String symbol; + private String isin; + private Double price; + private Double change; + private Double changePercent; + private Long volume; + private EquityPrice equityPrice; + private Instant timestamp; +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/model/TimeInterval.java b/am-common-cache-service/src/main/java/com/portfolio/redis/model/TimeInterval.java new file mode 100644 index 0000000..9122c2d --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/model/TimeInterval.java @@ -0,0 +1,34 @@ +package com.portfolio.redis.model; + +import java.time.Duration; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum TimeInterval { + FIVE_MINUTES(Duration.ofMinutes(5), "5m"), + TEN_MINUTES(Duration.ofMinutes(10), "10m"), + FIFTEEN_MINUTES(Duration.ofMinutes(15), "15m"), + THIRTY_MINUTES(Duration.ofMinutes(30), "30m"), + ONE_HOUR(Duration.ofHours(1), "1H"), + ONE_DAY(Duration.ofDays(1), "1D"), + ONE_WEEK(Duration.ofDays(7), "1W"), + ONE_MONTH(Duration.ofDays(30), "1M"), + ONE_YEAR(Duration.ofDays(365), "1Y"), + OVERALL(null, "all"); + + private final Duration duration; + private final String code; + + public static TimeInterval fromCode(String code) { + if (code == null) return OVERALL; + + for (TimeInterval interval : values()) { + if (interval.getCode().equalsIgnoreCase(code)) { + return interval; + } + } + throw new IllegalArgumentException("Invalid time interval code: " + code); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/AbstractRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/AbstractRedisService.java new file mode 100644 index 0000000..50f1214 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/AbstractRedisService.java @@ -0,0 +1,140 @@ +package com.portfolio.redis.service.base; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisTemplate; + +import com.portfolio.redis.exception.RedisOperationException; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public abstract class AbstractRedisService implements RedisOperations { + + protected final RedisTemplate redisTemplate; + + protected abstract String getServiceName(); + protected abstract Duration getDefaultTtl(); + protected abstract K buildKey(Object... parts); + + @Override + public void set(K key, V value, Duration ttl) { + String operation = getServiceName() + ".set"; + + try { + redisTemplate.opsForValue().set(key, value, ttl); + log.debug("[{}] Successfully set value for key: {} with TTL: {}", getServiceName(), key, ttl); + } catch (Exception e) { + log.error("[{}] Error setting value for key {}: {}", getServiceName(), key, e.getMessage(), e); + throw new RedisOperationException("Failed to set value in Redis", e); + } + } + + @Override + public void setBatch(Map entries, Duration ttl) { + String operation = getServiceName() + ".setBatch"; + + try { + redisTemplate.opsForValue().multiSet(entries); + entries.keySet().forEach(key -> + redisTemplate.expire(key, ttl.getSeconds(), TimeUnit.SECONDS)); + log.debug("[{}] Successfully set batch of {} entries", getServiceName(), entries.size()); + } catch (Exception e) { + log.error("[{}] Error setting batch entries: {}", getServiceName(), e.getMessage(), e); + throw new RedisOperationException("Failed to set batch entries in Redis", e); + } + } + + @Override + public Optional get(K key) { + String operation = getServiceName() + ".get"; + + try { + V value = redisTemplate.opsForValue().get(key); + + if (value != null) { + log.debug("[{}] Cache hit for key: {}", getServiceName(), key); + return Optional.of(value); + } else { + log.debug("[{}] Cache miss for key: {}", getServiceName(), key); + return Optional.empty(); + } + } catch (Exception e) { + log.error("[{}] Error retrieving value for key {}: {}", getServiceName(), key, e.getMessage(), e); + throw new RedisOperationException("Failed to get value from Redis", e); + } + } + + @Override + public List getAll(List keys) { + String operation = getServiceName() + ".getAll"; + + try { + return redisTemplate.opsForValue().multiGet(keys); + } catch (Exception e) { + log.error("[{}] Error retrieving multiple values: {}", getServiceName(), e.getMessage(), e); + throw new RedisOperationException("Failed to get multiple values from Redis", e); + } + } + + @Override + public void delete(K key) { + String operation = getServiceName() + ".delete"; + + try { + redisTemplate.delete(key); + log.debug("[{}] Successfully deleted key: {}", getServiceName(), key); + } catch (Exception e) { + log.error("[{}] Error deleting key {}: {}", getServiceName(), key, e.getMessage(), e); + throw new RedisOperationException("Failed to delete key from Redis", e); + } + } + + @Override + public void deleteBatch(List keys) { + String operation = getServiceName() + ".deleteBatch"; + + try { + redisTemplate.delete(keys); + log.debug("[{}] Successfully deleted {} keys", getServiceName(), keys.size()); + } catch (Exception e) { + log.error("[{}] Error deleting batch of keys: {}", getServiceName(), e.getMessage(), e); + throw new RedisOperationException("Failed to delete batch of keys from Redis", e); + } + } + + @Override + public boolean exists(K key) { + String operation = getServiceName() + ".exists"; + + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + log.error("[{}] Error checking existence of key {}: {}", getServiceName(), key, e.getMessage(), e); + throw new RedisOperationException("Failed to check key existence in Redis", e); + } + } + + @Override + public Duration getTimeToLive(K key) { + String operation = getServiceName() + ".getTimeToLive"; + + try { + Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS); + return ttl != null && ttl > 0 ? Duration.ofSeconds(ttl) : Duration.ZERO; + } catch (Exception e) { + log.error("[{}] Error getting TTL for key {}: {}", getServiceName(), key, e.getMessage(), e); + throw new RedisOperationException("Failed to get TTL from Redis", e); + } + } + + protected Duration getEffectiveTtl(Duration requestedTtl) { + return requestedTtl != null ? requestedTtl : getDefaultTtl(); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/RedisOperations.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/RedisOperations.java new file mode 100644 index 0000000..a14ca7e --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/base/RedisOperations.java @@ -0,0 +1,17 @@ +package com.portfolio.redis.service.base; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface RedisOperations { + void set(K key, V value, Duration ttl); + void setBatch(Map entries, Duration ttl); + Optional get(K key); + List getAll(List keys); + void delete(K key); + void deleteBatch(List keys); + boolean exists(K key); + Duration getTimeToLive(K key); +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/MarketIndexIndicesRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/MarketIndexIndicesRedisService.java new file mode 100644 index 0000000..a077853 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/MarketIndexIndicesRedisService.java @@ -0,0 +1,162 @@ +// package com.portfolio.redis.service.impl; + +// import java.time.Duration; +// import java.time.Instant; +// import java.time.LocalDateTime; +// import java.time.ZoneOffset; +// import java.util.ArrayList; +// import java.util.List; +// import java.util.Map; +// import java.util.Optional; +// import java.util.concurrent.CompletableFuture; +// import java.util.stream.Collectors; + +// import org.springframework.beans.factory.annotation.Value; +// import org.springframework.data.redis.core.RedisTemplate; +// import org.springframework.scheduling.annotation.Async; +// import org.springframework.stereotype.Service; + +// import com.am.common.investment.model.equity.MarketIndexIndices; +// import com.portfolio.redis.model.MarketIndexIndicesCache; +// import com.portfolio.redis.service.base.AbstractRedisService; +// import com.portfolio.redis.util.RedisUtils; + +// import lombok.extern.slf4j.Slf4j; + +// @Slf4j +// @Service +// public class MarketIndexIndicesRedisService extends AbstractRedisService { + +// private static final int BATCH_SIZE = 100; + +// @Value("${spring.data.redis.market-indices.ttl}") +// private Integer marketIndicesTtl; + +// @Value("${spring.data.redis.market-indices.key-prefix}") +// private String marketIndicesKeyPrefix; + +// @Value("${spring.data.redis.market-indices.historical.ttl}") +// private Integer marketIndicesHistoricalTtl; + +// @Value("${spring.data.redis.market-indices.historical.key-prefix}") +// private String marketIndicesHistoricalKeyPrefix; + +// public MarketIndexIndicesRedisService( +// RedisTemplate redisTemplate) { +// super(redisTemplate); +// } + +// @Override +// protected String getServiceName() { +// return "MarketIndices"; +// } + +// @Override +// protected Duration getDefaultTtl() { +// return Duration.ofSeconds(marketIndicesTtl); +// } + +// @Override +// protected String buildKey(Object... parts) { +// return RedisUtils.buildKey(marketIndicesKeyPrefix, parts); +// } + +// @Async +// public CompletableFuture cacheMarketIndexUpdateBatch(List indexUpdates) { +// return CompletableFuture.runAsync(() -> { +// if (indexUpdates == null || indexUpdates.isEmpty()) { +// log.warn("Received empty or null market index updates batch"); +// return; +// } + +// log.info("Starting to process {} market index updates", indexUpdates.size()); + +// // Process updates in batches +// for (int i = 0; i < indexUpdates.size(); i += BATCH_SIZE) { +// int end = Math.min(i + BATCH_SIZE, indexUpdates.size()); +// List batch = indexUpdates.subList(i, end); +// processBatch(batch); +// } +// }); +// } + +// private void processBatch(List batch) { +// try { +// // Prepare realtime updates +// Map realtimeUpdates = batch.stream() +// .map(this::convertToMarketIndexIndicesCache) +// .collect(Collectors.toMap( +// index -> buildKey(index.getIndexSymbol()), +// index -> index +// )); + +// // Prepare historical updates +// Map historicalUpdates = batch.stream() +// .map(this::convertToMarketIndexIndicesCache) +// .collect(Collectors.toMap( +// index -> buildHistoricalKey(index.getIndexSymbol(), LocalDateTime.now().toInstant(ZoneOffset.UTC)), +// index -> index +// )); + +// // Cache realtime updates +// setBatch(realtimeUpdates, getDefaultTtl()); + +// // Cache historical updates +// setBatch(historicalUpdates, Duration.ofSeconds(marketIndicesHistoricalTtl)); +// } catch (Exception e) { +// log.error("Error processing market index update batch: {}", e.getMessage(), e); +// } +// } + +// public List getHistoricalPrices(String symbol, LocalDateTime startTime, LocalDateTime endTime) { +// try { +// String pattern = marketIndicesHistoricalKeyPrefix + symbol + ":*"; +// List historicalPrices = new ArrayList<>(); + +// redisTemplate.keys(pattern).stream() +// .map(key -> Optional.ofNullable(get(key).orElse(null))) +// .filter(Optional::isPresent) +// .map(Optional::get) +// .filter(index -> isWithinTimeRange(LocalDateTime.now().toInstant(ZoneOffset.UTC), startTime, endTime)) +// .forEach(historicalPrices::add); + +// return historicalPrices; +// } catch (Exception e) { +// log.error("Error retrieving historical prices for symbol {}: {}", symbol, e.getMessage(), e); +// return new ArrayList<>(); +// } +// } + +// public void deleteOldPrices(String symbol, LocalDateTime beforeTime) { +// try { +// String pattern = marketIndicesHistoricalKeyPrefix + symbol + ":*"; +// redisTemplate.keys(pattern).stream() +// .map(key -> Map.entry(key, get(key))) +// .filter(entry -> entry.getValue().isPresent()) +// .filter(entry -> LocalDateTime.now().isBefore(beforeTime)) +// .forEach(entry -> delete(entry.getKey())); +// } catch (Exception e) { +// log.error("Error deleting old market index data for symbol {}: {}", symbol, e.getMessage(), e); +// } +// } + +// private MarketIndexIndicesCache convertToMarketIndexIndicesCache(MarketIndexIndices index) { +// return MarketIndexIndicesCache.builder() +// .key(index.getIndexSymbol()) +// .indexSymbol(index.getIndexSymbol()) +// .index(index.getIndex()) +// .indexIndices(index) +// .timestamp(index.getTimestamp()) +// .build(); +// } + +// private String buildHistoricalKey(String symbol, Instant timestamp) { +// return RedisUtils.buildKey(marketIndicesHistoricalKeyPrefix, symbol, String.valueOf(timestamp.toEpochMilli())); +// } + +// private boolean isWithinTimeRange(Instant timestamp, LocalDateTime startTime, LocalDateTime endTime) { +// Instant startInstant = startTime.toInstant(ZoneOffset.UTC); +// Instant endInstant = endTime.toInstant(ZoneOffset.UTC); +// return !timestamp.isBefore(startInstant) && !timestamp.isAfter(endInstant); +// } +// } diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioAnalysisRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioAnalysisRedisService.java new file mode 100644 index 0000000..644991d --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioAnalysisRedisService.java @@ -0,0 +1,203 @@ +package com.portfolio.redis.service.impl; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.portfolio.redis.model.TimeInterval; +import com.portfolio.redis.model.PortfolioAnalysis; +import com.portfolio.redis.service.base.AbstractRedisService; +import com.portfolio.redis.util.RedisUtils; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class PortfolioAnalysisRedisService extends AbstractRedisService { + + private static final int BATCH_SIZE = 100; + + @Value("${spring.data.redis.portfolio-analysis.ttl}") + private Integer analysisTtl; + + @Value("${spring.data.redis.portfolio-analysis.key-prefix}") + private String analysisKeyPrefix; + + @Value("${spring.data.redis.portfolio-analysis.historical.key-prefix}") + private String historicalKeyPrefix; + + @Value("${spring.data.redis.portfolio-analysis.historical.ttl}") + private Integer historicalTtl; + + public PortfolioAnalysisRedisService( + RedisTemplate redisTemplate) { + super(redisTemplate); + } + + @Override + protected String getServiceName() { + return "PortfolioAnalysis"; + } + + @Override + protected Duration getDefaultTtl() { + return Duration.ofSeconds(analysisTtl); + } + + @Override + protected String buildKey(Object... parts) { + return RedisUtils.buildKey(analysisKeyPrefix, parts); + } + + @Async + public CompletableFuture cachePortfolioAnalysis(PortfolioAnalysis analysis, String userId, TimeInterval interval) { + log.info("Starting async caching of portfolio analysis - User: {}, Interval: {}", + userId, interval != null ? interval.getCode() : "null"); + + return CompletableFuture.runAsync(() -> { + String key = buildKey(userId, interval != null ? interval.getCode() : "all"); + Duration ttl = getEffectiveTtl(interval); + + set(key, analysis, ttl); + log.info("Successfully cached portfolio analysis - User: {}, Key: {}, TTL: {} seconds", + userId, key, ttl.getSeconds()); + }); + } + + public Optional getLatestAnalysis(String userId, TimeInterval interval) { + log.info("Retrieving latest portfolio analysis - User: {}, Interval: {}", + userId, interval != null ? interval.getCode() : "null"); + + String key = buildKey(userId, interval != null ? interval.getCode() : "all"); + Optional analysis = get(key); + + if (analysis.isPresent() && interval != null && interval.getDuration() != null) { + // Check if the analysis is still fresh (within the interval duration) + Instant cutoff = Instant.now().minus(interval.getDuration()); + + if (analysis.get().getLastUpdated().toInstant(ZoneOffset.UTC).isAfter(cutoff)) { + log.info("Found fresh portfolio analysis in cache - User: {}, Key: {}, LastUpdated: {}", + userId, key, analysis.get().getLastUpdated()); + return analysis; + } else { + log.info("Found stale portfolio analysis in cache - User: {}, Key: {}, LastUpdated: {}, deleting", + userId, key, analysis.get().getLastUpdated()); + delete(key); + return Optional.empty(); + } + } + + return analysis; + } + + @Async + public CompletableFuture cacheBatchAnalysis(List analyses, String userId, TimeInterval interval) { + return CompletableFuture.runAsync(() -> { + if (analyses == null || analyses.isEmpty()) { + log.warn("Received empty or null analysis batch"); + return; + } + + log.info("Starting to process {} portfolio analyses", analyses.size()); + + // Process updates in batches + for (int i = 0; i < analyses.size(); i += BATCH_SIZE) { + int end = Math.min(i + BATCH_SIZE, analyses.size()); + List batch = analyses.subList(i, end); + processBatch(batch, userId, interval); + } + }); + } + + private void processBatch(List batch, String userId, TimeInterval interval) { + try { + // Prepare realtime updates + Map realtimeUpdates = batch.stream() + .collect(Collectors.toMap( + analysis -> buildKey(userId, interval != null ? interval.getCode() : "all"), + analysis -> analysis + )); + + // Prepare historical updates + Map historicalUpdates = batch.stream() + .collect(Collectors.toMap( + analysis -> buildHistoricalKey(userId, interval, analysis.getLastUpdated().toInstant(ZoneOffset.UTC)), + analysis -> analysis + )); + + // Cache realtime updates + setBatch(realtimeUpdates, getDefaultTtl()); + + // Cache historical updates + setBatch(historicalUpdates, Duration.ofSeconds(historicalTtl)); + } catch (Exception e) { + log.error("Error processing analysis batch: {}", e.getMessage(), e); + } + } + + public List getHistoricalAnalysis(String userId, TimeInterval interval, LocalDateTime startTime, LocalDateTime endTime) { + try { + String pattern = buildHistoricalPattern(userId, interval); + List historicalAnalysis = new ArrayList<>(); + + redisTemplate.keys(pattern).stream() + .map(key -> get(key)) + .filter(Optional::isPresent) + .map(Optional::get) + .filter(analysis -> isWithinTimeRange(analysis.getLastUpdated(), startTime, endTime)) + .forEach(historicalAnalysis::add); + + return historicalAnalysis; + } catch (Exception e) { + log.error("Error retrieving historical analysis for user {}: {}", userId, e.getMessage(), e); + return new ArrayList<>(); + } + } + + public void deleteOldAnalysis(String userId, TimeInterval interval, LocalDateTime beforeTime) { + try { + String pattern = buildHistoricalPattern(userId, interval); + redisTemplate.keys(pattern).stream() + .map(key -> Map.entry(key, get(key))) + .filter(entry -> entry.getValue().isPresent()) + .filter(entry -> entry.getValue().get().getLastUpdated() + .isBefore(beforeTime)) + .forEach(entry -> delete(entry.getKey())); + } catch (Exception e) { + log.error("Error deleting old analysis for user {}: {}", userId, e.getMessage(), e); + } + } + + private String buildHistoricalPattern(String userId, TimeInterval interval) { + return RedisUtils.buildKey(historicalKeyPrefix, userId, interval != null ? interval.getCode() : "all", "*"); + } + + private String buildHistoricalKey(String userId, TimeInterval interval, java.time.Instant timestamp) { + return RedisUtils.buildKey(historicalKeyPrefix, userId, interval != null ? interval.getCode() : "all", String.valueOf(timestamp.toEpochMilli())); + } + + private boolean isWithinTimeRange(LocalDateTime dateTime, LocalDateTime startTime, LocalDateTime endTime) { + return !dateTime.isBefore(startTime) && !dateTime.isAfter(endTime); + } + + private Duration getEffectiveTtl(TimeInterval interval) { + if (interval != null && interval.getDuration() != null) { + Duration intervalDuration = interval.getDuration(); + Duration defaultDuration = getDefaultTtl(); + return intervalDuration.compareTo(defaultDuration) < 0 ? intervalDuration : defaultDuration; + } + return getDefaultTtl(); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioHoldingsRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioHoldingsRedisService.java new file mode 100644 index 0000000..797845f --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioHoldingsRedisService.java @@ -0,0 +1,66 @@ +package com.portfolio.redis.service.impl; + +import java.time.Duration; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import com.portfolio.redis.model.TimeInterval; +import com.portfolio.redis.model.PortfolioHoldings; +import com.portfolio.redis.service.base.AbstractRedisService; +import com.portfolio.redis.util.RedisUtils; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class PortfolioHoldingsRedisService extends AbstractRedisService { + + @Value("${spring.data.redis.portfolio-holdings.ttl}") + private Integer portfolioHoldingsTtl; + + @Value("${spring.data.redis.portfolio-holdings.key-prefix}") + private String portfolioHoldingsKeyPrefix; + + public PortfolioHoldingsRedisService( + RedisTemplate redisTemplate) { + super(redisTemplate); + } + + @Override + protected String getServiceName() { + return "PortfolioHoldings"; + } + + @Override + protected Duration getDefaultTtl() { + return Duration.ofSeconds(portfolioHoldingsTtl); + } + + @Override + protected String buildKey(Object... parts) { + return RedisUtils.buildKey(portfolioHoldingsKeyPrefix, parts); + } + + public void cachePortfolioHoldings(PortfolioHoldings holdings, String userId, TimeInterval interval) { + String key = buildKey(userId, interval != null ? interval.getCode() : "default"); + Duration ttl = getEffectiveTtl(interval); + set(key, holdings, ttl); + } + + public Optional getLatestHoldings(String userId, TimeInterval interval) { + String key = buildKey(userId, interval != null ? interval.getCode() : "default"); + return get(key); + } + + private Duration getEffectiveTtl(TimeInterval interval) { + if (interval != null && interval.getDuration() != null) { + Duration intervalDuration = interval.getDuration(); + Duration defaultDuration = getDefaultTtl(); + return intervalDuration.compareTo(defaultDuration) < 0 ? intervalDuration : defaultDuration; + } + return getDefaultTtl(); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioSummaryRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioSummaryRedisService.java new file mode 100644 index 0000000..a9d7233 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/PortfolioSummaryRedisService.java @@ -0,0 +1,100 @@ +package com.portfolio.redis.service.impl; + +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneOffset; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.portfolio.redis.model.TimeInterval; +import com.portfolio.redis.model.PortfolioSummaryV1; +import com.portfolio.redis.service.base.AbstractRedisService; +import com.portfolio.redis.util.RedisUtils; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class PortfolioSummaryRedisService extends AbstractRedisService { + + @Value("${spring.data.redis.portfolio-summary.ttl}") + private Integer portfolioSummaryTtl; + + @Value("${spring.data.redis.portfolio-summary.key-prefix}") + private String portfolioSummaryKeyPrefix; + + public PortfolioSummaryRedisService( + RedisTemplate redisTemplate) { + super(redisTemplate); + } + + @Override + protected String getServiceName() { + return "PortfolioSummary"; + } + + @Override + protected Duration getDefaultTtl() { + return Duration.ofSeconds(portfolioSummaryTtl); + } + + @Override + protected String buildKey(Object... parts) { + return RedisUtils.buildKey(portfolioSummaryKeyPrefix, parts); + } + + @Async + public CompletableFuture cachePortfolioSummary(PortfolioSummaryV1 summary, String userId, TimeInterval interval) { + log.info("Starting async caching of portfolio summary - User: {}, Interval: {}", + userId, interval != null ? interval.getCode() : "null"); + + return CompletableFuture.runAsync(() -> { + String key = buildKey(userId, interval != null ? interval.getCode() : "all"); + Duration ttl = getEffectiveTtl(interval); + + set(key, summary, ttl); + log.info("Successfully cached portfolio summary - User: {}, Key: {}, TTL: {} seconds", + userId, key, ttl.getSeconds()); + }); + } + + public Optional getLatestSummary(String userId, TimeInterval interval) { + log.info("Retrieving latest portfolio summary - User: {}, Interval: {}", + userId, interval != null ? interval.getCode() : "null"); + + String key = buildKey(userId, interval != null ? interval.getCode() : "all"); + Optional summary = get(key); + + if (summary.isPresent() && interval != null && interval.getDuration() != null) { + // Check if the summary is still fresh (within the interval duration) + Instant cutoff = Instant.now().minus(interval.getDuration()); + + if (summary.get().getLastUpdated().toInstant(ZoneOffset.UTC).isAfter(cutoff)) { + log.info("Found fresh portfolio summary in cache - User: {}, Key: {}, LastUpdated: {}", + userId, key, summary.get().getLastUpdated()); + return summary; + } else { + log.info("Found stale portfolio summary in cache - User: {}, Key: {}, LastUpdated: {}, deleting", + userId, key, summary.get().getLastUpdated()); + delete(key); + return Optional.empty(); + } + } + + return summary; + } + + private Duration getEffectiveTtl(TimeInterval interval) { + if (interval != null && interval.getDuration() != null) { + Duration intervalDuration = interval.getDuration(); + Duration defaultDuration = getDefaultTtl(); + return intervalDuration.compareTo(defaultDuration) < 0 ? intervalDuration : defaultDuration; + } + return getDefaultTtl(); + } +} diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/StockPriceRedisService.java b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/StockPriceRedisService.java new file mode 100644 index 0000000..20f8755 --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/service/impl/StockPriceRedisService.java @@ -0,0 +1,168 @@ +// package com.portfolio.redis.service.impl; + +// import java.time.Duration; +// import java.time.Instant; +// import java.time.LocalDateTime; +// import java.time.ZoneOffset; +// import java.util.ArrayList; +// import java.util.List; +// import java.util.Map; +// import java.util.Optional; +// import java.util.concurrent.CompletableFuture; +// import java.util.stream.Collectors; + +// import org.springframework.beans.factory.annotation.Value; +// import org.springframework.data.redis.core.RedisTemplate; +// import org.springframework.scheduling.annotation.Async; +// import org.springframework.stereotype.Service; + +// import com.am.common.investment.model.equity.EquityPrice; +// import com.portfolio.redis.model.StockPriceCache; +// import com.portfolio.redis.service.base.AbstractRedisService; +// import com.portfolio.redis.util.RedisUtils; + +// import lombok.extern.slf4j.Slf4j; + +// @Slf4j +// @Service +// public class StockPriceRedisService extends AbstractRedisService { + +// private static final int BATCH_SIZE = 100; + +// @Value("${spring.data.redis.stock.ttl}") +// private Integer stockTtl; + +// @Value("${spring.data.redis.stock.key-prefix}") +// private String stockKeyPrefix; + +// @Value("${spring.data.redis.stock.historical.key-prefix}") +// private String historicalKeyPrefix; + +// @Value("${spring.data.redis.stock.historical.ttl}") +// private Integer historicalTtl; + +// public StockPriceRedisService( +// RedisTemplate redisTemplate) { +// super(redisTemplate); +// } + +// @Override +// protected String getServiceName() { +// return "StockPrice"; +// } + +// @Override +// protected Duration getDefaultTtl() { +// return Duration.ofSeconds(stockTtl); +// } + +// @Override +// protected String buildKey(Object... parts) { +// return RedisUtils.buildKey(stockKeyPrefix, parts); +// } + +// @Async +// public CompletableFuture cacheEquityPriceUpdateBatch(List priceUpdates) { +// return CompletableFuture.runAsync(() -> { +// if (priceUpdates == null || priceUpdates.isEmpty()) { +// log.warn("Received empty or null price updates batch"); +// return; +// } + +// log.info("Starting to process {} price updates", priceUpdates.size()); + +// // Process updates in batches +// for (int i = 0; i < priceUpdates.size(); i += BATCH_SIZE) { +// int end = Math.min(i + BATCH_SIZE, priceUpdates.size()); +// List batch = priceUpdates.subList(i, end); +// processBatch(batch); +// } +// }); +// } + +// private void processBatch(List batch) { +// try { +// // Prepare realtime updates +// Map realtimeUpdates = batch.stream() +// .map(this::convertToStockPriceCache) +// .collect(Collectors.toMap( +// price -> buildKey(price.getSymbol()), +// price -> price +// )); + +// // Prepare historical updates +// Map historicalUpdates = batch.stream() +// .map(this::convertToStockPriceCache) +// .collect(Collectors.toMap( +// price -> buildHistoricalKey(price.getSymbol(), price.getTimestamp().toInstant(ZoneOffset.UTC)), +// price -> price +// )); + +// // Cache realtime updates +// setBatch(realtimeUpdates, getDefaultTtl()); + +// // Cache historical updates +// setBatch(historicalUpdates, Duration.ofSeconds(historicalTtl)); +// } catch (Exception e) { +// log.error("Error processing price update batch: {}", e.getMessage(), e); +// } +// } + +// public Optional getLatestPrice(String symbol) { +// return get(buildKey(symbol)); +// } + +// public List getHistoricalPrices(String symbol, LocalDateTime startTime, LocalDateTime endTime) { +// try { +// String pattern = historicalKeyPrefix + symbol + ":*"; +// List historicalPrices = new ArrayList<>(); + +// redisTemplate.keys(pattern).stream() +// .map(key -> get(key)) +// .filter(Optional::isPresent) +// .map(Optional::get) +// .filter(price -> isWithinTimeRange(price.getTimestamp(), startTime, endTime)) +// .forEach(historicalPrices::add); + +// return historicalPrices; +// } catch (Exception e) { +// log.error("Error retrieving historical prices for symbol {}: {}", symbol, e.getMessage(), e); +// return new ArrayList<>(); +// } +// } + +// public void deleteOldPrices(String symbol, Instant beforeTime) { +// try { +// String pattern = historicalKeyPrefix + symbol + ":*"; +// redisTemplate.keys(pattern).stream() +// .map(key -> Map.entry(key, get(key))) +// .filter(entry -> entry.getValue().isPresent()) +// .filter(entry -> entry.getValue().get().getTimestamp() +// .isBefore(beforeTime)) +// .forEach(entry -> delete(entry.getKey())); +// } catch (Exception e) { +// log.error("Error deleting old prices for symbol {}: {}", symbol, e.getMessage(), e); +// } +// } + +// private StockPriceCache convertToStockPriceCache(EquityPrice price) { +// return StockPriceCache.builder() +// .symbol(price.getSymbol()) +// .isin(price.getIsin()) +// .price(price.getValue()) +// .change(price.getPriceChange()) +// .changePercent(price.getPriceChangePercent()) +// .volume(price.getVolume()) +// .equityPrice(price) +// .timestamp(price.getDate().toInstant(ZoneOffset.UTC)) +// .build(); +// } + +// private String buildHistoricalKey(String symbol, Instant timestamp) { +// return RedisUtils.buildKey(historicalKeyPrefix, symbol, String.valueOf(timestamp.toEpochMilli())); +// } + +// private boolean isWithinTimeRange(Instant timestamp, LocalDateTime startTime, LocalDateTime endTime) { +// return !timestamp.isBefore(startTime.toInstant(ZoneOffset.UTC)) && !timestamp.isAfter(endTime.toInstant(ZoneOffset.UTC)); +// } +// } diff --git a/am-common-cache-service/src/main/java/com/portfolio/redis/util/RedisUtils.java b/am-common-cache-service/src/main/java/com/portfolio/redis/util/RedisUtils.java new file mode 100644 index 0000000..3a0947f --- /dev/null +++ b/am-common-cache-service/src/main/java/com/portfolio/redis/util/RedisUtils.java @@ -0,0 +1,67 @@ +package com.portfolio.redis.util; + +import java.time.Duration; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class RedisUtils { + + public static void batchSet(RedisTemplate redisTemplate, Map keyValueMap, Duration ttl) { + try { + redisTemplate.opsForValue().multiSet(keyValueMap); + keyValueMap.keySet().forEach(key -> + redisTemplate.expire(key, ttl.getSeconds(), TimeUnit.SECONDS)); + log.debug("Successfully batch set {} entries with TTL: {} seconds", keyValueMap.size(), ttl.getSeconds()); + } catch (Exception e) { + log.error("Error during batch set operation: {}", e.getMessage(), e); + throw new RuntimeException("Failed to perform batch set operation", e); + } + } + + public static void deleteByPattern(RedisTemplate redisTemplate, String pattern) { + try { + Set keys = redisTemplate.keys(pattern); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + log.debug("Successfully deleted {} keys matching pattern: {}", keys.size(), pattern); + } + } catch (Exception e) { + log.error("Error deleting keys with pattern {}: {}", pattern, e.getMessage(), e); + throw new RuntimeException("Failed to delete keys by pattern", e); + } + } + + public static void setWithTtl(RedisTemplate redisTemplate, String key, T value, Duration ttl) { + try { + redisTemplate.opsForValue().set(key, value, ttl); + log.debug("Successfully set key: {} with TTL: {} seconds", key, ttl.getSeconds()); + } catch (Exception e) { + log.error("Error setting key {} with TTL: {}", key, e.getMessage(), e); + throw new RuntimeException("Failed to set value with TTL", e); + } + } + + public static String buildKey(String prefix, Object... parts) { + StringBuilder key = new StringBuilder(prefix); + for (Object part : parts) { + if (part != null) { + key.append(String.valueOf(part)).append(":"); + } + } + // Remove trailing colon if present + if (key.charAt(key.length() - 1) == ':') { + key.setLength(key.length() - 1); + } + return key.toString(); + } +} diff --git a/am-common-cache-service/src/main/resources/application.yml b/am-common-cache-service/src/main/resources/application.yml new file mode 100644 index 0000000..5d7bca3 --- /dev/null +++ b/am-common-cache-service/src/main/resources/application.yml @@ -0,0 +1,25 @@ +spring: + data: + redis: + redisendpoint: localhost:6379 + portfolio-mover: + ttl: 300 # 5 minutes + key-prefix: "portfolio:mover:" + portfolio-summary: + ttl: 300 # 5 minutes + key-prefix: "portfolio:summary:" + portfolio-holdings: + ttl: 300 # 5 minutes + key-prefix: "portfolio:holdings:" + market-indices: + ttl: 300 # 5 minutes + key-prefix: "market:indices:" + historical: + key-prefix: "market:indices:historical:" + ttl: 604800 # 7 days + stock: + ttl: 300 # 5 minutes + key-prefix: "stock:price:" + historical: + key-prefix: "stock:price:historical:" + ttl: 604800 # 7 days diff --git a/am-common-data-app/pom.xml b/am-common-data-app/pom.xml new file mode 100644 index 0000000..cf514b4 --- /dev/null +++ b/am-common-data-app/pom.xml @@ -0,0 +1,138 @@ + + + 4.0.0 + + + com.am.common + am-common-data-parent + 1.2.10-SNAPSHOT + + + am-common-data-app + AM Common Data Application + + + + + com.am.common + am-common-data-model + ${project.version} + + + + com.am.common + am-common-data-service + ${project.version} + + + com.am.common + am-common-data-mongo + ${project.version} + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-data-mongodb + ${spring-boot.version} + + + + + com.h2database + h2 + 2.1.214 + runtime + + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + test + + + org.testcontainers + mongodb + 1.19.5 + test + + + org.assertj + assertj-core + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + **/*Test.java + **/*Tests.java + **/*ContainerTest.java + + 1 + false + -Xmx1024m + + + + + diff --git a/am-common-data-app/src/main/java/com/am/common/amcommondata/AmCommonDataApplication.java b/am-common-data-app/src/main/java/com/am/common/amcommondata/AmCommonDataApplication.java new file mode 100644 index 0000000..a4b7e3b --- /dev/null +++ b/am-common-data-app/src/main/java/com/am/common/amcommondata/AmCommonDataApplication.java @@ -0,0 +1,28 @@ +package com.am.common.amcommondata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@ComponentScans({ + @ComponentScan("com.am.common.amcommondata"), + @ComponentScan("com.am.common.amcommondata.service"), + @ComponentScan("com.portfolio") +}) +@EnableJpaRepositories(basePackages = { + "com.am.common.amcommondata.repository.asset", + "com.am.common.amcommondata.repository.security" +}) +@EntityScan(basePackages = { + "com.am.common.amcommondata.domain", + "com.am.common.amcommondata.domain.asset" +}) +@SpringBootApplication +public class AmCommonDataApplication { + public static void main(String[] args) { + SpringApplication.run(AmCommonDataApplication.class, args); + } +} diff --git a/am-common-data-app/src/main/java/com/am/common/amcommondata/controller/PortfolioController.java b/am-common-data-app/src/main/java/com/am/common/amcommondata/controller/PortfolioController.java new file mode 100644 index 0000000..4c8fa47 --- /dev/null +++ b/am-common-data-app/src/main/java/com/am/common/amcommondata/controller/PortfolioController.java @@ -0,0 +1,43 @@ +// package com.am.common.amcommondata.controller; + +// import com.am.common.amcommondata.model.PortfolioModel; +// import com.am.common.amcommondata.service.PortfolioService; +// import lombok.RequiredArgsConstructor; +// import org.springframework.http.ResponseEntity; +// import org.springframework.web.bind.annotation.*; + +// import java.util.List; +// import java.util.UUID; + +// @RestController +// @RequestMapping("/portfolios") +// @RequiredArgsConstructor +// public class PortfolioController { +// private final PortfolioService portfolioService; + +// @GetMapping +// public ResponseEntity> getAllPortfolios() { +// return ResponseEntity.ok(portfolioService.getAllPortfolios()); +// } + +// @GetMapping("/{id}") +// public ResponseEntity getPortfolioById(@PathVariable UUID id) { +// return ResponseEntity.ok(portfolioService.getPortfolio(id)); +// } + +// @PostMapping +// public ResponseEntity createPortfolio(@RequestBody PortfolioModel portfolio) { +// return ResponseEntity.ok(portfolioService.createPortfolio(portfolio)); +// } + +// @PutMapping("/{id}") +// public ResponseEntity updatePortfolio(@PathVariable UUID id, @RequestBody PortfolioModel portfolio) { +// return ResponseEntity.ok(portfolioService.updatePortfolio(id, portfolio)); +// } + +// @DeleteMapping("/{id}") +// public ResponseEntity deletePortfolio(@PathVariable UUID id) { +// portfolioService.deletePortfolio(id); +// return ResponseEntity.noContent().build(); +// } +// } diff --git a/am-common-data-app/src/main/java/com/am/common/amcommondata/exception/GlobalExceptionHandler.java b/am-common-data-app/src/main/java/com/am/common/amcommondata/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..ad81a50 --- /dev/null +++ b/am-common-data-app/src/main/java/com/am/common/amcommondata/exception/GlobalExceptionHandler.java @@ -0,0 +1,35 @@ +package com.am.common.amcommondata.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; + +import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity handleRuntimeException(RuntimeException ex, WebRequest request) { + Map body = new LinkedHashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", ex.getMessage()); + body.put("type", ex.getClass().getSimpleName()); + + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleAllExceptions(Exception ex, WebRequest request) { + Map body = new LinkedHashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", "An unexpected error occurred"); + body.put("type", ex.getClass().getSimpleName()); + + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/am-common-data-app/src/main/resources/application.yml b/am-common-data-app/src/main/resources/application.yml new file mode 100644 index 0000000..667cb18 --- /dev/null +++ b/am-common-data-app/src/main/resources/application.yml @@ -0,0 +1,32 @@ +spring: + application: + name: am-common-data + main: + allow-bean-definition-overriding: true + datasource: + url: jdbc:h2:mem:amcommondata + username: sa + password: password + driverClassName: org.h2.Driver + jpa: + database-platform: org.hibernate.dialect.H2Dialect + hibernate: + ddl-auto: update + show-sql: true + h2: + console: + enabled: true + path: /h2-console + flyway: + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + data: + mongodb: + uuid-representation: standard + database: amcommondata + +server: + port: 8080 + servlet: + context-path: /api diff --git a/am-common-data-app/src/main/resources/db/migration/V1__init.sql b/am-common-data-app/src/main/resources/db/migration/V1__init.sql new file mode 100644 index 0000000..c7ad25a --- /dev/null +++ b/am-common-data-app/src/main/resources/db/migration/V1__init.sql @@ -0,0 +1,77 @@ +-- Create portfolios table +CREATE TABLE portfolios ( + id UUID PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + owner VARCHAR(255), + currency VARCHAR(10), + fundType VARCHAR(100) DEFAULT 'DEFAULT', + status VARCHAR(50), + total_value DOUBLE PRECISION, + asset_count INTEGER, + tags TEXT, + notes TEXT, + created_at TIMESTAMP, + updated_at TIMESTAMP, + created_by VARCHAR(255), + updated_by VARCHAR(255), + version INTEGER NOT NULL DEFAULT 0 +); + +-- Create assets table +CREATE TABLE assets ( + id UUID PRIMARY KEY, + symbol VARCHAR(50) NOT NULL, + isin VARCHAR(12), + name VARCHAR(255), + description TEXT, + portfolio_id UUID, + asset_type VARCHAR(50) NOT NULL, + quantity DOUBLE PRECISION, + avg_buying_price DOUBLE PRECISION, + current_value DOUBLE PRECISION, + investment_value DOUBLE PRECISION, + broker_type VARCHAR(100), + exchange VARCHAR(50), + sector VARCHAR(100), + industry VARCHAR(100), + currency VARCHAR(10), + country VARCHAR(100), + tags TEXT, + notes TEXT, + status VARCHAR(50), + purchase_date TIMESTAMP, + maturity_date TIMESTAMP, + created_at TIMESTAMP, + modified_at TIMESTAMP, + FOREIGN KEY (portfolio_id) REFERENCES portfolios(id) +); + +-- Create equity table (subclass of Asset) +CREATE TABLE equity ( + id UUID PRIMARY KEY, + market_cap DOUBLE PRECISION, + pe_ratio DOUBLE PRECISION, + dividend_yield DOUBLE PRECISION, + FOREIGN KEY (id) REFERENCES assets(id) +); + +-- Insert sample portfolio data + + +INSERT INTO portfolios (id, name, description, owner, currency, status, created_at, updated_at) +VALUES + ('123e4567-e89b-12d3-a456-426614174000', 'Growth Portfolio', 'Long-term growth investment portfolio', 'MKU257', 'INR', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('123e4567-e89b-12d3-a456-426614174001', 'Dividend Portfolio', 'Income focused portfolio', 'MKU257', 'INR', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + +-- Insert sample asset data +INSERT INTO assets (id, symbol, name, portfolio_id, asset_type, quantity, avg_buying_price, current_value, currency, status, created_at, modified_at) +VALUES + ('123e4567-e89b-12d3-a456-426614174002', 'AAPL', 'Apple Inc.', '123e4567-e89b-12d3-a456-426614174000', 'EQUITY', 100, 150.00, 175.00, 'USD', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('123e4567-e89b-12d3-a456-426614174003', 'MSFT', 'Microsoft Corporation', '123e4567-e89b-12d3-a456-426614174000', 'EQUITY', 50, 280.00, 310.00, 'USD', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + +-- Insert sample equity data +INSERT INTO equity (id, market_cap, pe_ratio, dividend_yield) +VALUES + ('123e4567-e89b-12d3-a456-426614174002', 2800000000000, 28.5, 0.65), + ('123e4567-e89b-12d3-a456-426614174003', 2300000000000, 32.1, 0.85); diff --git a/am-common-data-app/src/test/java/com/am/common/amcommondata/config/MongoTestConfig.java b/am-common-data-app/src/test/java/com/am/common/amcommondata/config/MongoTestConfig.java new file mode 100644 index 0000000..2358b79 --- /dev/null +++ b/am-common-data-app/src/test/java/com/am/common/amcommondata/config/MongoTestConfig.java @@ -0,0 +1,59 @@ +package com.am.common.amcommondata.config; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.mongodb.MongoDatabaseFactory; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; +import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.utility.DockerImageName; + +import jakarta.annotation.PreDestroy; + +@TestConfiguration +@EnableAutoConfiguration +@EnableMongoRepositories(basePackages = "com.am.common.amcommondata.repository") +public class MongoTestConfig { + private static final MongoDBContainer mongoDBContainer; + + static { + mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:6.0")); + mongoDBContainer.start(); + System.setProperty("spring.data.mongodb.uri", mongoDBContainer.getReplicaSetUrl()); + } + + @Bean + public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter converter) { + return new MongoTemplate(mongoDbFactory, converter); + } + + @Bean + public MongoDatabaseFactory mongoDbFactory() { + return new SimpleMongoClientDatabaseFactory( + System.getProperty("spring.data.mongodb.uri", "mongodb://localhost:27017/test") + ); + } + + // @Bean + // public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory, MongoMappingContext mongoMappingContext, MongoCustomConversions conversions) { + // DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(databaseFactory); + // MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext); + // converter.setTypeMapper(new DefaultMongoTypeMapper(null)); + // converter.setCustomConversions(conversions); + // return converter; + // } + + @PreDestroy + public void cleanup() { + if (mongoDBContainer != null && mongoDBContainer.isRunning()) { + mongoDBContainer.stop(); + } + } +} diff --git a/am-common-data-app/src/test/java/com/am/common/amcommondata/config/TestConfig.java b/am-common-data-app/src/test/java/com/am/common/amcommondata/config/TestConfig.java new file mode 100644 index 0000000..5925269 --- /dev/null +++ b/am-common-data-app/src/test/java/com/am/common/amcommondata/config/TestConfig.java @@ -0,0 +1,48 @@ +package com.am.common.amcommondata.config; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; + +@Configuration +@EnableAutoConfiguration +@ComponentScan(basePackages = { + "com.am.common.amcommondata", + "com.am.common.amcommondata.model", + "com.am.common.amcommondata.service", + "com.portfolio" +}) +@EntityScan(basePackages = { + "com.am.common.amcommondata.domain", + "com.am.common.amcommondata.domain.asset" +}) +@EnableJpaRepositories(basePackages = { + "com.am.common.amcommondata.repository", + "com.am.common.amcommondata.repository.asset" +}) +@Import(MongoTestConfig.class) +public class TestConfig { + // @Container + // static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15.2") + // .withDatabaseName("testdb") + // .withUsername("test") + // .withPassword("test"); + + // static { + // postgres.start(); + // } + + // @DynamicPropertySource + // static void configureProperties(DynamicPropertyRegistry registry) { + // registry.add("spring.datasource.url", postgres::getJdbcUrl); + // registry.add("spring.datasource.username", postgres::getUsername); + // registry.add("spring.datasource.password", postgres::getPassword); + // } +} diff --git a/am-common-data-app/src/test/java/com/am/common/amcommondata/service/PortfolioServiceContainerTest.java b/am-common-data-app/src/test/java/com/am/common/amcommondata/service/PortfolioServiceContainerTest.java new file mode 100644 index 0000000..d6b8080 --- /dev/null +++ b/am-common-data-app/src/test/java/com/am/common/amcommondata/service/PortfolioServiceContainerTest.java @@ -0,0 +1,140 @@ +// package com.am.common.amcommondata.service; + +// import com.am.common.amcommondata.config.TestConfig; +// import com.am.common.amcommondata.model.PortfolioModel; +// import com.am.common.amcommondata.model.asset.AssetModel; +// import com.am.common.amcommondata.model.enums.AssetType; +// import com.am.common.amcommondata.model.enums.BrokerType; +// import com.am.common.amcommondata.model.enums.FundType; +// import com.am.common.amcommondata.repository.portfolio.PortfolioRepository; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.springframework.beans.factory.annotation.Autowired; +// import org.springframework.boot.test.context.SpringBootTest; +// import org.testcontainers.junit.jupiter.Testcontainers; + +// import java.time.LocalDateTime; +// import java.util.HashSet; +// import java.util.List; +// import java.util.Set; + +// import static org.junit.jupiter.api.Assertions.*; + +// @SpringBootTest(classes = TestConfig.class) +// @Testcontainers +// public class PortfolioServiceContainerTest { + +// @Autowired +// private PortfolioService portfolioService; + +// @Autowired +// private PortfolioRepository portfolioRepository; + + +// private PortfolioModel testPortfolio; + +// @BeforeEach +// void setUp() { +// portfolioRepository.deleteAll(); + +// Set assetTypes = new HashSet<>(); +// assetTypes.add("BOND"); +// assetTypes.add("STOCK"); + +// testPortfolio = createTestPortfolio(FundType.DEFAULT); +// } + +// @Test +// void shouldCreatePortfolio() { +// PortfolioModel created = portfolioService.createPortfolio(testPortfolio); + +// assertNotNull(created.getId()); +// assertEquals(testPortfolio.getName(), created.getName()); +// assertEquals(testPortfolio.getDescription(), created.getDescription()); +// assertEquals(testPortfolio.getCreatedBy(), created.getCreatedBy()); +// assertNotNull(created.getAssets()); +// assertEquals(1, created.getAssets().size()); +// } + +// @Test +// void shouldGetPortfolio() { +// PortfolioModel created = portfolioService.createPortfolio(testPortfolio); +// PortfolioModel found = portfolioService.getPortfolioById(created.getId()); + +// assertEquals(created.getId(), found.getId()); +// assertEquals(created.getName(), found.getName()); +// assertEquals(created.getDescription(), found.getDescription()); +// assertEquals(created.getCreatedBy(), found.getCreatedBy()); +// } + +// @Test +// void shouldGetAllPortfolios() { +// portfolioService.createPortfolio(testPortfolio); + +// PortfolioModel secondPortfolio = createTestPortfolio(FundType.EQUITY_LARGE_CAP); +// portfolioService.createPortfolio(secondPortfolio); + +// List portfolios = portfolioService.getPortfoliosByUserId("test-user"); +// assertEquals(2, portfolios.size()); +// } + +// @Test +// void shouldGetPortfoliosByUser() { +// portfolioService.createPortfolio(testPortfolio); + +// List portfolios = portfolioService.getPortfoliosByUserId("test-user"); +// assertFalse(portfolios.isEmpty()); +// assertEquals("test-user", portfolios.get(0).getCreatedBy()); +// } + +// @Test +// void shouldUpdatePortfolio() { +// PortfolioModel created = portfolioService.createPortfolio(testPortfolio); +// created.setName("Updated Portfolio"); +// created.setDescription("Updated Description"); + +// PortfolioModel updated = portfolioService.updatePortfolio(created.getId(), created); +// assertEquals("Updated Portfolio", updated.getName()); +// assertEquals("Updated Description", updated.getDescription()); +// } + +// @Test +// void shouldDeletePortfolio() { +// PortfolioModel created = portfolioService.createPortfolio(testPortfolio); +// portfolioService.deletePortfolio(created.getId()); + +// assertTrue(portfolioRepository.findById(created.getId()).isEmpty()); +// } + +// private PortfolioModel createTestPortfolio(FundType fundType) { +// AssetModel asset = createTestAsset("isin", "symbol", "name", "description", AssetType.EQUITY, null); +// PortfolioModel portfolio = PortfolioModel.builder() +// .name("Test Portfolio") +// .description("Test Portfolio Description") +// .assets(Set.of(asset)) +// .owner("test-user") +// .fundType(fundType) +// .totalValue(100000.0) +// .assetCount(0) +// .status("ACTIVE") +// .createdBy("test-user") +// .createdAt(LocalDateTime.now()) +// .updatedAt(LocalDateTime.now()) +// .build(); +// return portfolio; +// } + +// private AssetModel createTestAsset(String isin, String symbol, String name, String description, AssetType assetType, PortfolioModel portfolio) { +// AssetModel asset = AssetModel.builder() +// .isin(isin) +// .symbol(symbol) +// .name(name) +// .description(description) +// .assetType(assetType) +// .quantity(100.0) +// .avgBuyingPrice(10.0) +// .brokerType(BrokerType.DHAN) +// .build(); +// return asset; +// } +// } diff --git a/am-common-data-app/src/test/java/com/am/common/amcommondata/service/SecurityServiceContainerTest.java b/am-common-data-app/src/test/java/com/am/common/amcommondata/service/SecurityServiceContainerTest.java new file mode 100644 index 0000000..80bee0f --- /dev/null +++ b/am-common-data-app/src/test/java/com/am/common/amcommondata/service/SecurityServiceContainerTest.java @@ -0,0 +1,199 @@ +// package com.am.common.amcommondata.service; + +// import static org.assertj.core.api.Assertions.assertThat; + +// import java.time.LocalDateTime; +// import java.util.HashMap; +// import java.util.List; +// import java.util.Map; +// import java.util.Optional; +// import java.util.UUID; +// import java.util.stream.Collectors; + +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.springframework.beans.factory.annotation.Autowired; +// import org.springframework.boot.test.context.SpringBootTest; +// import org.springframework.test.context.DynamicPropertyRegistry; +// import org.springframework.test.context.DynamicPropertySource; +// import org.testcontainers.containers.MongoDBContainer; +// import org.testcontainers.junit.jupiter.Container; +// import org.testcontainers.junit.jupiter.Testcontainers; +// import org.testcontainers.utility.DockerImageName; + +// import com.am.common.amcommondata.AmCommonDataApplication; +// import com.am.common.amcommondata.model.MarketCapType; +// import com.am.common.amcommondata.model.security.CompanyInfoModel; +// import com.am.common.amcommondata.model.security.SecurityKeyModel; +// import com.am.common.amcommondata.model.security.SecurityMetadataModel; +// import com.am.common.amcommondata.model.security.SecurityModel; + +// @SpringBootTest(classes = AmCommonDataApplication.class) +// @Testcontainers +// class SecurityServiceContainerTest { + +// @Container +// static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:6.0")); + +// @DynamicPropertySource +// static void setProperties(DynamicPropertyRegistry registry) { +// registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); +// } + +// @Autowired +// private SecurityService securityService; + +// @BeforeEach +// void setup() { +// securityService.deleteAll(); +// } + +// @Test +// void shouldSaveAndRetrieveSecurityModel() { +// // Given +// SecurityModel security = createTestSecurityModel(); + +// // When +// SecurityModel savedSecurity = securityService.save(security); +// Optional foundSecurity = securityService.findByIsin(savedSecurity.getKey().getIsin()); +// Optional foundBySymbol = securityService.findBySymbol(security.getKey().getSymbol()); +// Optional foundByIsin = securityService.findByIsin(security.getKey().getIsin()); + +// // Then +// assertThat(foundSecurity).isPresent(); +// SecurityModel retrievedSecurity = foundSecurity.get(); + +// // Verify key info +// assertThat(retrievedSecurity.getKey().getSymbol()).isEqualTo("AAPL"); +// assertThat(retrievedSecurity.getKey().getIsin()).isEqualTo("US0378331005"); + +// // Verify metadata +// assertThat(retrievedSecurity.getMetadata().getSector()).isEqualTo("Technology"); +// assertThat(retrievedSecurity.getMetadata().getIndustry()).isEqualTo("Consumer Electronics"); +// assertThat(retrievedSecurity.getMetadata().getMarketCapValue()).isEqualTo(3000000000000L); + +// // Verify company info +// assertThat(retrievedSecurity.getCompanyInfo().getLegalName()).isEqualTo("Apple Inc."); +// assertThat(retrievedSecurity.getCompanyInfo().getWebsite()).isEqualTo("www.apple.com"); + +// // Verify audit metadata +// assertThat(retrievedSecurity.getAudit()).isNotNull(); +// assertThat(retrievedSecurity.getAudit().getVersion()).isEqualTo(1L); +// assertThat(retrievedSecurity.getAudit().getCreatedAt()).isNotNull(); + +// // Verify index queries work +// assertThat(foundBySymbol).isPresent(); +// assertThat(foundByIsin).isPresent(); +// } + +// @Test +// void shouldFindSecuritiesByMarketCapAndSector() { +// // Given +// SecurityModel apple = createTestSecurityModel(); // 3T market cap + +// SecurityModel microsoft = createTestSecurityModel(); +// microsoft.getKey().setSymbol("MSFT"); +// microsoft.getKey().setIsin("US5949181045"); +// microsoft.getMetadata().setMarketCapValue(Double.valueOf(2800000000000L)); +// microsoft.getCompanyInfo().setLegalName("Microsoft Corporation"); + +// SecurityModel amazon = createTestSecurityModel(); +// amazon.getKey().setSymbol("AMZN"); +// amazon.getKey().setIsin("US0231351067"); +// amazon.getMetadata().setMarketCapValue(Double.valueOf(1500000000000L)); +// amazon.getCompanyInfo().setLegalName("Amazon.com Inc."); + +// SecurityModel walmart = createTestSecurityModel(); +// walmart.getKey().setSymbol("WMT"); +// walmart.getKey().setIsin("US9311421039"); +// walmart.getMetadata().setMarketCapValue(Double.valueOf(400000000000L)); +// walmart.getMetadata().setSector("Consumer Defensive"); +// walmart.getCompanyInfo().setLegalName("Walmart Inc."); + +// securityService.saveAll(List.of(apple, microsoft, amazon, walmart)); + +// // When - Find large tech companies +// List largeTechCompanies = securityService +// .findActiveLargeCapsByMinMarketCapAndSector(2000000000000L, "Technology"); + +// // Then +// assertThat(largeTechCompanies).hasSize(2); +// assertThat(largeTechCompanies).extracting(s -> s.getKey().getSymbol()) +// .containsExactly("AAPL", "MSFT"); +// assertThat(largeTechCompanies).extracting(s -> s.getMetadata().getMarketCapValue()) +// .allMatch(cap -> cap >= 2000000000000L); +// } + +// @Test +// void shouldFindSecuritiesBySymbols() { +// // Given +// SecurityModel apple = createTestSecurityModel(); // AAPL +// apple.setId(UUID.randomUUID()); // Ensure unique ID + +// SecurityModel microsoft = createTestSecurityModel(); +// microsoft.setId(UUID.randomUUID()); // Ensure unique ID +// microsoft.getKey().setSymbol("MSFT"); +// microsoft.getKey().setIsin("US5949181045"); +// microsoft.getCompanyInfo().setLegalName("Microsoft Corporation"); + +// // Save securities individually to avoid batch issues +// securityService.save(apple); +// securityService.save(microsoft); + +// // When - Find securities by symbols +// List foundSecurities = securityService.findBySymbols(List.of("AAPL", "MSFT", "UNKNOWN")); + +// // Then +// assertThat(foundSecurities).isNotNull(); +// assertThat(foundSecurities.size()).isEqualTo(2); + +// // Extract symbols for easier assertion +// List foundSymbols = foundSecurities.stream() +// .map(s -> s.getKey().getSymbol()) +// .collect(Collectors.toList()); + +// assertThat(foundSymbols).contains("AAPL", "MSFT"); + +// // Verify company names by finding each security in the result list +// for (SecurityModel security : foundSecurities) { +// String symbol = security.getKey().getSymbol(); +// if ("AAPL".equals(symbol)) { +// assertThat(security.getCompanyInfo().getLegalName()).isEqualTo("Apple Inc."); +// } else if ("MSFT".equals(symbol)) { +// assertThat(security.getCompanyInfo().getLegalName()).isEqualTo("Microsoft Corporation"); +// } +// } +// } + +// private SecurityModel createTestSecurityModel() { +// SecurityKeyModel keyInfo = SecurityKeyModel.builder() +// .symbol("AAPL") +// .isin("US0378331005") +// .build(); + +// SecurityMetadataModel metadata = SecurityMetadataModel.builder() +// .sector("Technology") +// .industry("Consumer Electronics") +// .marketCapValue(Double.valueOf(3000000000000L)) +// .marketCapType(MarketCapType.LARGE_CAP) +// .securityType("EQUITY") +// .exchange("NASDAQ") +// .countryOfIncorporation("USA") +// .listingDate(LocalDateTime.now().minusYears(40)) +// .tradingStatus("ACTIVE") +// .customAttributes(new HashMap<>()) +// .build(); + +// CompanyInfoModel companyInfo = CompanyInfoModel.builder() +// .legalName("Apple Inc.") +// .website("www.apple.com") +// .build(); + +// return SecurityModel.builder() +// .id(UUID.randomUUID()) +// .key(keyInfo) +// .metadata(metadata) +// .companyInfo(companyInfo) +// .build(); +// } +// } diff --git a/am-common-data-app/src/test/resources/application.properties b/am-common-data-app/src/test/resources/application.properties new file mode 100644 index 0000000..ef797bc --- /dev/null +++ b/am-common-data-app/src/test/resources/application.properties @@ -0,0 +1,4 @@ +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect diff --git a/am-common-data-app/src/test/resources/application.yml b/am-common-data-app/src/test/resources/application.yml new file mode 100644 index 0000000..61f1466 --- /dev/null +++ b/am-common-data-app/src/test/resources/application.yml @@ -0,0 +1,21 @@ +spring: + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + data: + mongodb: + uuid-representation: standard + test: + database: + replace: none + +logging: + level: + root: INFO + org.springframework: INFO + org.hibernate: INFO + org.testcontainers: INFO diff --git a/am-common-data-app/src/test/resources/logback-test.xml b/am-common-data-app/src/test/resources/logback-test.xml new file mode 100644 index 0000000..879186d --- /dev/null +++ b/am-common-data-app/src/test/resources/logback-test.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/am-common-data-model/pom.xml b/am-common-data-model/pom.xml index 325d5d3..aaa8595 100644 --- a/am-common-data-model/pom.xml +++ b/am-common-data-model/pom.xml @@ -7,7 +7,8 @@ com.am.common am-common-data-parent - 1.2.0-SNAPSHOT + 1.2.10-SNAPSHOT + am-common-data-model diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModel.java index 59d1043..c1a864f 100644 --- a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModel.java +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModel.java @@ -7,9 +7,10 @@ import java.time.LocalDateTime; import java.util.Set; import java.util.UUID; -import java.util.HashSet; import com.am.common.amcommondata.model.asset.AssetModel; +import com.am.common.amcommondata.model.enums.BrokerType; +import com.am.common.amcommondata.model.enums.FundType; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -22,15 +23,19 @@ public class PortfolioModel { private UUID id; private String name; private String description; + private String owner; + private String currency; + private FundType fundType; + private String status; + private String tags; + private String notes; private Set assets; + private Double totalValue; + private BrokerType brokerType; + private Integer assetCount; private LocalDateTime createdAt; private LocalDateTime updatedAt; private String createdBy; private String updatedBy; - private Double totalValue; - private Integer assetCount; - private String status; - - @Builder.Default - private Set assetTypes = new HashSet<>(); + private Long version; } diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModelV1.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModelV1.java new file mode 100644 index 0000000..02a8fa4 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/PortfolioModelV1.java @@ -0,0 +1,41 @@ +package com.am.common.amcommondata.model; + +import lombok.Data; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import com.am.common.amcommondata.model.asset.equity.EquityModel; +import com.am.common.amcommondata.model.enums.BrokerType; +import com.am.common.amcommondata.model.enums.FundType; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +public class PortfolioModelV1 { + private UUID id; + private String name; + private String description; + private String owner; + private String currency; + private FundType fundType; + private String status; + private String tags; + private String notes; + private List equityModels; + private Double totalValue; + private BrokerType brokerType; + private Integer assetCount; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private String createdBy; + private String updatedBy; + private Long version; +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/AssetModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/AssetModel.java index 4fdbc2e..3c202ab 100644 --- a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/AssetModel.java +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/AssetModel.java @@ -1,21 +1,23 @@ package com.am.common.amcommondata.model.asset; import com.am.common.amcommondata.model.enums.AssetType; +import com.am.common.amcommondata.model.enums.BrokerType; import com.am.common.amcommondata.model.asset.common.MarketDataModel; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import java.time.LocalDateTime; import java.util.UUID; @Data @SuperBuilder +@NoArgsConstructor @JsonInclude(Include.NON_NULL) public class AssetModel { private UUID id; - private String isin; private String symbol; private String name; private String description; @@ -25,7 +27,7 @@ public class AssetModel { private Double avgBuyingPrice; private Double currentValue; private Double investmentValue; - private String buyingPlatform; + private BrokerType brokerType; private Double profitLoss; private Double profitLossPercentage; private LocalDateTime createdAt; diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/common/TimeSeriesDataModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/common/TimeSeriesDataModel.java index 2a1c4f7..b6cd0c2 100644 --- a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/common/TimeSeriesDataModel.java +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/common/TimeSeriesDataModel.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.Builder; import java.time.LocalDateTime; +import java.util.UUID; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -11,7 +12,7 @@ @Builder @JsonInclude(Include.NON_NULL) public class TimeSeriesDataModel { - private Long id; + private UUID id; private LocalDateTime timestamp; private String timeframe; private OHLCVModel ohlcv; diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/equity/EquityModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/equity/EquityModel.java index 23c001c..88fcbff 100644 --- a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/equity/EquityModel.java +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/equity/EquityModel.java @@ -6,17 +6,20 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @SuperBuilder +@NoArgsConstructor @EqualsAndHashCode(callSuper = true) @JsonInclude(Include.NON_NULL) public class EquityModel extends AssetModel { + private String isin; private String companyName; private String sector; private String industry; - private Double marketCap; + private String marketCap; private String exchange; private Double peRatio; private Double pbRatio; @@ -24,6 +27,5 @@ public class EquityModel extends AssetModel { private Double eps; private Integer sharesOutstanding; private String stockType; // Common, Preferred - private String isin; private String countryOfIncorporation; } diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/mutualfund/MutualFundModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/mutualfund/MutualFundModel.java index a0d8031..41fa1d2 100644 --- a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/mutualfund/MutualFundModel.java +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/asset/mutualfund/MutualFundModel.java @@ -6,14 +6,17 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import java.time.LocalDate; @Data @SuperBuilder +@NoArgsConstructor @EqualsAndHashCode(callSuper = true) @JsonInclude(Include.NON_NULL) public class MutualFundModel extends AssetModel { + private String isin; private String fundHouse; private String category; // Equity, Debt, Hybrid, etc. private String subCategory; diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/common/AuditModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/common/AuditModel.java new file mode 100644 index 0000000..5e46ba5 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/common/AuditModel.java @@ -0,0 +1,21 @@ +package com.am.common.amcommondata.model.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuditModel { + private LocalDateTime createdAt; + private String createdBy; + private LocalDateTime updatedAt; + private String updatedBy; + private LocalDate dataDate; +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/Currency.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/Currency.java new file mode 100644 index 0000000..ff1b6b5 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/Currency.java @@ -0,0 +1,15 @@ +package com.am.common.amcommondata.model.enums; + +public enum Currency { + USD, + EUR, + GBP, + JPY, + INR, + CNY, + AUD, + CAD, + CHF, + HKD, + SGD +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/DocumentStatus.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/DocumentStatus.java new file mode 100644 index 0000000..711fc61 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/DocumentStatus.java @@ -0,0 +1,11 @@ +package com.am.common.amcommondata.model.enums; + +public enum DocumentStatus { + ACTIVE, + INACTIVE, + DELETED, + PENDING_APPROVAL, + REJECTED, + ARCHIVED, + DRAFT +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/FundType.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/FundType.java new file mode 100644 index 0000000..9a4a6a4 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/FundType.java @@ -0,0 +1,173 @@ +package com.am.common.amcommondata.model.enums; + +/** + * Enum representing different types of mutual funds with their sub-categories. + */ +public enum FundType { + // Equity Funds + EQUITY_LARGE_CAP("Large Cap Equity Fund", "Equity"), + EQUITY_MID_CAP("Mid Cap Equity Fund", "Equity"), + EQUITY_SMALL_CAP("Small Cap Equity Fund", "Equity"), + EQUITY_MULTI_CAP("Multi Cap Equity Fund", "Equity"), + EQUITY_LARGE_MID_CAP("Large & Mid Cap Equity Fund", "Equity"), + EQUITY_DIVIDEND_YIELD("Dividend Yield Fund", "Equity"), + EQUITY_FOCUSED("Focused Equity Fund", "Equity"), + EQUITY_CONTRA("Contra Fund", "Equity"), + EQUITY_VALUE("Value Fund", "Equity"), + + // Debt Funds + DEBT_ULTRA_SHORT("Ultra Short Duration Fund", "Debt"), + DEBT_LOW_DURATION("Low Duration Fund", "Debt"), + DEBT_SHORT_DURATION("Short Duration Fund", "Debt"), + DEBT_MEDIUM_DURATION("Medium Duration Fund", "Debt"), + DEBT_LONG_DURATION("Long Duration Fund", "Debt"), + DEBT_DYNAMIC_BOND("Dynamic Bond Fund", "Debt"), + DEBT_CORPORATE_BOND("Corporate Bond Fund", "Debt"), + DEBT_CREDIT_RISK("Credit Risk Fund", "Debt"), + DEBT_BANKING_PSU("Banking and PSU Fund", "Debt"), + DEBT_FLOATER("Floater Fund", "Debt"), + + // Hybrid Funds + HYBRID_AGGRESSIVE("Aggressive Hybrid Fund", "Hybrid"), + HYBRID_BALANCED("Balanced Hybrid Fund", "Hybrid"), + HYBRID_CONSERVATIVE("Conservative Hybrid Fund", "Hybrid"), + HYBRID_EQUITY_SAVINGS("Equity Savings Fund", "Hybrid"), + HYBRID_MULTI_ASSET("Multi Asset Allocation Fund", "Hybrid"), + + // Index Funds + INDEX_NIFTY50("Nifty 50 Index Fund", "Index"), + INDEX_SENSEX("Sensex Index Fund", "Index"), + INDEX_NIFTY_NEXT50("Nifty Next 50 Index Fund", "Index"), + INDEX_MIDCAP("Midcap Index Fund", "Index"), + INDEX_SMALLCAP("Smallcap Index Fund", "Index"), + INDEX_SECTORAL("Sectoral Index Fund", "Index"), + + // Sectoral/Thematic Funds + SECTOR_BANKING("Banking Sector Fund", "Sectoral"), + SECTOR_IT("IT Sector Fund", "Sectoral"), + SECTOR_PHARMA("Pharma Sector Fund", "Sectoral"), + SECTOR_FMCG("FMCG Sector Fund", "Sectoral"), + SECTOR_INFRASTRUCTURE("Infrastructure Fund", "Sectoral"), + SECTOR_AUTO("Auto Sector Fund", "Sectoral"), + SECTOR_POWER("Power Sector Fund", "Sectoral"), + SECTOR_METAL("Metal Sector Fund", "Sectoral"), + SECTOR_CONSUMPTION("Consumption Sector Fund", "Sectoral"), + + THEME_ESG("ESG Fund", "Thematic"), + THEME_MNC("MNC Fund", "Thematic"), + THEME_GLOBAL_ADVANTAGE("Global Advantage Fund", "Thematic"), + + // Solution Oriented Funds + SOLUTION_RETIREMENT("Retirement Fund", "Solution"), + SOLUTION_CHILDREN("Children's Fund", "Solution"), + SOLUTION_PENSION("Pension Fund", "Solution"), + + // International Funds + INTL_US_EQUITY("US Equity Fund", "International"), + INTL_EMERGING_MARKETS("Emerging Markets Fund", "International"), + INTL_GLOBAL_EQUITY("Global Equity Fund", "International"), + INTL_EUROPE("European Markets Fund", "International"), + INTL_JAPAN("Japanese Markets Fund", "International"), + INTL_CHINA("China Markets Fund", "International"), + + // Other Fund Types + LIQUID_FUND("Liquid Fund", "Other"), + OVERNIGHT_FUND("Overnight Fund", "Other"), + ARBITRAGE_FUND("Arbitrage Fund", "Other"), + GILT_FUND("Gilt Fund", "Other"), + GILT_CONSTANT_MATURITY("Gilt with Constant Maturity", "Other"), + ELSS_TAX_SAVER("ELSS Tax Saver", "Other"), + FOF_DOMESTIC("Fund of Funds - Domestic", "Other"), + FOF_OVERSEAS("Fund of Funds - Overseas", "Other"), + + //Default Type + DEFAULT("Default", "Other"); + + private final String displayName; + private final String category; + + FundType(String displayName, String category) { + this.displayName = displayName; + this.category = category; + } + + public String getDisplayName() { + return displayName; + } + + public String getCategory() { + return category; + } + + @Override + public String toString() { + return displayName; + } + + public static FundType fromDisplayName(String displayName) { + for (FundType type : values()) { + if (type.getDisplayName().equals(displayName)) { + return type; + } + } + return DEFAULT; + } + + // Helper methods for categories + public boolean isEquity() { + return category.equals("Equity"); + } + + public boolean isDebt() { + return category.equals("Debt"); + } + + public boolean isHybrid() { + return category.equals("Hybrid"); + } + + public boolean isIndex() { + return category.equals("Index"); + } + + public boolean isSectoral() { + return category.equals("Sectoral"); + } + + public boolean isThematic() { + return category.equals("Thematic"); + } + + public boolean isSolution() { + return category.equals("Solution"); + } + + public boolean isInternational() { + return category.equals("International"); + } + + // Helper methods for specific fund types + public boolean isLargeCap() { + return this == EQUITY_LARGE_CAP; + } + + public boolean isMidCap() { + return this == EQUITY_MID_CAP; + } + + public boolean isSmallCap() { + return this == EQUITY_SMALL_CAP; + } + + public boolean isELSS() { + return this == ELSS_TAX_SAVER; + } + + public boolean isLiquid() { + return this == LIQUID_FUND; + } + + public boolean isDefault() { + return this == DEFAULT; + } +} \ No newline at end of file diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/PortfolioStatus.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/PortfolioStatus.java new file mode 100644 index 0000000..fe90a96 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/enums/PortfolioStatus.java @@ -0,0 +1,9 @@ +package com.am.common.amcommondata.model.enums; + +public enum PortfolioStatus { + ACTIVE, + INACTIVE, + ARCHIVED, + PENDING, + DELETED +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/EquityIndexMembershipModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/EquityIndexMembershipModel.java new file mode 100644 index 0000000..863d66e --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/EquityIndexMembershipModel.java @@ -0,0 +1,25 @@ +package com.am.common.amcommondata.model.indices; + +import com.am.common.amcommondata.model.asset.equity.EquityModel; +import com.am.common.amcommondata.model.common.AuditModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.UUID; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EquityIndexMembershipModel { + private UUID id; + private EquityModel equity; + private MarketIndexModel index; + private LocalDate entryDate; + private LocalDate exitDate; + private Double weight; + private AuditModel auditInfo; +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/IndexType.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/IndexType.java new file mode 100644 index 0000000..aa41265 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/IndexType.java @@ -0,0 +1,8 @@ +package com.am.common.amcommondata.model.indices; + +public enum IndexType { + MARKET_CAP, // e.g., NIFTY 50, MIDCAP + SECTOR, // e.g., IT, BANKING + THEMATIC, // e.g., ESG, CONSUMPTION + STRATEGY // e.g., QUALITY, VALUE +} diff --git a/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/MarketIndexModel.java b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/MarketIndexModel.java new file mode 100644 index 0000000..2810582 --- /dev/null +++ b/am-common-data-model/src/main/java/com/am/common/amcommondata/model/indices/MarketIndexModel.java @@ -0,0 +1,25 @@ +package com.am.common.amcommondata.model.indices; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.UUID; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MarketIndexModel { + private String key; + private String index; + private String indexSymbol; + private String code; + private String name; + private String description; + private IndexType indexType; + private boolean active; + private List indexMemberships; +} diff --git a/am-common-data-mongo/pom.xml b/am-common-data-mongo/pom.xml new file mode 100644 index 0000000..16c168c --- /dev/null +++ b/am-common-data-mongo/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.am.common + am-common-data-parent + 1.2.10-SNAPSHOT + + + am-common-data-mongo + + + + com.am.common + am-common-data-model + ${project.version} + + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + org.testcontainers + mongodb + 1.19.5 + test + + + + + org.projectlombok + lombok + provided + + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/AssetDocument.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/AssetDocument.java new file mode 100644 index 0000000..eeef15e --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/AssetDocument.java @@ -0,0 +1,38 @@ +package com.am.common.amcommondata.document.asset; + +import java.time.LocalDateTime; + +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.document.base.BaseDocument; +import com.am.common.amcommondata.model.asset.common.MarketDataModel; +import com.am.common.amcommondata.model.enums.AssetType; +import com.am.common.amcommondata.model.enums.BrokerType; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class AssetDocument extends BaseDocument { + @Field("symbol") + private String symbol; + private String name; + private String description; + private AssetType assetType; + private MarketDataModel marketData; + private Double quantity; + private Double avgBuyingPrice; + private Double currentValue; + private Double investmentValue; + private BrokerType brokerType; + private Double profitLoss; + private Double profitLossPercentage; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private Boolean isActive; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/equity/EquityDocument.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/equity/EquityDocument.java new file mode 100644 index 0000000..7f43d25 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/asset/equity/EquityDocument.java @@ -0,0 +1,32 @@ +package com.am.common.amcommondata.document.asset.equity; + +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.document.asset.AssetDocument; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class EquityDocument extends AssetDocument { + private String isin; + @Field("equity_symbol") + private String symbol; + private String companyName; + private String sector; + private String industry; + private String marketCap; + private String exchange; + private Double peRatio; + private Double pbRatio; + private Double dividendYield; + private Double eps; + private Integer sharesOutstanding; + private String stockType; + private String countryOfIncorporation; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/base/BaseDocument.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/base/BaseDocument.java new file mode 100644 index 0000000..d0b4666 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/base/BaseDocument.java @@ -0,0 +1,34 @@ +package com.am.common.amcommondata.document.base; + +import java.util.UUID; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.document.common.AuditMetadata; +import com.am.common.amcommondata.model.enums.DocumentStatus; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public abstract class BaseDocument { + + @Id + @Field("_id") + private String id; + + @Field("baseStatus") + private DocumentStatus status; + + @Field("audit") + private AuditMetadata audit; + + @Field("parentId") + private String parentId; // Reference to parent document if any, stored as String in MongoDB +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/common/AuditMetadata.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/common/AuditMetadata.java new file mode 100644 index 0000000..faaa1f5 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/common/AuditMetadata.java @@ -0,0 +1,55 @@ +package com.am.common.amcommondata.document.common; + +import java.time.LocalDateTime; + +import org.springframework.data.mongodb.core.mapping.Field; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) + +public class AuditMetadata { + @Field("created_at") + private LocalDateTime createdAt; + + @Field("created_by") + private String createdBy; + + @Field("created_by_email") + private String createdByEmail; + + @Field("updated_at") + private LocalDateTime updatedAt; + + @Field("updated_by") + private String updatedBy; + + @Field("updated_by_email") + private String updatedByEmail; + + @Field("version") + @Builder.Default + private Long version = 1L; + + @Field("last_action") + private String lastAction; // CREATE, UPDATE, VERIFY, APPROVE, etc. + + @Field("last_action_comment") + private String lastActionComment; + + @Field("source_system") + private String sourceSystem; // System that created/updated this record + + @Field("source_reference") + private String sourceReference; // Reference ID in the source system +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/portfolio/PortfolioDocument.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/portfolio/PortfolioDocument.java new file mode 100644 index 0000000..bbdfd87 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/portfolio/PortfolioDocument.java @@ -0,0 +1,87 @@ +package com.am.common.amcommondata.document.portfolio; + +import java.util.List; + +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.document.asset.equity.EquityDocument; +import com.am.common.amcommondata.document.base.BaseDocument; +import com.am.common.amcommondata.model.enums.BrokerType; +import com.am.common.amcommondata.model.enums.Currency; +import com.am.common.amcommondata.model.enums.DocumentStatus; +import com.am.common.amcommondata.model.enums.FundType; +import com.am.common.amcommondata.model.enums.PortfolioStatus; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Document(collection = "portfolios") +public class PortfolioDocument extends BaseDocument { + @Field("name") + private String name; + + @Field("description") + private String description; + + @Field("owner") + private String owner; + + @Field("currency") + private String currency; // Store as String in MongoDB + + @Field("fundType") + private FundType fundType; + + @Field("portfolioStatus") + private PortfolioStatus portfolioStatus; // Store as String in MongoDB + + @Field("tags") + private List tags; + + @Field("notes") + private String notes; + + @Field("equities") + private List equities; + + @Field("totalValue") + private Double totalValue; + + @Field("brokerType") + private BrokerType brokerType; + + // Helper methods for enum conversions + public void setCurrency(Currency currency) { + this.currency = currency != null ? currency.name() : null; + } + + public Currency getCurrency() { + return this.currency != null ? Currency.valueOf(this.currency) : null; + } + + public void setPortfolioStatus(PortfolioStatus status) { + this.portfolioStatus = status != null ? status : null; + } + + public PortfolioStatus getPortfolioStatus() { + return this.portfolioStatus != null ? this.portfolioStatus : null; + } + + // Override base status methods to use DocumentStatus enum + @Override + public void setStatus(DocumentStatus status) { + super.setStatus(status); + } + + @Override + public DocumentStatus getStatus() { + return super.getStatus(); + } +} \ No newline at end of file diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/SecurityDocument.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/SecurityDocument.java new file mode 100644 index 0000000..a9e0ea4 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/SecurityDocument.java @@ -0,0 +1,24 @@ +package com.am.common.amcommondata.document.security; + +import org.springframework.data.mongodb.core.mapping.Document; + +import com.am.common.amcommondata.document.base.BaseDocument; +import com.am.common.amcommondata.document.security.metadata.CompanyInfo; +import com.am.common.amcommondata.document.security.metadata.KeyInfo; +import com.am.common.amcommondata.document.security.metadata.SecurityMetadata; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "securities") +public class SecurityDocument extends BaseDocument { + private KeyInfo key; + private SecurityMetadata metadata; + private CompanyInfo companyInfo; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/CompanyInfo.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/CompanyInfo.java new file mode 100644 index 0000000..f94cd3c --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/CompanyInfo.java @@ -0,0 +1,47 @@ +package com.am.common.amcommondata.document.security.metadata; + +import java.time.LocalDateTime; + +import org.springframework.data.mongodb.core.mapping.Field; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CompanyInfo { + + @Field("legal_name") + private String legalName; + + @Field("symbol") + private String symbol; + + @Field("isin") + private String isin; + + @Field("cin") + private String cin; + + @Field("pan") + private String pan; + + @Field("website") + private String website; + + @Field("email") + private String email; + + @Field("phone_number") + private String phoneNumber; + + @Field("description") + private String description; + + @Field("business_description") + private String businessDescription; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/KeyInfo.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/KeyInfo.java new file mode 100644 index 0000000..084c7bd --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/KeyInfo.java @@ -0,0 +1,21 @@ +package com.am.common.amcommondata.document.security.metadata; + +import org.springframework.data.mongodb.core.mapping.Field; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KeyInfo { + + @Field("symbol") + private String symbol; + + @Field("isin") + private String isin; +} \ No newline at end of file diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/SecurityMetadata.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/SecurityMetadata.java new file mode 100644 index 0000000..9446ece --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/document/security/metadata/SecurityMetadata.java @@ -0,0 +1,57 @@ +package com.am.common.amcommondata.document.security.metadata; + +import java.time.LocalDateTime; + +import java.util.Map; + +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.model.MarketCapType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SecurityMetadata { + + @Field("security_name") + private String securityName; + + @Field("sector") + private String sector; + + @Field("industry") + private String industry; + + @Field("market_cap_value") + private Double marketCapValue; + + @Field("market_cap_type") + private MarketCapType marketCapType; + + @Field("security_type") + private String securityType; + + @Field("exchange") + private String exchange; + + @Field("country_of_incorporation") + private String countryOfIncorporation; + + @Field("listing_date") + private LocalDateTime listingDate; + + @Field("delisting_date") + private LocalDateTime delistingDate; + + @Field("trading_status") + private String tradingStatus; + + @Field("custom_attributes") + private Map customAttributes; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/listener/VersionIncrementEventListener.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/listener/VersionIncrementEventListener.java new file mode 100644 index 0000000..d8edb0e --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/listener/VersionIncrementEventListener.java @@ -0,0 +1,38 @@ +package com.am.common.amcommondata.listener; + +import java.time.LocalDateTime; + +import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; +import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.base.BaseDocument; +import com.am.common.amcommondata.document.common.AuditMetadata; + +@Component +public class VersionIncrementEventListener extends AbstractMongoEventListener { + + @Override + public void onBeforeSave(BeforeSaveEvent event) { + BaseDocument document = event.getSource(); + if (document != null) { + AuditMetadata audit = document.getAudit(); + if (audit == null) { + audit = new AuditMetadata(); + audit.setVersion(1L); + audit.setCreatedAt(LocalDateTime.now()); + audit.setLastAction("CREATE"); + document.setAudit(audit); + } else { + // Only increment version if this is an update + if (audit.getCreatedAt() != null) { + audit.setVersion(audit.getVersion() + 1); + audit.setUpdatedAt(LocalDateTime.now()); + if (audit.getLastAction() == null) { + audit.setLastAction("UPDATE"); + } + } + } + } + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/AuditMapper.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/AuditMapper.java new file mode 100644 index 0000000..b46211d --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/AuditMapper.java @@ -0,0 +1,44 @@ +package com.am.common.amcommondata.mapper; + +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.common.AuditMetadata; +import com.am.common.amcommondata.model.AuditModel; + +@Component +public class AuditMapper { + + public AuditModel toModel(AuditMetadata metadata) { + if (metadata == null) { + return null; + } + + return AuditModel.builder() + .version(metadata.getVersion()) + .createdAt(metadata.getCreatedAt()) + .createdBy(metadata.getCreatedBy()) + .createdByEmail(metadata.getCreatedByEmail()) + .updatedAt(metadata.getUpdatedAt()) + .updatedBy(metadata.getUpdatedBy()) + .updatedByEmail(metadata.getUpdatedByEmail()) + .lastAction(metadata.getLastAction()) + .build(); + } + + public AuditMetadata toMetadata(AuditModel model) { + if (model == null) { + return null; + } + + return AuditMetadata.builder() + .version(model.getVersion()) + .createdAt(model.getCreatedAt()) + .createdBy(model.getCreatedBy()) + .createdByEmail(model.getCreatedByEmail()) + .updatedAt(model.getUpdatedAt()) + .updatedBy(model.getUpdatedBy()) + .updatedByEmail(model.getUpdatedByEmail()) + .lastAction(model.getLastAction()) + .build(); + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/PortfolioMapper.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/PortfolioMapper.java new file mode 100644 index 0000000..7e079b7 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/PortfolioMapper.java @@ -0,0 +1,94 @@ +package com.am.common.amcommondata.mapper; + +import java.util.UUID; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.common.AuditMetadata; +import com.am.common.amcommondata.document.portfolio.PortfolioDocument; +import com.am.common.amcommondata.mapper.asset.EquityMapper; +import com.am.common.amcommondata.model.PortfolioModelV1; +import com.am.common.amcommondata.model.enums.Currency; + +@Component +public class PortfolioMapper { + + @Autowired + private EquityMapper equityMapper; + + public PortfolioModelV1 toModel(PortfolioDocument document) { + if (document == null) { + return null; + } + + PortfolioModelV1 model = PortfolioModelV1.builder() + .id(UUID.fromString(document.getId())) + .name(document.getName()) + .description(document.getDescription()) + .owner(document.getOwner()) + .currency(document.getCurrency() != null ? document.getCurrency().name() : null) + .fundType(document.getFundType()) + //.status(document.getPortfolioStatus() != null ? document.getPortfolioStatus().name() : null) + //.tags(document.getTags() != null ? document.getTags().stream().map(String::valueOf).collect(Collectors.toList()) : null) + .notes(document.getNotes()) + .equityModels(document.getEquities() != null + ? document.getEquities().stream() + .map(equityMapper::toModel) + .collect(Collectors.toList()) + : null) + .totalValue(document.getTotalValue()) + .brokerType(document.getBrokerType()) + .assetCount(document.getEquities() != null ? document.getEquities().size() : 0) + .build(); + + if (document.getAudit() != null) { + model.setCreatedAt(document.getAudit().getCreatedAt()); + model.setCreatedBy(document.getAudit().getCreatedBy()); + model.setUpdatedAt(document.getAudit().getUpdatedAt()); + model.setUpdatedBy(document.getAudit().getUpdatedBy()); + model.setVersion(document.getAudit().getVersion()); + } + + return model; + } + + public PortfolioDocument toDocument(PortfolioModelV1 model) { + if (model == null) { + return null; + } + + PortfolioDocument document = PortfolioDocument.builder() + .id(model.getId().toString()) + .name(model.getName()) + .description(model.getDescription()) + .owner(model.getOwner()) + .fundType(model.getFundType()) + //.tags(model.getTags()) + .notes(model.getNotes()) + .equities(model.getEquityModels() != null + ? model.getEquityModels().stream() + .map(equityMapper::toDocument) + .collect(Collectors.toList()) + : null) + .totalValue(model.getTotalValue()) + .brokerType(model.getBrokerType()) + .build(); + + // Set enums using helper methods + document.setCurrency(model.getCurrency() != null ? Currency.valueOf(model.getCurrency()) : null); + //document.setPortfolioStatus(model.getStatus() != null ? PortfolioStatus.valueOf(model.getStatus()) : null); + //document.setBaseStatus(DocumentStatus.ACTIVE); // Default status for new documents + + document.setAudit(AuditMetadata.builder() + .createdAt(model.getCreatedAt()) + .createdBy(model.getCreatedBy()) + .updatedAt(model.getUpdatedAt()) + .updatedBy(model.getUpdatedBy()) + .version(model.getVersion()) + .build()); + + return document; + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/SecurityModelMapper.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/SecurityModelMapper.java new file mode 100644 index 0000000..3877543 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/SecurityModelMapper.java @@ -0,0 +1,210 @@ +package com.am.common.amcommondata.mapper; + +import java.util.UUID; + +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.common.AuditMetadata; +import com.am.common.amcommondata.document.security.SecurityDocument; +import com.am.common.amcommondata.document.security.metadata.CompanyInfo; +import com.am.common.amcommondata.document.security.metadata.KeyInfo; +import com.am.common.amcommondata.document.security.metadata.SecurityMetadata; +import com.am.common.amcommondata.model.AuditModel; +import com.am.common.amcommondata.model.MarketCapType; +import com.am.common.amcommondata.model.security.CompanyInfoModel; +import com.am.common.amcommondata.model.security.SecurityKeyModel; +import com.am.common.amcommondata.model.security.SecurityMetadataModel; +import com.am.common.amcommondata.model.security.SecurityModel; + +@Component +public class SecurityModelMapper { + + public SecurityModel toModel(SecurityDocument document) { + if (document == null) { + return null; + } + + SecurityModel model = SecurityModel.builder() + //.status(document.getStatus()) + .audit(toAuditModel(document.getAudit())) + .key(toKeyModel(document.getKey())) + .metadata(toMetadataModel(document.getMetadata())) + .companyInfo(toCompanyInfoModel(document.getCompanyInfo())) + .build(); + + // Set ID after building to avoid UUID encoding issues + model.setId(UUID.fromString(document.getId())); + return model; + } + + public SecurityDocument toDocument(SecurityModel model) { + if (model == null) { + return null; + } + + SecurityDocument document = SecurityDocument.builder() + //.status(model.getStatus()) + .audit(toAuditMetadata(model.getAudit())) + .key(toKeyInfo(model.getKey())) + .metadata(toSecurityMetadata(model.getMetadata())) + .companyInfo(toCompanyInfo(model.getCompanyInfo())) + .build(); + + // Set ID after building to avoid UUID encoding issues + document.setId(model.getId().toString()); + return document; + } + + private AuditModel toAuditModel(AuditMetadata metadata) { + if (metadata == null) { + return null; + } + + return AuditModel.builder() + .version(metadata.getVersion()) + .createdAt(metadata.getCreatedAt()) + .createdBy(metadata.getCreatedBy()) + .createdByEmail(metadata.getCreatedByEmail()) + .updatedAt(metadata.getUpdatedAt()) + .updatedBy(metadata.getUpdatedBy()) + .updatedByEmail(metadata.getUpdatedByEmail()) + .lastAction(metadata.getLastAction()) + .build(); + } + + private AuditMetadata toAuditMetadata(AuditModel model) { + if (model == null) { + return null; + } + + return AuditMetadata.builder() + .version(model.getVersion()) + .createdAt(model.getCreatedAt()) + .createdBy(model.getCreatedBy()) + .createdByEmail(model.getCreatedByEmail()) + .updatedAt(model.getUpdatedAt()) + .updatedBy(model.getUpdatedBy()) + .updatedByEmail(model.getUpdatedByEmail()) + .lastAction(model.getLastAction()) + .build(); + } + + private SecurityKeyModel toKeyModel(KeyInfo keyInfo) { + if (keyInfo == null) { + return null; + } + + return SecurityKeyModel.builder() + .symbol(keyInfo.getSymbol()) + .isin(keyInfo.getIsin()) + .build(); + } + + private KeyInfo toKeyInfo(SecurityKeyModel model) { + if (model == null) { + return null; + } + + return KeyInfo.builder() + .symbol(model.getSymbol()) + .isin(model.getIsin()) + .build(); + } + + private SecurityMetadataModel toMetadataModel(SecurityMetadata metadata) { + if (metadata == null) { + return null; + } + + return SecurityMetadataModel.builder() + .securityName(metadata.getSecurityName()) + .sector(metadata.getSector()) + .industry(metadata.getIndustry()) + .marketCapValue(metadata.getMarketCapValue()) + .marketCapType(metadata.getMarketCapType()) + .securityType(metadata.getSecurityType()) + .exchange(metadata.getExchange()) + .countryOfIncorporation(metadata.getCountryOfIncorporation()) + .listingDate(metadata.getListingDate()) + .delistingDate(metadata.getDelistingDate()) + .tradingStatus(metadata.getTradingStatus()) + .customAttributes(metadata.getCustomAttributes()) + .build(); + } + + private SecurityMetadata toSecurityMetadata(SecurityMetadataModel model) { + if (model == null) { + return null; + } + + return SecurityMetadata.builder() + .securityName(model.getSecurityName()) + .sector(model.getSector()) + .industry(model.getIndustry()) + .marketCapValue(model.getMarketCapValue()) + .marketCapType(getMarketCapType(model.getMarketCapValue())) + .securityType(model.getSecurityType()) + .exchange(model.getExchange()) + .countryOfIncorporation(model.getCountryOfIncorporation()) + .listingDate(model.getListingDate()) + .delistingDate(model.getDelistingDate()) + .tradingStatus(model.getTradingStatus()) + .customAttributes(model.getCustomAttributes()) + .build(); + } + + private CompanyInfoModel toCompanyInfoModel(CompanyInfo companyInfo) { + if (companyInfo == null) { + return null; + } + + return CompanyInfoModel.builder() + .legalName(companyInfo.getLegalName()) + .symbol(companyInfo.getSymbol()) + .isin(companyInfo.getIsin()) + .cin(companyInfo.getCin()) + .pan(companyInfo.getPan()) + .website(companyInfo.getWebsite()) + .email(companyInfo.getEmail()) + .phoneNumber(companyInfo.getPhoneNumber()) + .description(companyInfo.getDescription()) + .businessDescription(companyInfo.getBusinessDescription()) + .build(); + } + + private CompanyInfo toCompanyInfo(CompanyInfoModel model) { + if (model == null) { + return null; + } + + return CompanyInfo.builder() + .legalName(model.getLegalName()) + .symbol(model.getSymbol()) + .isin(model.getIsin()) + .cin(model.getCin()) + .pan(model.getPan()) + .website(model.getWebsite()) + .email(model.getEmail()) + .phoneNumber(model.getPhoneNumber()) + .description(model.getDescription()) + .businessDescription(model.getBusinessDescription()) + .build(); + } + + public MarketCapType getMarketCapType(Double marketCapValue) { + if (marketCapValue != null && marketCapValue > 0) { + // Calculate market cap type based on market cap value + if (marketCapValue >= 1000000000000D) { // > 1 lakh crore + return MarketCapType.LARGE_CAP; + } else if (marketCapValue >= 250000000000D) { // > 25k crore + return MarketCapType.MID_CAP; + } else if (marketCapValue >= 50000000000D) { // > 5k crore + return MarketCapType.SMALL_CAP; + } else { + return MarketCapType.MICRO_CAP; + } + } else { + return MarketCapType.MICRO_CAP; + } + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/AssetMapper.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/AssetMapper.java new file mode 100644 index 0000000..6f55726 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/AssetMapper.java @@ -0,0 +1,73 @@ +package com.am.common.amcommondata.mapper.asset; + +import java.util.UUID; + +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.asset.AssetDocument; +import com.am.common.amcommondata.model.asset.AssetModel; + +@Component +public abstract class AssetMapper { + + public M toModel(D document) { + if (document == null) { + return null; + } + + M model = createModel(); + populateModel(model, document); + return model; + } + + public D toDocument(M model) { + if (model == null) { + return null; + } + + D document = createDocument(); + populateDocument(document, model); + return document; + } + + protected abstract M createModel(); + protected abstract D createDocument(); + + protected void populateModel(M model, D document) { + model.setId(document.getId() != null ? UUID.fromString(document.getId()) : null); + model.setSymbol(document.getSymbol()); + model.setName(document.getName()); + model.setDescription(document.getDescription()); + model.setAssetType(document.getAssetType()); + model.setMarketData(document.getMarketData()); + model.setQuantity(document.getQuantity()); + model.setAvgBuyingPrice(document.getAvgBuyingPrice()); + model.setCurrentValue(document.getCurrentValue()); + model.setInvestmentValue(document.getInvestmentValue()); + model.setBrokerType(document.getBrokerType()); + model.setProfitLoss(document.getProfitLoss()); + model.setProfitLossPercentage(document.getProfitLossPercentage()); + model.setCreatedAt(document.getCreatedAt()); + model.setUpdatedAt(document.getUpdatedAt()); + model.setIsActive(document.getIsActive()); + } + + protected void populateDocument(D document, M model) { + document.setId(model.getId() != null ? model.getId().toString() : null); + document.setSymbol(model.getSymbol()); + document.setName(model.getName()); + document.setDescription(model.getDescription()); + document.setAssetType(model.getAssetType()); + document.setMarketData(model.getMarketData()); + document.setQuantity(model.getQuantity()); + document.setAvgBuyingPrice(model.getAvgBuyingPrice()); + document.setCurrentValue(model.getCurrentValue()); + document.setInvestmentValue(model.getInvestmentValue()); + document.setBrokerType(model.getBrokerType()); + document.setProfitLoss(model.getProfitLoss()); + document.setProfitLossPercentage(model.getProfitLossPercentage()); + document.setCreatedAt(model.getCreatedAt()); + document.setUpdatedAt(model.getUpdatedAt()); + document.setIsActive(model.getIsActive()); + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/EquityMapper.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/EquityMapper.java new file mode 100644 index 0000000..1e02b07 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/mapper/asset/EquityMapper.java @@ -0,0 +1,56 @@ +package com.am.common.amcommondata.mapper.asset; + +import org.springframework.stereotype.Component; + +import com.am.common.amcommondata.document.asset.equity.EquityDocument; +import com.am.common.amcommondata.model.asset.equity.EquityModel; + +@Component +public class EquityMapper extends AssetMapper { + + @Override + protected EquityModel createModel() { + return EquityModel.builder().build(); + } + + @Override + protected EquityDocument createDocument() { + return EquityDocument.builder().build(); + } + + @Override + protected void populateModel(EquityModel model, EquityDocument document) { + super.populateModel(model, document); + model.setIsin(document.getIsin()); + model.setCompanyName(document.getCompanyName()); + model.setSector(document.getSector()); + model.setIndustry(document.getIndustry()); + model.setMarketCap(document.getMarketCap()); + model.setExchange(document.getExchange()); + model.setPeRatio(document.getPeRatio()); + model.setPbRatio(document.getPbRatio()); + model.setDividendYield(document.getDividendYield()); + model.setEps(document.getEps()); + model.setSharesOutstanding(document.getSharesOutstanding()); + model.setStockType(document.getStockType()); + model.setCountryOfIncorporation(document.getCountryOfIncorporation()); + } + + @Override + protected void populateDocument(EquityDocument document, EquityModel model) { + super.populateDocument(document, model); + document.setIsin(model.getIsin()); + document.setCompanyName(model.getCompanyName()); + document.setSector(model.getSector()); + document.setIndustry(model.getIndustry()); + document.setMarketCap(model.getMarketCap()); + document.setExchange(model.getExchange()); + document.setPeRatio(model.getPeRatio()); + document.setPbRatio(model.getPbRatio()); + document.setDividendYield(model.getDividendYield()); + document.setEps(model.getEps()); + document.setSharesOutstanding(model.getSharesOutstanding()); + document.setStockType(model.getStockType()); + document.setCountryOfIncorporation(model.getCountryOfIncorporation()); + } +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/AuditModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/AuditModel.java new file mode 100644 index 0000000..adb7523 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/AuditModel.java @@ -0,0 +1,23 @@ +package com.am.common.amcommondata.model; + +import java.time.LocalDateTime; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuditModel { + private Long version; + private LocalDateTime createdAt; + private String createdBy; + private String createdByEmail; + private LocalDateTime updatedAt; + private String updatedBy; + private String updatedByEmail; + private String lastAction; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/BaseModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/BaseModel.java new file mode 100644 index 0000000..60eedbc --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/BaseModel.java @@ -0,0 +1,18 @@ +package com.am.common.amcommondata.model; + +import java.util.UUID; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BaseModel { + private UUID id; + private String status; + private AuditModel audit; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/MarketCapType.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/MarketCapType.java new file mode 100644 index 0000000..9f7b6d2 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/MarketCapType.java @@ -0,0 +1,49 @@ +package com.am.common.amcommondata.model; + +public enum MarketCapType { + LARGE_CAP(1000000000000D, "LARGE_CAP"), // > 1 lakh crore + MID_CAP(250000000000D, "MID_CAP"), // 25k to 1 lakh crore + SMALL_CAP(50000000000D, "SMALL_CAP"), // 5k to 25k crore + MICRO_CAP(0D, "MICRO_CAP"); // < 5k crore + + private final double minMarketCapInCrores; + private final String name; + + MarketCapType(double minMarketCapInCrores, String name) { + this.minMarketCapInCrores = minMarketCapInCrores; + this.name = name; + } + + public double getMinMarketCapInCrores() { + return minMarketCapInCrores; + } + + public String getName() { + return name; + } + + public static MarketCapType fromMarketCap(double marketCapInCrores) { + if (marketCapInCrores >= LARGE_CAP.minMarketCapInCrores) { + return LARGE_CAP; + } else if (marketCapInCrores >= MID_CAP.minMarketCapInCrores) { + return MID_CAP; + } else if (marketCapInCrores >= SMALL_CAP.minMarketCapInCrores) { + return SMALL_CAP; + } else { + return MICRO_CAP; + } + } + + public static MarketCapType fromMarketCap(String name) { + if (name.equals(LARGE_CAP.name)) { + return LARGE_CAP; + } else if (name.equals(MID_CAP.name)) { + return MID_CAP; + } else if (name.equals(SMALL_CAP.name)) { + return SMALL_CAP; + } else { + return MICRO_CAP; + } + } +} + diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/CompanyInfoModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/CompanyInfoModel.java new file mode 100644 index 0000000..3ce0c2c --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/CompanyInfoModel.java @@ -0,0 +1,23 @@ +package com.am.common.amcommondata.model.security; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CompanyInfoModel { + private String legalName; + private String symbol; + private String isin; + private String cin; + private String pan; + private String website; + private String email; + private String phoneNumber; + private String description; + private String businessDescription; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityKeyModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityKeyModel.java new file mode 100644 index 0000000..4e91692 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityKeyModel.java @@ -0,0 +1,15 @@ +package com.am.common.amcommondata.model.security; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SecurityKeyModel { + private String symbol; + private String isin; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityMetadataModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityMetadataModel.java new file mode 100644 index 0000000..dc4d616 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityMetadataModel.java @@ -0,0 +1,30 @@ +package com.am.common.amcommondata.model.security; + +import java.time.LocalDateTime; +import java.util.Map; + +import com.am.common.amcommondata.model.MarketCapType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SecurityMetadataModel { + private String securityName; + private String sector; + private String industry; + private Double marketCapValue; + private MarketCapType marketCapType; + private String securityType; + private String exchange; + private String countryOfIncorporation; + private LocalDateTime listingDate; + private LocalDateTime delistingDate; + private String tradingStatus; + private Map customAttributes; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityModel.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityModel.java new file mode 100644 index 0000000..c0b83a5 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/model/security/SecurityModel.java @@ -0,0 +1,20 @@ +package com.am.common.amcommondata.model.security; + +import com.am.common.amcommondata.model.BaseModel; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SecurityModel extends BaseModel { + private SecurityKeyModel key; + private SecurityMetadataModel metadata; + private CompanyInfoModel companyInfo; +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/base/BaseRepository.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/base/BaseRepository.java new file mode 100644 index 0000000..bad0afc --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/base/BaseRepository.java @@ -0,0 +1,16 @@ +package com.am.common.amcommondata.repository.base; + +import java.util.List; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; +import org.springframework.data.repository.NoRepositoryBean; + +import com.am.common.amcommondata.document.base.BaseDocument; + +@NoRepositoryBean +public interface BaseRepository extends MongoRepository { + + @Query(value = "{'_id': ?0}", sort = "{'audit.version': -1}") + List findAllVersionsById(String id); +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioDocumentRepository.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioDocumentRepository.java new file mode 100644 index 0000000..ca519ca --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioDocumentRepository.java @@ -0,0 +1,15 @@ +package com.am.common.amcommondata.repository.portfolio; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Repository; + +import com.am.common.amcommondata.document.portfolio.PortfolioDocument; +import com.am.common.amcommondata.repository.base.BaseRepository; + +@Repository +public interface PortfolioDocumentRepository extends BaseRepository { + List findByOwner(String owner); + Optional findById(String id); +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/security/SecurityRepository.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/security/SecurityRepository.java new file mode 100644 index 0000000..4cd147e --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/repository/security/SecurityRepository.java @@ -0,0 +1,62 @@ +package com.am.common.amcommondata.repository.security; + +import java.util.List; + +import org.springframework.data.mongodb.repository.Query; +import org.springframework.stereotype.Repository; + +import com.am.common.amcommondata.document.security.SecurityDocument; +import com.am.common.amcommondata.repository.base.BaseRepository; + +@Repository +public interface SecurityRepository extends BaseRepository { + + @Query(value = "{'key.symbol': ?0}", sort = "{'audit.createdAt': -1}") + List findBySymbol(String symbol); + + @Query(value = "{'key.isin': ?0}", sort = "{'audit.createdAt': -1}") + List findByIsin(String isin); + + @Query(value = "{$or: [{'key.isin': ?0}, {'key.symbol': ?0}]}", sort = "{'audit.createdAt': -1}") + List findByKey(String key); + + @Query(value = "{'metadata.sector': ?0}", sort = "{'audit.createdAt': -1}") + List findBySector(String sector); + + @Query(value = "{'metadata.industry': ?0}", sort = "{'audit.createdAt': -1}") + List findByIndustry(String industry); + + @Query(value = "{'metadata.marketCapType': ?0}", sort = "{'audit.createdAt': -1}") + List findByMarketCapType(String marketCapType); + + @Query(value = "{'metadata.marketCapValue': {$gte: ?0}}", sort = "{'metadata.marketCapValue': -1}") + List findByMinMarketCap(Long minMarketCap); + + @Query(value = "{'metadata.exchange': ?0}", sort = "{'audit.createdAt': -1}") + List findByExchange(String exchange); + + @Query(value = "{'metadata.tradingStatus': ?0}", sort = "{'audit.createdAt': -1}") + List findByTradingStatus(String tradingStatus); + + @Query(value = "{'companyInfo.legalName': {$regex: ?0, $options: 'i'}}", sort = "{'audit.createdAt': -1}") + List findByCompanyNamePattern(String namePattern); + + @Query(value = "{$and: [{'metadata.marketCapValue': {$gte: ?0}}, {'metadata.sector': ?1}, {'metadata.tradingStatus': 'ACTIVE'}]}", + sort = "{'audit.createdAt': -1}") + List findActiveLargeCapsByMinMarketCapAndSector(Long minMarketCap, String sector); + + @Query(value = "{'key.symbol': {$in: ?0}}", sort = "{'audit.createdAt': -1}") + List findBySymbols(List symbols); + + /** + * Enhanced search method that searches for securities based on a search parameter. + * The search is performed across ISIN, symbol, and security name fields with prioritized results. + * Results are ordered by exact match priority and then by name length for better relevance. + * + * @param searchParam The search parameter to look for + * @return List of matching SecurityDocument objects + */ + @Query(value = "{$and: [{$or: [{'key.isin': ?0}, {'key.symbol': ?0}, {'metadata.securityName': {$regex: ?0, $options: 'i'}}]}, {'metadata.tradingStatus': 'ACTIVE'}]}", + sort = "{'audit.createdAt': -1}") + List findSecurityBySearchParam(String searchParam); +} diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/KeyInfo.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/KeyInfo.java new file mode 100644 index 0000000..e116965 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/KeyInfo.java @@ -0,0 +1,21 @@ +package com.am.common.amcommondata.security.metadata; + +import org.springframework.data.mongodb.core.mapping.Field; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KeyInfo { + + @Field("symbol") + private String symbol; + + @Field("isin") + private String isin; +} \ No newline at end of file diff --git a/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/SecurityMetadata.java b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/SecurityMetadata.java new file mode 100644 index 0000000..6902e36 --- /dev/null +++ b/am-common-data-mongo/src/main/java/com/am/common/amcommondata/security/metadata/SecurityMetadata.java @@ -0,0 +1,57 @@ +package com.am.common.amcommondata.security.metadata; + +import java.time.LocalDateTime; + +import java.util.Map; + +import org.springframework.data.mongodb.core.mapping.Field; + +import com.am.common.amcommondata.model.MarketCapType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SecurityMetadata { + + @Field("security_name") + private String securityName; + + @Field("sector") + private String sector; + + @Field("industry") + private String industry; + + @Field("market_cap_value") + private Double marketCapValue; + + @Field("market_cap_type") + private MarketCapType marketCapType; + + @Field("security_type") + private String securityType; + + @Field("exchange") + private String exchange; + + @Field("country_of_incorporation") + private String countryOfIncorporation; + + @Field("listing_date") + private LocalDateTime listingDate; + + @Field("delisting_date") + private LocalDateTime delistingDate; + + @Field("trading_status") + private String tradingStatus; + + @Field("custom_attributes") + private Map customAttributes; +} diff --git a/am-common-data-persistence/pom.xml b/am-common-data-persistence/pom.xml index 39c6f7a..0dc60a2 100644 --- a/am-common-data-persistence/pom.xml +++ b/am-common-data-persistence/pom.xml @@ -7,7 +7,7 @@ com.am.common am-common-data-parent - 1.2.0-SNAPSHOT + 1.2.10-SNAPSHOT am-common-data-persistence diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/Asset.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/Asset.java index 540d3dd..2fe4203 100644 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/Asset.java +++ b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/Asset.java @@ -2,10 +2,13 @@ import com.am.common.amcommondata.domain.portfolio.Portfolio; import com.am.common.amcommondata.model.enums.AssetType; +import com.am.common.amcommondata.model.enums.BrokerType; import jakarta.persistence.*; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import lombok.experimental.SuperBuilder; import java.time.LocalDateTime; import java.util.UUID; @@ -17,7 +20,7 @@ @NoArgsConstructor @Inheritance(strategy = InheritanceType.JOINED) @Table(name = "assets") -public abstract class Asset { +public class Asset { @Id @GeneratedValue(generator = "UUID") @GenericGenerator( @@ -30,14 +33,16 @@ public abstract class Asset { @Column(nullable = false) private String symbol; + @Column(nullable = false) private String isin; - @Column(nullable = false) private String name; private String description; - @ManyToOne(fetch = FetchType.LAZY) + @ToString.Exclude + @EqualsAndHashCode.Exclude + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "portfolio_id") private Portfolio portfolio; @@ -49,18 +54,21 @@ public abstract class Asset { private Double avgBuyingPrice; private Double currentValue; private Double investmentValue; - private String buyingPlatform; - private String broker; + + @Enumerated(EnumType.STRING) + @Column(name = "broker_type", nullable = false) + private BrokerType brokerType; + private String exchange; private String sector; private String industry; + private String marketType; private String currency; private String country; private String tags; private String notes; private String status; private LocalDateTime purchaseDate; - private LocalDateTime lastUpdated; private LocalDateTime maturityDate; private LocalDateTime createdAt; private LocalDateTime modifiedAt; @@ -75,4 +83,4 @@ protected void onCreate() { protected void onUpdate() { modifiedAt = LocalDateTime.now(); } -} +} \ No newline at end of file diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/Bond.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/Bond.java deleted file mode 100644 index 918a8bd..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/Bond.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.am.common.amcommondata.domain.asset.bond; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.BondInfo; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.MarketData; -import com.am.common.amcommondata.domain.common.*; - -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Bond extends Asset { - - @NotNull(message = "Bond info is required") - @Convert(converter = BondInfoConverter.class) - @Column(name = "bond_info", columnDefinition = "jsonb") - private BondInfo bondInfo; - - @Convert(converter = MarketDataConverter.class) - @Column(name = "market_data", columnDefinition = "jsonb") - private MarketData marketData; - - @Convert(converter = InvestmentDetailsConverter.class) - @Column(name = "investment_details", columnDefinition = "jsonb") - private InvestmentDetails investmentDetails; - - @Convert(converter = TaxInfoConverter.class) - @Column(name = "tax_info", columnDefinition = "jsonb") - private TaxInfo taxInfo; - - private String certificateNumber; - private String depositoryParticipant; - private String dematAccount; - private String registrarName; - private Boolean interestTaxable; - private String form15GHSubmitted; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/BondInfoConverter.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/BondInfoConverter.java deleted file mode 100644 index 4b52825..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/bond/BondInfoConverter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.am.common.amcommondata.domain.asset.bond; - - -import com.am.common.amcommondata.domain.common.*; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Converter(autoApply = true) -public class BondInfoConverter implements AttributeConverter { - private static final Logger logger = LoggerFactory.getLogger(BondInfoConverter.class); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - public String convertToDatabaseColumn(BondInfo bondInfo) { - try { - return objectMapper.writeValueAsString(bondInfo); - } catch (Exception e) { - logger.error("Error converting BondInfo to JSON", e); - return null; - } - } - - @Override - public BondInfo convertToEntityAttribute(String json) { - try { - return objectMapper.readValue(json, BondInfo.class); - } catch (Exception e) { - logger.error("Error converting JSON to BondInfo", e); - return null; - } - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/Commodity.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/Commodity.java deleted file mode 100644 index 1449fe7..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/Commodity.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.am.common.amcommondata.domain.asset.commodity; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotBlank; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Commodity extends Asset { - @NotBlank(message = "Commodity symbol is required") - private String symbol; - - @Embedded - private CommodityInfo commodityInfo; - - @Embedded - private MarketData marketData; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String category; // Precious Metals, Energy, Agriculture - private Double globalDemand; - private Double globalSupply; - private String seasonality; - private String mainProducingCountries; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/CommodityInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/CommodityInfo.java deleted file mode 100644 index 49daf2a..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/commodity/CommodityInfo.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.domain.asset.commodity; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class CommodityInfo { - private String commodityType; // Physical, Future, ETF - private String grade; - private String unit; - private Double purity; - private String storageLocation; - private String custodian; - private String deliveryMethod; - private String contractSize; - private String expiryDate; - private Boolean isDeliverable; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/CryptoInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/CryptoInfo.java deleted file mode 100644 index 3e1c3c9..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/CryptoInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.am.common.amcommondata.domain.asset.cryptocurrency; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class CryptoInfo { - private String blockchain; // Bitcoin, Ethereum, etc. - private String tokenType; // Native, ERC20, etc. - private String contractAddress; - private Integer decimals; - private String networkType; // Mainnet, Testnet - private String consensusMechanism; - private Double circulatingSupply; - private Double maxSupply; - private String walletType; // Hot, Cold, Hardware - private String walletAddress; - private String storageMethod; // Exchange, Personal Wallet - private String exchangeName; - private String tradingPair; - private Boolean staking; - private Double stakingAPY; - private String stakingPlatform; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/Cryptocurrency.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/Cryptocurrency.java deleted file mode 100644 index 9effd83..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/cryptocurrency/Cryptocurrency.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.am.common.amcommondata.domain.asset.cryptocurrency; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotNull; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Cryptocurrency extends Asset { - @NotNull(message = "Crypto info is required") - @Embedded - private CryptoInfo cryptoInfo; - - @Embedded - private MarketData marketData; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String transactionHash; - private String securityMeasures; - private String backupStatus; - private String recoveryPhrase; - private Boolean twoFactorEnabled; - private String kycStatus; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/CompanyInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/CompanyInfo.java deleted file mode 100644 index 7dc5e23..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/CompanyInfo.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.am.common.amcommondata.domain.asset.equity; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class CompanyInfo { - private String companyName; - private String sector; - private String industry; - private String isin; - private String companyDescription; - private String headquartersCountry; - private String website; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/Equity.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/Equity.java deleted file mode 100644 index 1e5d221..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/Equity.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.am.common.amcommondata.domain.asset.equity; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotBlank; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Equity extends Asset { - @NotBlank(message = "Stock symbol is required") - private String symbol; - - @Embedded - private CompanyInfo companyInfo; - - @Embedded - private MarketData marketData; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private FundamentalData fundamentalData; - - @Embedded - private TaxInfo taxInfo; - - // Corporate Actions - private String lastSplitDate; - private String lastSplitRatio; - private String lastDividendDate; - private Double lastDividendAmount; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/FundamentalData.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/FundamentalData.java deleted file mode 100644 index 65f56a1..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/equity/FundamentalData.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.am.common.amcommondata.domain.asset.equity; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class FundamentalData { - private Double peRatio; - private Double pbRatio; - private Double eps; - private Double dividendYield; - private String dividendPaymentFrequency; - private String financialYearEnd; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/DepositInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/DepositInfo.java deleted file mode 100644 index 38eebf4..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/DepositInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.am.common.amcommondata.domain.asset.fixeddeposit; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; -import java.time.LocalDate; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class DepositInfo { - private String depositType; // Regular, Tax Saving, Special - private Double interestRate; - private String interestPayoutFrequency; // Monthly, Quarterly, Annual, At Maturity - private LocalDate startDate; - private LocalDate maturityDate; - private Integer tenureInMonths; - private Boolean autoRenewal; - private String renewalInstructions; - private Boolean nominationRegistered; - private String nomineeDetails; - private String bankBranch; - private String accountNumber; - private String depositNumber; - private Double maturityAmount; - private String interestCompounding; // Monthly, Quarterly, Half-Yearly, Yearly - private Boolean prematureWithdrawalAllowed; - private Double prematureWithdrawalPenalty; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/FixedDeposit.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/FixedDeposit.java deleted file mode 100644 index a57065a..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/fixeddeposit/FixedDeposit.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.am.common.amcommondata.domain.asset.fixeddeposit; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotNull; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class FixedDeposit extends Asset { - @NotNull(message = "Deposit info is required") - @Embedded - private DepositInfo depositInfo; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String bankName; - private String depositScheme; - private String depositStatus; // Active, Matured, Closed - private String linkedBankAccount; - private Boolean isTaxSaving; - private String form15GHSubmitted; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/Gold.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/Gold.java deleted file mode 100644 index 6b8c730..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/Gold.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.am.common.amcommondata.domain.asset.gold; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotNull; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Gold extends Asset { - @NotNull(message = "Gold info is required") - @Embedded - private GoldInfo goldInfo; - - @Embedded - private MarketData marketData; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String dealerName; - private String purchaseInvoiceNumber; - private String custodialAccount; - private String vaultLocation; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/GoldInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/GoldInfo.java deleted file mode 100644 index 8ee39f1..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/gold/GoldInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.am.common.amcommondata.domain.asset.gold; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class GoldInfo { - private String form; // Physical, Digital, ETF, Sovereign Bond - private Double weightInGrams; - private Double purity; // 24K, 22K etc - private String hallmarkCertificateNumber; - private String manufacturer; - private String serialNumber; - private String storageLocation; - private String custodian; - private Boolean isInsured; - private String insuranceProvider; - private String policyNumber; - private Double insuredValue; - private String buybackGuarantee; - private Double buybackRate; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/FundInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/FundInfo.java deleted file mode 100644 index af24ba1..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/FundInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.am.common.amcommondata.domain.asset.mutualfund; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class FundInfo { - private String fundCategory; // Equity, Debt, Hybrid, etc. - private String fundType; // Open-ended, Close-ended - private String fundStyle; // Large Cap, Mid Cap, Small Cap, etc. - private String fundManager; - private String fundHouse; - private Double aum; - private Double expenseRatio; - private Double exitLoad; - private String benchmark; - private Double benchmarkReturn; - private String investmentStrategy; - private Integer minInvestmentAmount; - private String sipOption; - private String redemptionPeriod; - private String fundRating; - private String ratingAgency; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/MutualFund.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/MutualFund.java deleted file mode 100644 index 78b467f..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/mutualfund/MutualFund.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.am.common.amcommondata.domain.asset.mutualfund; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotBlank; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class MutualFund extends Asset { - @NotBlank(message = "Fund symbol is required") - private String symbol; - - @Embedded - private FundInfo fundInfo; - - @Embedded - private MarketData marketData; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String folioNumber; - private String registrarName; - private String distributorCode; - private Boolean directPlan; - private String dividendOption; // Reinvestment, Payout -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/NPS.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/NPS.java deleted file mode 100644 index c58cea9..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/NPS.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.am.common.amcommondata.domain.asset.pension; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.*; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotBlank; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class NPS extends Asset { - @NotBlank(message = "PRAN is required") - private String pran; - - @Embedded - private PensionInfo pensionInfo; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String subscriberType; // Government, Corporate, All Citizens - private String accountType; // Tier I, Tier II - private String kycStatus; - private String nominationRegistered; - private String lastContributionDate; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/PensionInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/PensionInfo.java deleted file mode 100644 index b52a462..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/pension/PensionInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.am.common.amcommondata.domain.asset.pension; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class PensionInfo { - private String schemeType; // Tier 1, Tier 2 - private String pensionFundManager; - private String assetAllocation; - private Double employerContribution; - private Double employeeContribution; - private String withdrawalRules; - private Integer lockInPeriod; - private String nomineeDetails; - private Boolean autoChoice; - private String activeChoice; - private Double equityAllocation; - private Double corporateBondAllocation; - private Double governmentBondAllocation; - private Double alternativeAllocation; - private String annuityProvider; - private String annuityType; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/PropertyInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/PropertyInfo.java deleted file mode 100644 index d140bae..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/PropertyInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.am.common.amcommondata.domain.asset.realestate; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class PropertyInfo { - private String propertyType; // Residential, Commercial, Land - private String constructionStatus; // Ready, Under Construction - private Integer yearBuilt; - private Double builtUpArea; - private Double carpetArea; - private String areaUnit; - private Integer numberOfBedrooms; - private Integer numberOfBathrooms; - private Integer floorNumber; - private Integer totalFloors; - private Boolean furnished; - private String furnishingDetails; - private String amenities; - private String parkingType; - private Integer parkingSpaces; - private String waterSupply; - private String powerBackup; - private String maintenanceStatus; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RealEstate.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RealEstate.java deleted file mode 100644 index 9a2c21d..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RealEstate.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.am.common.amcommondata.domain.asset.realestate; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.domain.common.InvestmentDetails; -import com.am.common.amcommondata.domain.common.LocationInfo; -import com.am.common.amcommondata.domain.common.TaxInfo; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Entity; -import jakarta.persistence.Embedded; -import jakarta.validation.constraints.NotBlank; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class RealEstate extends Asset { - @NotBlank(message = "Property ID is required") - private String propertyId; - - @Embedded - private LocationInfo locationInfo; - - @Embedded - private PropertyInfo propertyInfo; - - @Embedded - private RentalInfo rentalInfo; - - @Embedded - private InvestmentDetails investmentDetails; - - @Embedded - private TaxInfo taxInfo; - - private String ownershipType; // Individual, Joint, Company - private String titleDeedNumber; - private String registrationNumber; - private String legalStatus; - private String encumbranceStatus; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RentalInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RentalInfo.java deleted file mode 100644 index 84addb6..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/asset/realestate/RentalInfo.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.am.common.amcommondata.domain.asset.realestate; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class RentalInfo { - private Boolean isRented; - private Double monthlyRent; - private Double securityDeposit; - private String tenantName; - private String leaseStartDate; - private String leaseEndDate; - private String rentPaymentDate; - private String rentEscalationTerms; - private Double maintenanceCharges; - private String maintenanceIncluded; - private Double propertyTax; - private Double insurancePremium; - private Double annualAppreciation; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/BondInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/BondInfo.java deleted file mode 100644 index 9629226..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/BondInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BondInfo { - private String issuer; - private String bondType; - private Double faceValue; - private Double couponRate; - private String couponFrequency; - private Double yieldToMaturity; - private String creditRating; - private Boolean callable; - private Boolean convertible; - private String interestPaymentDates; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetails.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetails.java deleted file mode 100644 index f31762b..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetails.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class InvestmentDetails { - private Double quantity; - private Double investmentAmount; - private Double currentValue; - private Double averagePrice; - private Double averagePriceInHomeCurrency; - private String investmentGrade; - private String riskRating; - private Double returnOnInvestment; - private Double annualizedReturn; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetailsConverter.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetailsConverter.java deleted file mode 100644 index 7cab737..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/InvestmentDetailsConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Converter(autoApply = true) -public class InvestmentDetailsConverter implements AttributeConverter { - private static final Logger logger = LoggerFactory.getLogger(InvestmentDetailsConverter.class); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - public String convertToDatabaseColumn(InvestmentDetails investmentDetails) { - try { - return objectMapper.writeValueAsString(investmentDetails); - } catch (Exception e) { - logger.error("Error converting InvestmentDetails to JSON", e); - return null; - } - } - - @Override - public InvestmentDetails convertToEntityAttribute(String json) { - try { - return objectMapper.readValue(json, InvestmentDetails.class); - } catch (Exception e) { - logger.error("Error converting JSON to InvestmentDetails", e); - return null; - } - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/LocationInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/LocationInfo.java deleted file mode 100644 index 69fd8c8..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/LocationInfo.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class LocationInfo { - private String address; - private String city; - private String state; - private String country; - private String postalCode; - private Double latitude; - private Double longitude; - private String propertyType; - private Double area; - private String areaUnit; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketData.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketData.java deleted file mode 100644 index 33567e8..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketData.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Embeddable; -import jakarta.persistence.FetchType; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.ArrayList; -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Embeddable -public class MarketData { - @ElementCollection(fetch = FetchType.LAZY) - private List timeSeriesData = new ArrayList<>(); - - private OHLCV latestOHLCV; - private Double marketPrice; - private Double dayHigh; - private Double dayLow; - private Double fiftyTwoWeekHigh; - private Double fiftyTwoWeekLow; - private Double previousClose; - private Long volume; - private Double marketCap; - private String marketCapCategory; - private Double priceInHomeCurrency; - - // Market Indicators - private Double relativeStrengthIndex; - private Double movingAverage50; - private Double movingAverage200; - private Double averageVolume30Days; - private Double beta; - private Double volatility30Days; - - // Order Book - private Double bidPrice; - private Double askPrice; - private Long bidSize; - private Long askSize; - private Double spreadPercentage; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketDataConverter.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketDataConverter.java deleted file mode 100644 index 127d59b..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/MarketDataConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Converter(autoApply = true) -public class MarketDataConverter implements AttributeConverter { - private static final Logger logger = LoggerFactory.getLogger(MarketDataConverter.class); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - public String convertToDatabaseColumn(MarketData marketData) { - try { - return objectMapper.writeValueAsString(marketData); - } catch (Exception e) { - logger.error("Error converting MarketData to JSON", e); - return null; - } - } - - @Override - public MarketData convertToEntityAttribute(String json) { - try { - return objectMapper.readValue(json, MarketData.class); - } catch (Exception e) { - logger.error("Error converting JSON to MarketData", e); - return null; - } - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/OHLCV.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/OHLCV.java deleted file mode 100644 index 57e7fbf..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/OHLCV.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Embeddable -public class OHLCV { - private LocalDateTime timestamp; - private Double open; - private Double high; - private Double low; - private Double close; - private Double volume; - private Double vwap; - private Long numberOfTrades; - private Double turnover; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfo.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfo.java deleted file mode 100644 index 9943cd8..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfo.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import jakarta.persistence.Embeddable; - -@Data -@Embeddable -@SuperBuilder -@NoArgsConstructor -public class TaxInfo { - private Double capitalGainsTaxRate; - private Double dividendTaxRate; - private String regulatoryBody; - private Boolean qualifiedForTaxTreaty; - private String taxJurisdiction; - private String taxLotMethod; // FIFO, LIFO, etc. -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfoConverter.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfoConverter.java deleted file mode 100644 index b3dcf9d..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TaxInfoConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Converter(autoApply = true) -public class TaxInfoConverter implements AttributeConverter { - private static final Logger logger = LoggerFactory.getLogger(TaxInfoConverter.class); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - public String convertToDatabaseColumn(TaxInfo taxInfo) { - try { - return objectMapper.writeValueAsString(taxInfo); - } catch (Exception e) { - logger.error("Error converting TaxInfo to JSON", e); - return null; - } - } - - @Override - public TaxInfo convertToEntityAttribute(String json) { - try { - return objectMapper.readValue(json, TaxInfo.class); - } catch (Exception e) { - logger.error("Error converting JSON to TaxInfo", e); - return null; - } - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TimeSeriesData.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TimeSeriesData.java deleted file mode 100644 index febbb62..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/common/TimeSeriesData.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.am.common.amcommondata.domain.common; - -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Embeddable; -import jakarta.persistence.FetchType; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.util.Map; - -import com.am.common.amcommondata.model.enums.TimeFrame; - -import java.util.List; -import java.util.HashMap; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Embeddable -public class TimeSeriesData { - private LocalDateTime timestamp; - private TimeFrame timeFrame; - - @ElementCollection(fetch = FetchType.LAZY) - private Map> timeFrameData = new HashMap<>(); - - private OHLCV latestFiveMin; - private OHLCV latestFifteenMin; - private OHLCV latestHourly; - private OHLCV latestDaily; - private OHLCV latestWeekly; - private OHLCV latestMonthly; - private OHLCV latestYearly; -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/AssetType.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/AssetType.java deleted file mode 100644 index 43c16fa..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/AssetType.java +++ /dev/null @@ -1,13 +0,0 @@ -// package com.am.common.amcommondata.domain.enums; - -// public enum AssetType { -// EQUITY, -// FIXED_INCOME, -// MUTUAL_FUND, -// ETF, -// COMMODITY, -// NPS, -// REAL_ESTATE, -// CASH, -// CRYPTOCURRENCY -// } diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/BrokerType.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/BrokerType.java deleted file mode 100644 index 3073500..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/BrokerType.java +++ /dev/null @@ -1,28 +0,0 @@ -// package com.am.common.amcommondata.domain.enums; - -// public enum BrokerType { -// DHAN("Dhan"), -// ZERODHA("Zerodha"), -// MSTOCK("MStock"), -// GROW("Grow"), -// KOTAK("Kotak"); - -// private String brokerName; - -// BrokerType(String brokerName) { -// this.brokerName = brokerName; -// } - -// public static BrokerType fromCode(String code) { -// for (BrokerType type : values()) { -// if (type.getCode().equals(code)) { -// return type; -// } -// } -// return null; -// } - -// public String getCode() { -// return brokerName; -// } -// } diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TimeFrame.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TimeFrame.java deleted file mode 100644 index e0f7abf..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TimeFrame.java +++ /dev/null @@ -1,27 +0,0 @@ -// package com.am.common.amcommondata.domain.enums; - -// public enum TimeFrame { -// FIVE_MINUTES("5m", 5), -// FIFTEEN_MINUTES("15m", 15), -// ONE_HOUR("1h", 60), -// DAILY("1d", 1440), -// WEEKLY("1w", 10080), -// MONTHLY("1M", 43200), -// YEARLY("1Y", 525600); - -// private final String code; -// private final int minutes; - -// TimeFrame(String code, int minutes) { -// this.code = code; -// this.minutes = minutes; -// } - -// public String getCode() { -// return code; -// } - -// public int getMinutes() { -// return minutes; -// } -// } diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TransactionType.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TransactionType.java deleted file mode 100644 index 6bbcf65..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/enums/TransactionType.java +++ /dev/null @@ -1,10 +0,0 @@ -// package com.am.common.amcommondata.domain.enums; - -// public enum TransactionType { -// BUY, -// SELL, -// DIVIDEND, -// SPLIT, -// BONUS, -// RIGHTS -// } diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/Exchange.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/Exchange.java deleted file mode 100644 index 52c0d90..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/Exchange.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.am.common.amcommondata.domain.exchange; - -import jakarta.persistence.*; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import java.util.HashSet; -import java.util.Set; -import java.time.LocalDateTime; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@Table(name = "exchanges") -public class Exchange { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable = false, unique = true) - private String code; // NSE, BSE, NYSE etc. - - @Column(nullable = false) - private String name; - - private String description; - private String country; - private String timezone; - private String currency; - private String website; - - @OneToMany(mappedBy = "exchange", cascade = CascadeType.ALL, orphanRemoval = true) - private Set indices = new HashSet<>(); - - @Column(name = "trading_hours") - private String tradingHours; - - @Column(name = "clearing_house") - private String clearingHouse; - - @Column(name = "regulatory_body") - private String regulatoryBody; - - private Boolean active = true; - - @Column(name = "created_at") - private LocalDateTime createdAt; - - @Column(name = "updated_at") - private LocalDateTime updatedAt; - - @Version - private Long version; - - @PrePersist - protected void onCreate() { - createdAt = LocalDateTime.now(); - updatedAt = LocalDateTime.now(); - } - - @PreUpdate - protected void onUpdate() { - updatedAt = LocalDateTime.now(); - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexConstituent.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexConstituent.java deleted file mode 100644 index a7255ff..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexConstituent.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.am.common.amcommondata.domain.exchange; - -import com.am.common.amcommondata.domain.asset.Asset; -import jakarta.persistence.*; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import java.time.LocalDateTime; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@Table(name = "index_constituents") -public class IndexConstituent { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "index_id", nullable = false) - private MarketIndex index; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "asset_id", nullable = false) - private Asset asset; - - @Column(name = "weight_percentage", nullable = false) - private Double weightPercentage; - - @Column(name = "free_float_factor") - private Double freeFloatFactor; - - @Column(name = "inclusion_date") - private LocalDateTime inclusionDate; - - @Column(name = "last_rebalancing_date") - private LocalDateTime lastRebalancingDate; - - private Boolean active = true; - - @Column(name = "created_at") - private LocalDateTime createdAt; - - @Column(name = "updated_at") - private LocalDateTime updatedAt; - - @Version - private Long version; - - @PrePersist - protected void onCreate() { - createdAt = LocalDateTime.now(); - updatedAt = LocalDateTime.now(); - } - - @PreUpdate - protected void onUpdate() { - updatedAt = LocalDateTime.now(); - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexType.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexType.java deleted file mode 100644 index 5b77276..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/IndexType.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.am.common.amcommondata.domain.exchange; - -public enum IndexType { - BROAD_MARKET("Broad Market"), // e.g., NIFTY 50, SENSEX - SECTORAL("Sectoral"), // e.g., NIFTY Bank, NIFTY IT - THEMATIC("Thematic"), // e.g., NIFTY Infrastructure - STRATEGY("Strategy"), // e.g., NIFTY Quality 30 - MARKET_CAP("Market Cap"), // e.g., NIFTY Midcap 100 - FIXED_INCOME("Fixed Income"), // e.g., NIFTY Corporate Bond - ESG("ESG"), // Environmental, Social, and Governance - VOLATILITY("Volatility"), // e.g., India VIX - DERIVATIVE("Derivative"), // Futures and Options - GLOBAL("Global"); // International indices - - private final String description; - - IndexType(String description) { - this.description = description; - } - - public String getDescription() { - return description; - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/MarketIndex.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/MarketIndex.java deleted file mode 100644 index 4e2f96a..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/exchange/MarketIndex.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.am.common.amcommondata.domain.exchange; - -import com.am.common.amcommondata.domain.common.MarketData; -import jakarta.persistence.*; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import java.time.LocalDateTime; -import java.util.HashSet; -import java.util.Set; - -@Data -@Entity -@SuperBuilder -@NoArgsConstructor -@Table(name = "market_indices") -public class MarketIndex { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable = false) - private String code; // NIFTY50, BANKNIFTY, etc. - - @Column(nullable = false) - private String name; - - private String description; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "exchange_id", nullable = false) - private Exchange exchange; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "parent_index_id") - private MarketIndex parentIndex; - - @OneToMany(mappedBy = "parentIndex", cascade = CascadeType.ALL) - private Set subIndices = new HashSet<>(); - - @Enumerated(EnumType.STRING) - @Column(name = "index_type") - private IndexType indexType; - - @Embedded - private MarketData marketData; - - @OneToMany(mappedBy = "index", cascade = CascadeType.ALL) - private Set constituents = new HashSet<>(); - - private Double baseValue; - - @Column(name = "base_date") - private LocalDateTime baseDate; - - @Column(name = "calculation_frequency") - private String calculationFrequency; // Real-time, End of Day - - @Column(name = "rebalancing_frequency") - private String rebalancingFrequency; // Monthly, Quarterly, Semi-Annual, Annual - - private String methodology; - private Boolean active = true; - - @Column(name = "created_at") - private LocalDateTime createdAt; - - @Column(name = "updated_at") - private LocalDateTime updatedAt; - - @Version - private Long version; - - @PrePersist - protected void onCreate() { - createdAt = LocalDateTime.now(); - updatedAt = LocalDateTime.now(); - } - - @PreUpdate - protected void onUpdate() { - updatedAt = LocalDateTime.now(); - } -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/portfolio/Portfolio.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/portfolio/Portfolio.java index a11d374..e8a4557 100644 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/portfolio/Portfolio.java +++ b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/domain/portfolio/Portfolio.java @@ -1,9 +1,14 @@ package com.am.common.amcommondata.domain.portfolio; import com.am.common.amcommondata.domain.asset.Asset; +import com.am.common.amcommondata.model.enums.BrokerType; +import com.am.common.amcommondata.model.enums.FundType; + import jakarta.persistence.*; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import lombok.experimental.SuperBuilder; import org.hibernate.annotations.GenericGenerator; @@ -27,26 +32,42 @@ public class Portfolio { @Column(name = "id", updatable = false, nullable = false) private UUID id; - @Column(nullable = false) private String name; private String description; private String owner; private String currency; - private String type; + + @Enumerated(EnumType.STRING) + @Column(name = "fund_type", nullable = true) + private FundType fundType; + + @Enumerated(EnumType.STRING) + @Column(name = "broker_type", nullable = true) + private BrokerType brokerType; + private String status; private String tags; private String notes; - @OneToMany(mappedBy = "portfolio", cascade = CascadeType.ALL, orphanRemoval = true) + @ToString.Exclude + @EqualsAndHashCode.Exclude + @OneToMany(mappedBy = "portfolio", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) private Set assets = new HashSet<>(); + @Column(name = "total_value") + private Double totalValue; + + @Column(name = "asset_count") + private Integer assetCount; + private LocalDateTime createdAt; private LocalDateTime updatedAt; private String createdBy; private String updatedBy; @Version + @Column(nullable = true) private Long version; public void addAsset(Asset asset) { @@ -79,4 +100,4 @@ public Double getCurrentValue() { .mapToDouble(Asset::getCurrentValue) .sum(); } -} +} \ No newline at end of file diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/PortfolioRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/PortfolioRepository.java deleted file mode 100644 index aaf12f6..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/PortfolioRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.am.common.amcommondata.repository; - -import com.am.common.amcommondata.domain.portfolio.Portfolio; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.UUID; - -@Repository -public interface PortfolioRepository extends JpaRepository { - List findByCreatedBy(String createdBy); - - @Query("SELECT p FROM Portfolio p LEFT JOIN FETCH p.assets WHERE p.id = :id") - Portfolio findByIdWithAssets(UUID id); - - List findByNameContainingIgnoreCase(String name); - - @Query("SELECT p FROM Portfolio p WHERE p.assets.size > :minAssets") - List findPortfoliosWithMinimumAssets(int minAssets); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/AssetRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/AssetRepository.java deleted file mode 100644 index a6d0574..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/AssetRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.am.common.amcommondata.repository.asset; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.model.enums.AssetType; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface AssetRepository extends JpaRepository { - List findByAssetType(AssetType assetType); - - List findByPortfolioId(Long portfolioId); - - @Query("SELECT a FROM Asset a WHERE a.marketData.marketCap >= :minMarketCap") - List findByMinimumMarketCap(Double minMarketCap); - - @Query("SELECT DISTINCT a.assetType FROM Asset a") - List findAllAssetTypes(); - - @Query("SELECT a FROM Asset a WHERE TYPE(a) = :assetClass") - List findByAssetClass(Class assetClass); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/bond/BondRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/bond/BondRepository.java deleted file mode 100644 index f0b46bb..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/bond/BondRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.am.common.amcommondata.repository.asset.bond; - -import com.am.common.amcommondata.domain.asset.bond.Bond; -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.List; - -@Repository -public interface BondRepository extends JpaRepository { - List findByBondInfoBondType(String bondType); - - List findByBondInfoIssuer(String issuer); - - @Query("SELECT b FROM Bond b WHERE b.bondInfo.maturityDate <= :maturityDate") - List findByMaxMaturityDate(LocalDateTime maturityDate); - - @Query("SELECT b FROM Bond b WHERE b.bondInfo.couponRate >= :minRate") - List findByMinimumCouponRate(Double minRate); - - List findByBondInfoCreditRating(String creditRating); - - @Query("SELECT b FROM Bond b WHERE b.bondInfo.yieldToMaturity >= :minYield") - List findByMinimumYTM(Double minYield); - - @Query("SELECT DISTINCT b.bondInfo.issuer FROM Bond b WHERE b.bondInfo.bondType = :bondType") - List findAllIssuersForBondType(String bondType); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/commodity/CommodityRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/commodity/CommodityRepository.java deleted file mode 100644 index 7ea1ed4..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/commodity/CommodityRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.repository.asset.commodity; - -import com.am.common.amcommondata.domain.asset.commodity.Commodity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface CommodityRepository extends JpaRepository { - List findByCommodityType(String type); - List findByForm(String form); - List findByStorageLocation(String location); - - @Query("SELECT c FROM Commodity c WHERE c.weight >= :minWeight AND c.weightUnit = :unit") - List findByMinimumWeight(Double minWeight, String unit); - - @Query("SELECT c FROM Commodity c WHERE c.purity >= :minPurity") - List findByMinimumPurity(Double minPurity); - - @Query("SELECT c FROM Commodity c WHERE c.form = 'Physical' AND c.storageLocation = :location") - List findPhysicalCommoditiesByLocation(String location); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/cryptocurrency/CryptocurrencyRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/cryptocurrency/CryptocurrencyRepository.java deleted file mode 100644 index f97e15c..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/cryptocurrency/CryptocurrencyRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.am.common.amcommondata.repository.asset.cryptocurrency; - -import com.am.common.amcommondata.domain.asset.cryptocurrency.Cryptocurrency; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface CryptocurrencyRepository extends JpaRepository { - List findByCryptoInfoBlockchain(String blockchain); - - List findByCryptoInfoTokenType(String tokenType); - - @Query("SELECT c FROM Cryptocurrency c WHERE c.cryptoInfo.marketCap >= :minMarketCap") - List findByMinimumMarketCap(Double minMarketCap); - - List findByCryptoInfoStakingTrue(); - - @Query("SELECT c FROM Cryptocurrency c WHERE c.cryptoInfo.stakingAPY >= :minAPY") - List findByMinimumStakingAPY(Double minAPY); - - @Query("SELECT DISTINCT c.cryptoInfo.blockchain FROM Cryptocurrency c") - List findAllBlockchains(); - - List findByCryptoInfoWalletType(String walletType); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/equity/EquityRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/equity/EquityRepository.java deleted file mode 100644 index 6fb6593..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/equity/EquityRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.am.common.amcommondata.repository.asset.equity; - -import com.am.common.amcommondata.domain.asset.equity.Equity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -public interface EquityRepository extends JpaRepository { - Optional findEquityBySymbol(String symbol); - Optional findEquityByIsin(String isin); - List findBySector(String sector); - List findByIndustry(String industry); - List findByExchange(String exchange); - List findByMarketCapGreaterThan(Double marketCap); - - @Query("SELECT e FROM Equity e WHERE e.dividendYield >= :minYield") - List findHighDividendYieldStocks(Double minYield); - - @Query("SELECT e FROM Equity e WHERE e.peRatio BETWEEN :minPE AND :maxPE") - List findByPERatioRange(Double minPE, Double maxPE); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/fixeddeposit/FixedDepositRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/fixeddeposit/FixedDepositRepository.java deleted file mode 100644 index 3c81390..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/fixeddeposit/FixedDepositRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.am.common.amcommondata.repository.asset.fixeddeposit; - -import com.am.common.amcommondata.domain.asset.fixeddeposit.FixedDeposit; -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.List; - -@Repository -public interface FixedDepositRepository extends JpaRepository { - List findByBankName(String bankName); - - @Query("SELECT fd FROM FixedDeposit fd WHERE fd.depositInfo.interestRate >= :minRate") - List findByMinimumInterestRate(Double minRate); - - @Query("SELECT fd FROM FixedDeposit fd WHERE fd.depositInfo.maturityDate <= :date") - List findByMaturityDateBefore(LocalDateTime date); - - List findByDepositInfoAutoRenewalTrue(); - - @Query("SELECT fd FROM FixedDeposit fd WHERE fd.depositInfo.tenureInMonths >= :minTenure") - List findByMinimumTenure(Integer minTenure); - - List findByIsTaxSavingTrue(); - - @Query("SELECT DISTINCT fd.bankName FROM FixedDeposit fd") - List findAllBanks(); - - @Query("SELECT fd FROM FixedDeposit fd WHERE fd.depositInfo.maturityAmount >= :minAmount") - List findByMinimumMaturityAmount(Double minAmount); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/gold/GoldRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/gold/GoldRepository.java deleted file mode 100644 index e4f70e6..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/gold/GoldRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.am.common.amcommondata.repository.asset.gold; - -import com.am.common.amcommondata.domain.asset.gold.Gold; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface GoldRepository extends JpaRepository { - List findByGoldInfoForm(String form); - - @Query("SELECT g FROM Gold g WHERE g.goldInfo.purity >= :minPurity") - List findByMinimumPurity(Double minPurity); - - List findByGoldInfoManufacturer(String manufacturer); - - List findByGoldInfoIsInsuredTrue(); - - @Query("SELECT g FROM Gold g WHERE g.goldInfo.weightInGrams >= :minWeight") - List findByMinimumWeight(Double minWeight); - - @Query("SELECT DISTINCT g.goldInfo.form FROM Gold g") - List findAllGoldForms(); - - List findByGoldInfoHallmarkCertificateNumberIsNotNull(); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/mutualfund/MutualFundRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/mutualfund/MutualFundRepository.java deleted file mode 100644 index 9cf83ee..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/mutualfund/MutualFundRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.am.common.amcommondata.repository.asset.mutualfund; - -import com.am.common.amcommondata.domain.asset.mutualfund.MutualFund; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface MutualFundRepository extends JpaRepository { - List findByFundInfoFundCategory(String category); - - List findByFundInfoFundHouse(String fundHouse); - - @Query("SELECT mf FROM MutualFund mf WHERE mf.fundInfo.expenseRatio <= :maxExpenseRatio") - List findByMaxExpenseRatio(Double maxExpenseRatio); - - @Query("SELECT DISTINCT mf.fundInfo.fundHouse FROM MutualFund mf") - List findAllFundHouses(); - - List findByDirectPlanTrue(); - - @Query("SELECT mf FROM MutualFund mf WHERE mf.fundInfo.aum >= :minAum") - List findByMinimumAUM(Double minAum); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/pension/PensionRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/pension/PensionRepository.java deleted file mode 100644 index 55ca7c5..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/pension/PensionRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.am.common.amcommondata.repository.asset.pension; - -import com.am.common.amcommondata.domain.asset.pension.NPS; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; -import java.time.LocalDate; - -@Repository -public interface PensionRepository extends JpaRepository { - List findBySchemeType(String schemeType); - List findByFundManager(String fundManager); - List findByMaturityDateBefore(LocalDate date); - - @Query("SELECT p FROM NPS p WHERE p.currentReturn >= :minReturn") - List findHighPerformingSchemes(Double minReturn); - - @Query("SELECT p FROM NPS p WHERE p.employeeContribution + p.employerContribution + p.governmentContribution >= :minContribution") - List findByMinimumTotalContribution(Double minContribution); - - @Query("SELECT p FROM NPS p WHERE p.expectedCorpus >= :minCorpus") - List findByMinimumExpectedCorpus(Double minCorpus); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/realestate/RealEstateRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/realestate/RealEstateRepository.java deleted file mode 100644 index 5ec02ab..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/asset/realestate/RealEstateRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.repository.asset.realestate; - -import com.am.common.amcommondata.domain.asset.realestate.RealEstate; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; - -@Repository -public interface RealEstateRepository extends JpaRepository { - List findByPropertyType(String propertyType); - List findByLocation(String location); - List findByOccupancyStatus(String status); - - @Query("SELECT r FROM RealEstate r WHERE r.rentalIncome > 0") - List findRentGeneratingProperties(); - - @Query("SELECT r FROM RealEstate r WHERE r.currentValuation >= :minValue") - List findHighValueProperties(Double minValue); - - @Query("SELECT r FROM RealEstate r WHERE r.area >= :minArea AND r.areaUnit = :unit") - List findByMinimumArea(Double minArea, String unit); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/ExchangeRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/ExchangeRepository.java deleted file mode 100644 index dff2dd0..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/ExchangeRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.repository.exchange; - -import com.am.common.amcommondata.domain.exchange.Exchange; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - -@Repository -public interface ExchangeRepository extends JpaRepository { - Optional findByCode(String code); - - List findByCountry(String country); - - @Query("SELECT e FROM Exchange e LEFT JOIN FETCH e.indices WHERE e.id = :id") - Optional findByIdWithIndices(Long id); - - List findByActiveTrue(); - - @Query("SELECT DISTINCT e.country FROM Exchange e") - List findAllCountries(); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/IndexConstituentRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/IndexConstituentRepository.java deleted file mode 100644 index a87eadd..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/IndexConstituentRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.am.common.amcommondata.repository.exchange; - -import com.am.common.amcommondata.domain.exchange.IndexConstituent; -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.List; - -@Repository -public interface IndexConstituentRepository extends JpaRepository { - List findByIndexId(Long indexId); - - List findByAssetId(Long assetId); - - @Query("SELECT ic FROM IndexConstituent ic WHERE ic.index.code = :indexCode ORDER BY ic.weightPercentage DESC") - List findByIndexCodeOrderByWeightDesc(String indexCode); - - @Query("SELECT ic FROM IndexConstituent ic WHERE ic.index.id = :indexId AND ic.weightPercentage >= :minWeight") - List findHeavyweightConstituents(Long indexId, Double minWeight); - - List findByInclusionDateAfter(LocalDateTime date); - - @Query("SELECT ic FROM IndexConstituent ic WHERE ic.index.id = :indexId AND ic.lastRebalancingDate = " + - "(SELECT MAX(ic2.lastRebalancingDate) FROM IndexConstituent ic2 WHERE ic2.index.id = :indexId)") - List findLatestIndexComposition(Long indexId); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/MarketIndexRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/MarketIndexRepository.java deleted file mode 100644 index 95e589f..0000000 --- a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/exchange/MarketIndexRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.am.common.amcommondata.repository.exchange; - -import com.am.common.amcommondata.domain.exchange.MarketIndex; -import com.am.common.amcommondata.domain.exchange.IndexType; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - -@Repository -public interface MarketIndexRepository extends JpaRepository { - Optional findByCode(String code); - - List findByExchangeCode(String exchangeCode); - - List findByIndexType(IndexType indexType); - - @Query("SELECT mi FROM MarketIndex mi LEFT JOIN FETCH mi.constituents WHERE mi.id = :id") - Optional findByIdWithConstituents(Long id); - - List findByParentIndexId(Long parentIndexId); - - @Query("SELECT mi FROM MarketIndex mi WHERE mi.parentIndex IS NULL") - List findAllParentIndices(); - - @Query("SELECT DISTINCT mi.indexType FROM MarketIndex mi WHERE mi.exchange.code = :exchangeCode") - List findIndexTypesByExchange(String exchangeCode); -} diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/AssetRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/AssetRepository.java new file mode 100644 index 0000000..cbb8b0f --- /dev/null +++ b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/AssetRepository.java @@ -0,0 +1,14 @@ +package com.am.common.amcommondata.repository.portfolio; + +import com.am.common.amcommondata.domain.asset.Asset; +import com.am.common.amcommondata.domain.portfolio.Portfolio; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.UUID; + +@Repository +public interface AssetRepository extends JpaRepository { + List findByIsin(String isin); + List findByPortfolio(Portfolio portfolio); +} \ No newline at end of file diff --git a/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioRepository.java b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioRepository.java new file mode 100644 index 0000000..5ff3cf5 --- /dev/null +++ b/am-common-data-persistence/src/main/java/com/am/common/amcommondata/repository/portfolio/PortfolioRepository.java @@ -0,0 +1,13 @@ +package com.am.common.amcommondata.repository.portfolio; + +import com.am.common.amcommondata.domain.portfolio.Portfolio; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.UUID; + +@Repository +public interface PortfolioRepository extends JpaRepository { + List findByOwner(String owner); + +} \ No newline at end of file diff --git a/am-common-data-service/pom.xml b/am-common-data-service/pom.xml index 9707dfb..66f77fb 100644 --- a/am-common-data-service/pom.xml +++ b/am-common-data-service/pom.xml @@ -7,7 +7,7 @@ com.am.common am-common-data-parent - 1.2.0-SNAPSHOT + 1.2.10-SNAPSHOT am-common-data-service @@ -20,7 +20,7 @@ com.am.common - am-common-data-persistence + am-common-data-mongo ${project.version} @@ -61,11 +61,6 @@ ${java.version} ${java.version} - - org.mapstruct - mapstruct-processor - 1.5.5.Final - org.projectlombok lombok @@ -76,6 +71,11 @@ lombok-mapstruct-binding 0.2.0 + + org.mapstruct + mapstruct-processor + 1.5.5.Final + diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceAutoConfiguration.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceAutoConfiguration.java new file mode 100644 index 0000000..e5f8553 --- /dev/null +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceAutoConfiguration.java @@ -0,0 +1,47 @@ +package com.am.common.amcommondata.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoTemplate; + +import com.am.common.amcommondata.mapper.SecurityModelMapper; +import com.am.common.amcommondata.repository.security.SecurityRepository; +import com.am.common.amcommondata.service.AuditService; +import com.am.common.amcommondata.service.SecurityService; + +/** + * Auto-configuration for SecurityService to ensure it works properly when included as a dependency + * in other services. + */ +@AutoConfiguration +@Configuration +@EnableConfigurationProperties(SecurityServiceProperties.class) +@ConditionalOnProperty(prefix = "am.common.security", name = "enabled", havingValue = "true", matchIfMissing = true) +@ComponentScan(basePackages = { + "com.am.common.amcommondata.service", + "com.am.common.amcommondata.repository.security", + "com.am.common.amcommondata.mapper" +}) +public class SecurityServiceAutoConfiguration { + + private static final Logger log = LoggerFactory.getLogger(SecurityServiceAutoConfiguration.class); + + @Bean + @ConditionalOnMissingBean + public SecurityService securityService( + SecurityRepository securityRepository, + SecurityModelMapper securityMapper, + AuditService auditService, + SecurityServiceProperties properties) { + + log.info("Initializing SecurityService with properties: {}", properties); + return new SecurityService(securityRepository, securityMapper, auditService); + } +} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceProperties.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceProperties.java new file mode 100644 index 0000000..5555a5b --- /dev/null +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/config/SecurityServiceProperties.java @@ -0,0 +1,72 @@ +package com.am.common.amcommondata.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for SecurityService. + */ +@ConfigurationProperties(prefix = "am.common.security") +public class SecurityServiceProperties { + + /** + * Enable/disable the SecurityService auto-configuration. + */ + private boolean enabled = true; + + /** + * Maximum number of symbols to process in a single batch. + */ + private int maxSymbolBatchSize = 100; + + /** + * Enable/disable detailed logging for security operations. + */ + private boolean detailedLogging = false; + + /** + * Default timeout in milliseconds for security operations. + */ + private int operationTimeoutMs = 5000; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getMaxSymbolBatchSize() { + return maxSymbolBatchSize; + } + + public void setMaxSymbolBatchSize(int maxSymbolBatchSize) { + this.maxSymbolBatchSize = maxSymbolBatchSize; + } + + public boolean isDetailedLogging() { + return detailedLogging; + } + + public void setDetailedLogging(boolean detailedLogging) { + this.detailedLogging = detailedLogging; + } + + public int getOperationTimeoutMs() { + return operationTimeoutMs; + } + + public void setOperationTimeoutMs(int operationTimeoutMs) { + this.operationTimeoutMs = operationTimeoutMs; + } + + @Override + public String toString() { + return "SecurityServiceProperties{" + + "enabled=" + enabled + + ", maxSymbolBatchSize=" + maxSymbolBatchSize + + ", detailedLogging=" + detailedLogging + + ", operationTimeoutMs=" + operationTimeoutMs + + '}'; + } +} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/AuditService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/AuditService.java new file mode 100644 index 0000000..cadc9c6 --- /dev/null +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/AuditService.java @@ -0,0 +1,24 @@ +package com.am.common.amcommondata.service; + +import java.time.LocalDateTime; + +import org.springframework.stereotype.Service; + +import com.am.common.amcommondata.document.base.BaseDocument; +import com.am.common.amcommondata.document.common.AuditMetadata; + + +@Service +public class AuditService { + + public void updateAudit(BaseDocument document) { + if (document.getAudit() == null) { + document.setAudit(new AuditMetadata()); + document.getAudit().setCreatedAt(LocalDateTime.now()); + document.getAudit().setVersion(1L); + } else { + document.getAudit().setUpdatedAt(LocalDateTime.now()); + document.getAudit().setVersion(document.getAudit().getVersion() + 1); + } + } +} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioService.java index e468a6a..74f6a4c 100644 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioService.java +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioService.java @@ -1,66 +1,12 @@ package com.am.common.amcommondata.service; -import com.am.common.amcommondata.domain.portfolio.Portfolio; -import com.am.common.amcommondata.model.PortfolioModel; -import com.am.common.amcommondata.repository.PortfolioRepository; -import com.am.common.amcommondata.service.mapper.PortfolioMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import com.am.common.amcommondata.model.PortfolioModelV1; + import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class PortfolioService { - private final PortfolioRepository portfolioRepository; - private final PortfolioMapper portfolioMapper; - - public PortfolioModel getPortfolio(UUID id) { - return portfolioRepository.findById(id) - .map(portfolioMapper::toModel) - .orElseThrow(() -> new RuntimeException("Portfolio not found: " + id)); - } - - public List getAllPortfolios() { - return portfolioRepository.findAll().stream() - .map(portfolioMapper::toModel) - .collect(Collectors.toList()); - } - - public List getPortfoliosByUser(String userId) { - return portfolioRepository.findByCreatedBy(userId).stream() - .map(portfolioMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public PortfolioModel createPortfolio(PortfolioModel portfolioModel) { - Portfolio portfolio = portfolioMapper.toEntity(portfolioModel); - portfolio = portfolioRepository.save(portfolio); - return portfolioMapper.toModel(portfolio); - } - - @Transactional - public PortfolioModel updatePortfolio(UUID id, PortfolioModel portfolioModel) { - Portfolio existingPortfolio = portfolioRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Portfolio not found: " + id)); - - Portfolio updatedPortfolio = portfolioMapper.updateEntity(existingPortfolio, portfolioModel); - updatedPortfolio = portfolioRepository.save(updatedPortfolio); - return portfolioMapper.toModel(updatedPortfolio); - } - - @Transactional - public void deletePortfolio(UUID id) { - portfolioRepository.deleteById(id); - } - public List searchPortfolios(String query) { - return portfolioRepository.findByNameContainingIgnoreCase(query).stream() - .map(portfolioMapper::toModel) - .collect(Collectors.toList()); - } +public interface PortfolioService { + List getPortfoliosByUserId(String userId); + PortfolioModelV1 getPortfolioById(UUID id); + PortfolioModelV1 createPortfolio(PortfolioModelV1 portfolio); } diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioServiceImpl.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioServiceImpl.java new file mode 100644 index 0000000..7e268d4 --- /dev/null +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/PortfolioServiceImpl.java @@ -0,0 +1,42 @@ +package com.am.common.amcommondata.service; + +import com.am.common.amcommondata.document.portfolio.PortfolioDocument; +import com.am.common.amcommondata.mapper.PortfolioMapper; +import com.am.common.amcommondata.model.PortfolioModelV1; +import com.am.common.amcommondata.repository.portfolio.PortfolioDocumentRepository; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class PortfolioServiceImpl implements PortfolioService { + private final PortfolioDocumentRepository portfolioDocumentRepository; + private final PortfolioMapper portfolioMapper; + + @Override + public List getPortfoliosByUserId(String userId) { + return portfolioDocumentRepository.findByOwner(userId).stream() + .map(portfolioMapper::toModel) + .collect(Collectors.toList()); + } + + @Override + public PortfolioModelV1 getPortfolioById(UUID id) { + return portfolioDocumentRepository.findById(id.toString()) + .map(portfolioMapper::toModel) + .orElse(null); + } + + @Transactional + public PortfolioModelV1 createPortfolio(PortfolioModelV1 portfolioModel) { + PortfolioDocument document = portfolioMapper.toDocument(portfolioModel); + return portfolioMapper.toModel(portfolioDocumentRepository.save(document)); + } +} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/SecurityService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/SecurityService.java new file mode 100644 index 0000000..707c8b7 --- /dev/null +++ b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/SecurityService.java @@ -0,0 +1,234 @@ +package com.am.common.amcommondata.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import com.am.common.amcommondata.document.security.SecurityDocument; +import com.am.common.amcommondata.mapper.SecurityModelMapper; +import com.am.common.amcommondata.model.security.SecurityModel; +import com.am.common.amcommondata.repository.security.SecurityRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class SecurityService { + + private static final Logger log = LoggerFactory.getLogger(SecurityService.class); + + private final SecurityRepository securityRepository; + private final SecurityModelMapper securityMapper; + private final AuditService auditService; + + + public SecurityModel save(SecurityModel securityModel) { + log.info("Saving security model: {}", securityModel.getKey()); + SecurityDocument document = securityMapper.toDocument(securityModel); + auditService.updateAudit(document); + SecurityModel result = securityMapper.toModel(securityRepository.save(document)); + log.debug("Successfully saved security with ID: {}", result.getId()); + return result; + } + + public Optional findById(UUID id) { + log.info("Finding security by ID: {}", id); + Optional result = securityRepository.findById(id.toString()) + .map(securityMapper::toModel); + log.debug("Found security by ID {}: {}", id, result.isPresent()); + return result; + } + + public Optional findBySymbol(String symbol) { + log.info("Finding security by symbol: {}", symbol); + Optional result = securityRepository.findBySymbol(symbol).stream() + .findFirst() + .map(securityMapper::toModel); + log.debug("Found security by symbol {}: {}", symbol, result.isPresent()); + return result; + } + + public Optional findByKey(String key) { + log.info("Finding security by key: {}", key); + Optional result = securityRepository.findByKey(key).stream() + .findFirst() + .map(securityMapper::toModel); + log.debug("Found security by key {}: {}", key, result.isPresent()); + return result; + } + + public Optional findByIsin(String isin) { + log.info("Finding security by ISIN: {}", isin); + Optional result = securityRepository.findByIsin(isin).stream() + .findFirst() + .map(securityMapper::toModel); + log.debug("Found security by ISIN {}: {}", isin, result.isPresent()); + return result; + } + + public List findActiveLargeCapsByMinMarketCapAndSector(Long minMarketCap, String sector) { + log.info("Finding active large caps by minimum market cap: {} and sector: {}", minMarketCap, sector); + List results = securityRepository.findActiveLargeCapsByMinMarketCapAndSector(minMarketCap, sector) + .stream() + .map(securityMapper::toModel) + .collect(Collectors.toList()); + log.debug("Found {} active large cap securities for sector: {} with minimum market cap: {}", results.size(), sector, minMarketCap); + return results; + } + + public List findAllVersionsById(UUID id) { + log.info("Finding all versions of security by ID: {}", id); + List results = securityRepository.findAllVersionsById(id.toString()) + .stream() + .map(securityMapper::toModel) + .collect(Collectors.toList()); + log.debug("Found {} versions of security with ID: {}", results.size(), id); + return results; + } + + public void deleteById(UUID id) { + log.info("Deleting security by ID: {}", id); + securityRepository.deleteById(id.toString()); + log.debug("Successfully deleted security with ID: {}", id); + } + + public void deleteAll() { + log.info("Deleting all securities"); + securityRepository.deleteAll(); + log.debug("Successfully deleted all securities"); + } + + public List saveAll(List securities) { + log.info("Saving batch of {} securities", securities.size()); + List documents = securities.stream() + .map(securityMapper::toDocument) + .peek(auditService::updateAudit) + .collect(Collectors.toList()); + + List savedDocuments = new ArrayList<>(); + for (int i = 0; i < documents.size(); i += 100) { + int end = Math.min(i + 100, documents.size()); + List batch = documents.subList(i, end); + log.debug("Saving batch of securities from index {} to {}", i, end - 1); + savedDocuments.addAll(securityRepository.saveAll(batch)); + } + + List result = savedDocuments.stream() + .map(securityMapper::toModel) + .collect(Collectors.toList()); + log.debug("Successfully saved {} securities", result.size()); + return result; + } + + /** + * Find securities by a list of symbols, returning the latest version of each security based on audit creation time. + * + * @param symbols List of security symbols to search for + * @return List of SecurityModel objects, with the latest version of each security + */ + /** + * Find securities by a list of symbols, returning the latest version of each security based on audit creation time. + * This method is designed to work reliably when used as a dependency in other services. + * + * @param symbols List of security symbols to search for + * @return List of SecurityModel objects, with the latest version of each security + */ + public List findBySymbols(List symbols) { + log.info("Finding securities by symbols: {}", symbols); + if (symbols == null || symbols.isEmpty()) { + log.debug("Empty symbols list provided, returning empty result"); + return new ArrayList<>(); + } + + try { + // Get all matching securities sorted by audit.createdAt in descending order + log.debug("Querying repository for securities with symbols: {}", symbols); + List allSecurities = securityRepository.findBySymbols(symbols); + + if (allSecurities == null) { + log.warn("Repository returned null for findBySymbols with symbols: {}", symbols); + return new ArrayList<>(); + } + + log.debug("Found {} security documents for {} requested symbols", allSecurities.size(), symbols.size()); + + // Group by symbol and take the first (latest) entry for each symbol + Map latestBySymbol = new HashMap<>(); + + for (SecurityDocument doc : allSecurities) { + if (doc != null && doc.getKey() != null && doc.getKey().getSymbol() != null) { + String symbol = doc.getKey().getSymbol(); + if (!latestBySymbol.containsKey(symbol)) { + latestBySymbol.put(symbol, doc); + } + } else { + log.warn("Encountered invalid security document: {}", doc); + } + } + + // Convert documents to models + List result = latestBySymbol.values().stream() + .map(securityMapper::toModel) + .collect(Collectors.toList()); + + log.debug("Returning {} unique securities after filtering for latest versions", result.size()); + return result; + } catch (Exception e) { + log.error("Error finding securities by symbols: {}", symbols, e); + return new ArrayList<>(); + } + } + + /** + * Enhanced search method that searches for securities based on a search parameter. + * The search is performed across ISIN, symbol, and security name fields with prioritized results. + * Results are ordered by exact match priority and then by name length for better relevance: + * 1. Exact ISIN match (highest priority) + * 2. Exact symbol match + * 3. Exact security name match (case insensitive) + * 4. Partial security name match (ordered by length) + * + * Only active securities are returned. + * + * @param searchParam The search parameter to look for + * @return List of matching SecurityModel objects in priority order + */ + public List findSecurityBySearchParam(String searchParam) { + log.info("Finding securities by search parameter: {}", searchParam); + if (searchParam == null || searchParam.trim().isEmpty()) { + log.debug("Empty search parameter provided, returning empty result"); + return new ArrayList<>(); + } + + try { + // Use the custom repository implementation for enhanced search + List matchingSecurities = securityRepository.findSecurityBySearchParam(searchParam); + + if (matchingSecurities == null) { + log.warn("Repository returned null for findSecurityBySearchParam with parameter: {}", searchParam); + return new ArrayList<>(); + } + + log.debug("Found {} matching securities for search parameter: {}", matchingSecurities.size(), searchParam); + + // Convert documents to models + List result = matchingSecurities.stream() + .map(securityMapper::toModel) + .collect(Collectors.toList()); + + log.debug("Returning {} securities after search and prioritization", result.size()); + return result; + } catch (Exception e) { + log.error("Error finding securities by search parameter: {}", searchParam, e); + return new ArrayList<>(); + } + } +} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/BondService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/BondService.java deleted file mode 100644 index e2e2007..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/BondService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.bond.Bond; -import com.am.common.amcommondata.model.asset.bond.BondModel; -import com.am.common.amcommondata.repository.asset.bond.BondRepository; -import com.am.common.amcommondata.service.mapper.asset.BondMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class BondService { - private final BondRepository bondRepository; - private final BondMapper bondMapper; - - public BondModel getBond(Long id) { - return bondRepository.findById(id) - .map(bondMapper::toModel) - .orElseThrow(() -> new RuntimeException("Bond not found: " + id)); - } - - public List getAllBonds() { - return bondRepository.findAll().stream() - .map(bondMapper::toModel) - .collect(Collectors.toList()); - } - - public List getBondsByType(String bondType) { - return bondRepository.findByBondInfoBondType(bondType).stream() - .map(bondMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public BondModel createBond(BondModel model) { - Bond entity = bondMapper.toEntity(model); - entity = bondRepository.save(entity); - return bondMapper.toModel(entity); - } - - @Transactional - public BondModel updateBond(Long id, BondModel model) { - Bond existingBond = bondRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Bond not found: " + id)); - Bond updatedBond = bondMapper.updateEntity(existingBond, model); - updatedBond = bondRepository.save(updatedBond); - return bondMapper.toModel(updatedBond); - } - - @Transactional - public void deleteBond(Long id) { - bondRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/CryptocurrencyService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/CryptocurrencyService.java deleted file mode 100644 index b21e7e4..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/CryptocurrencyService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.cryptocurrency.Cryptocurrency; -import com.am.common.amcommondata.model.asset.cryptocurrency.CryptocurrencyModel; -import com.am.common.amcommondata.repository.asset.cryptocurrency.CryptocurrencyRepository; -import com.am.common.amcommondata.service.mapper.asset.CryptocurrencyMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class CryptocurrencyService { - private final CryptocurrencyRepository cryptocurrencyRepository; - private final CryptocurrencyMapper cryptocurrencyMapper; - - public CryptocurrencyModel getCryptocurrency(Long id) { - return cryptocurrencyRepository.findById(id) - .map(cryptocurrencyMapper::toModel) - .orElseThrow(() -> new RuntimeException("Cryptocurrency not found: " + id)); - } - - public List getAllCryptocurrencies() { - return cryptocurrencyRepository.findAll().stream() - .map(cryptocurrencyMapper::toModel) - .collect(Collectors.toList()); - } - - public List getCryptocurrenciesByBlockchain(String blockchain) { - return cryptocurrencyRepository.findByCryptoInfoBlockchain(blockchain).stream() - .map(cryptocurrencyMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public CryptocurrencyModel createCryptocurrency(CryptocurrencyModel model) { - Cryptocurrency entity = cryptocurrencyMapper.toEntity(model); - entity = cryptocurrencyRepository.save(entity); - return cryptocurrencyMapper.toModel(entity); - } - - @Transactional - public CryptocurrencyModel updateCryptocurrency(Long id, CryptocurrencyModel model) { - Cryptocurrency existingCrypto = cryptocurrencyRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Cryptocurrency not found: " + id)); - Cryptocurrency updatedCrypto = cryptocurrencyMapper.updateEntity(existingCrypto, model); - updatedCrypto = cryptocurrencyRepository.save(updatedCrypto); - return cryptocurrencyMapper.toModel(updatedCrypto); - } - - @Transactional - public void deleteCryptocurrency(Long id) { - cryptocurrencyRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/EquityService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/EquityService.java deleted file mode 100644 index af55358..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/EquityService.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.model.asset.equity.EquityModel; -import com.am.common.amcommondata.repository.asset.equity.EquityRepository; -import com.am.common.amcommondata.service.mapper.asset.EquityMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class EquityService { - private final EquityRepository equityRepository; - private final EquityMapper equityMapper; - - public EquityModel getEquity(String symbol) { - return equityRepository.findEquityBySymbol(symbol) - .map(equityMapper::toModel) - .orElseThrow(() -> new RuntimeException("Equity not found: " + symbol)); - } - - public List getAllEquities() { - return equityRepository.findAll().stream() - .map(equityMapper::toModel) - .collect(Collectors.toList()); - } - - public List getEquitiesBySector(String sector) { - return equityRepository.findBySector(sector).stream() - .map(equityMapper::toModel) - .collect(Collectors.toList()); - } - - public List getHighDividendYieldStocks(Double minYield) { - return equityRepository.findHighDividendYieldStocks(minYield).stream() - .map(equityMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public EquityModel createEquity(EquityModel equityModel) { - var equity = equityMapper.toEntity(equityModel); - equity = equityRepository.save(equity); - return equityMapper.toModel(equity); - } - - @Transactional - public EquityModel updateEquity(String symbol, EquityModel equityModel) { - var existingEquity = equityRepository.findEquityBySymbol(symbol) - .orElseThrow(() -> new RuntimeException("Equity not found: " + symbol)); - var updatedEquity = equityMapper.updateEntity(existingEquity, equityModel); - updatedEquity = equityRepository.save(updatedEquity); - return equityMapper.toModel(updatedEquity); - } - -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/FixedDepositService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/FixedDepositService.java deleted file mode 100644 index a16b89e..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/FixedDepositService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.fixeddeposit.FixedDeposit; -import com.am.common.amcommondata.model.asset.fixeddeposit.FixedDepositModel; -import com.am.common.amcommondata.repository.asset.fixeddeposit.FixedDepositRepository; -import com.am.common.amcommondata.service.mapper.asset.FixedDepositMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class FixedDepositService { - private final FixedDepositRepository fixedDepositRepository; - private final FixedDepositMapper fixedDepositMapper; - - public FixedDepositModel getFixedDeposit(Long id) { - return fixedDepositRepository.findById(id) - .map(fixedDepositMapper::toModel) - .orElseThrow(() -> new RuntimeException("Fixed Deposit not found: " + id)); - } - - public List getAllFixedDeposits() { - return fixedDepositRepository.findAll().stream() - .map(fixedDepositMapper::toModel) - .collect(Collectors.toList()); - } - - public List getFixedDepositsByBank(String bankName) { - return fixedDepositRepository.findByBankName(bankName).stream() - .map(fixedDepositMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public FixedDepositModel createFixedDeposit(FixedDepositModel model) { - FixedDeposit entity = fixedDepositMapper.toEntity(model); - entity = fixedDepositRepository.save(entity); - return fixedDepositMapper.toModel(entity); - } - - @Transactional - public FixedDepositModel updateFixedDeposit(Long id, FixedDepositModel model) { - FixedDeposit existingFD = fixedDepositRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Fixed Deposit not found: " + id)); - FixedDeposit updatedFD = fixedDepositMapper.updateEntity(existingFD, model); - updatedFD = fixedDepositRepository.save(updatedFD); - return fixedDepositMapper.toModel(updatedFD); - } - - @Transactional - public void deleteFixedDeposit(Long id) { - fixedDepositRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/GoldService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/GoldService.java deleted file mode 100644 index 7374515..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/GoldService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.gold.Gold; -import com.am.common.amcommondata.model.asset.gold.GoldModel; -import com.am.common.amcommondata.repository.asset.gold.GoldRepository; -import com.am.common.amcommondata.service.mapper.asset.GoldMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class GoldService { - private final GoldRepository goldRepository; - private final GoldMapper goldMapper; - - public GoldModel getGold(Long id) { - return goldRepository.findById(id) - .map(goldMapper::toModel) - .orElseThrow(() -> new RuntimeException("Gold not found: " + id)); - } - - public List getAllGold() { - return goldRepository.findAll().stream() - .map(goldMapper::toModel) - .collect(Collectors.toList()); - } - - public List getGoldByPurity(Double purity) { - return goldRepository.findByMinimumPurity(purity).stream() - .map(goldMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public GoldModel createGold(GoldModel model) { - Gold entity = goldMapper.toEntity(model); - entity = goldRepository.save(entity); - return goldMapper.toModel(entity); - } - - @Transactional - public GoldModel updateGold(Long id, GoldModel model) { - Gold existingGold = goldRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Gold not found: " + id)); - Gold updatedGold = goldMapper.updateEntity(existingGold, model); - updatedGold = goldRepository.save(updatedGold); - return goldMapper.toModel(updatedGold); - } - - @Transactional - public void deleteGold(Long id) { - goldRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/MutualFundService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/MutualFundService.java deleted file mode 100644 index 4ab138c..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/MutualFundService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.mutualfund.MutualFund; -import com.am.common.amcommondata.model.asset.mutualfund.MutualFundModel; -import com.am.common.amcommondata.repository.asset.mutualfund.MutualFundRepository; -import com.am.common.amcommondata.service.mapper.asset.MutualFundMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class MutualFundService { - private final MutualFundRepository mutualFundRepository; - private final MutualFundMapper mutualFundMapper; - - public MutualFundModel getMutualFund(Long id) { - return mutualFundRepository.findById(id) - .map(mutualFundMapper::toModel) - .orElseThrow(() -> new RuntimeException("Mutual Fund not found: " + id)); - } - - public List getAllMutualFunds() { - return mutualFundRepository.findAll().stream() - .map(mutualFundMapper::toModel) - .collect(Collectors.toList()); - } - - public List getMutualFundsByCategory(String category) { - return mutualFundRepository.findByFundInfoFundCategory(category).stream() - .map(mutualFundMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public MutualFundModel createMutualFund(MutualFundModel model) { - MutualFund entity = mutualFundMapper.toEntity(model); - entity = mutualFundRepository.save(entity); - return mutualFundMapper.toModel(entity); - } - - @Transactional - public MutualFundModel updateMutualFund(Long id, MutualFundModel model) { - MutualFund existingFund = mutualFundRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Mutual Fund not found: " + id)); - MutualFund updatedFund = mutualFundMapper.updateEntity(existingFund, model); - updatedFund = mutualFundRepository.save(updatedFund); - return mutualFundMapper.toModel(updatedFund); - } - - @Transactional - public void deleteMutualFund(Long id) { - mutualFundRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/NPSService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/NPSService.java deleted file mode 100644 index 111bff9..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/NPSService.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.domain.asset.pension.NPS; -import com.am.common.amcommondata.model.asset.pension.NPSModel; -import com.am.common.amcommondata.repository.asset.pension.PensionRepository; -import com.am.common.amcommondata.service.mapper.asset.NPSMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class NPSService { - private final PensionRepository pensionRepository; - private final NPSMapper npsMapper; - - public NPSModel getNPS(Long id) { - return pensionRepository.findById(id) - .map(npsMapper::toModel) - .orElseThrow(() -> new RuntimeException("NPS not found: " + id)); - } - - public List getAllNPS() { - return pensionRepository.findAll().stream() - .map(npsMapper::toModel) - .collect(Collectors.toList()); - } - - public List getNPSBySchemeType(String schemeType) { - return pensionRepository.findBySchemeType(schemeType).stream() - .map(npsMapper::toModel) - .collect(Collectors.toList()); - } - - public List getHighPerformingSchemes(Double minReturn) { - return pensionRepository.findHighPerformingSchemes(minReturn).stream() - .map(npsMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public NPSModel createNPS(NPSModel model) { - NPS entity = npsMapper.toEntity(model); - entity = pensionRepository.save(entity); - return npsMapper.toModel(entity); - } - - @Transactional - public NPSModel updateNPS(Long id, NPSModel model) { - NPS existingNPS = pensionRepository.findById(id) - .orElseThrow(() -> new RuntimeException("NPS not found: " + id)); - NPS updatedNPS = npsMapper.updateEntity(existingNPS, model); - updatedNPS = pensionRepository.save(updatedNPS); - return npsMapper.toModel(updatedNPS); - } - - @Transactional - public void deleteNPS(Long id) { - pensionRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/RealEstateService.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/RealEstateService.java deleted file mode 100644 index e43bdd0..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/asset/RealEstateService.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.am.common.amcommondata.service.asset; - -import com.am.common.amcommondata.model.asset.realestate.RealEstateModel; -import com.am.common.amcommondata.repository.asset.realestate.RealEstateRepository; -import com.am.common.amcommondata.service.mapper.asset.RealEstateMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class RealEstateService { - private final RealEstateRepository realEstateRepository; - private final RealEstateMapper realEstateMapper; - - public RealEstateModel getRealEstate(Long id) { - return realEstateRepository.findById(id) - .map(realEstateMapper::toModel) - .orElseThrow(() -> new RuntimeException("Real Estate not found: " + id)); - } - - public List getAllRealEstates() { - return realEstateRepository.findAll().stream() - .map(realEstateMapper::toModel) - .collect(Collectors.toList()); - } - - public List getRealEstatesByType(String propertyType) { - return realEstateRepository.findByPropertyType(propertyType).stream() - .map(realEstateMapper::toModel) - .collect(Collectors.toList()); - } - - public List getRentGeneratingProperties() { - return realEstateRepository.findRentGeneratingProperties().stream() - .map(realEstateMapper::toModel) - .collect(Collectors.toList()); - } - - @Transactional - public RealEstateModel createRealEstate(RealEstateModel realEstateModel) { - var realEstate = realEstateMapper.toEntity(realEstateModel); - realEstate = realEstateRepository.save(realEstate); - return realEstateMapper.toModel(realEstate); - } - - @Transactional - public RealEstateModel updateRealEstate(Long id, RealEstateModel realEstateModel) { - var existingRealEstate = realEstateRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Real Estate not found: " + id)); - var updatedRealEstate = realEstateMapper.updateEntity(existingRealEstate, realEstateModel); - updatedRealEstate = realEstateRepository.save(updatedRealEstate); - return realEstateMapper.toModel(updatedRealEstate); - } - - @Transactional - public void deleteRealEstate(Long id) { - realEstateRepository.deleteById(id); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/AssetMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/AssetMapper.java deleted file mode 100644 index a0e2805..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/AssetMapper.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.am.common.amcommondata.service.mapper; - -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.model.asset.AssetModel; -import org.springframework.stereotype.Component; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class AssetMapper { - private final MarketDataMapper marketDataMapper; - - public AssetModel toModel(Asset entity) { - if (entity == null) { - return null; - } - - return AssetModel.builder() - //.id(entity.getId()) - .symbol(entity.getSymbol()) - .name(entity.getName()) - .description(entity.getDescription()) - .assetType(entity.getAssetType()) - .buyingPlatform(entity.getBuyingPlatform()) - //.marketData(marketDataMapper.toModel(entity.getMarketData())) - //.currentValue(calculateCurrentValue(entity)) - .investmentValue(calculateInvestmentValue(entity)) - .profitLoss(calculateProfitLoss(entity)) - .profitLossPercentage(calculateProfitLossPercentage(entity)) - .createdAt(entity.getCreatedAt()) - .updatedAt(entity.getModifiedAt()) - //.isActive(entity.isActive()) - .build(); - } - - // private Double calculateCurrentValue(Asset asset) { - // if (asset.getMarketData() == null || asset.getMarketData().getMarketPrice() == null) { - // return 0.0; - // } - // return asset.getMarketData().getMarketPrice(); - // } - - private Double calculateInvestmentValue(Asset asset) { - // Implementation depends on your business logic - return 0.0; - } - - private Double calculateProfitLoss(Asset asset) { - //Double currentValue = calculateCurrentValue(asset); - Double investmentValue = calculateInvestmentValue(asset); - //return currentValue - investmentValue; - return 0.0; - } - - private Double calculateProfitLossPercentage(Asset asset) { - Double profitLoss = calculateProfitLoss(asset); - Double investmentValue = calculateInvestmentValue(asset); - if (investmentValue == 0) { - return 0.0; - } - return (profitLoss / investmentValue) * 100; - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/MarketDataMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/MarketDataMapper.java deleted file mode 100644 index 9c5cc0b..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/MarketDataMapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.am.common.amcommondata.service.mapper; - -import com.am.common.amcommondata.domain.common.MarketData; -import com.am.common.amcommondata.model.asset.common.MarketDataModel; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {TimeSeriesDataMapper.class, OHLCVMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface MarketDataMapper { - - MarketData toEntity(MarketDataModel model); - - MarketDataModel toModel(MarketData entity); - - MarketData updateEntity(@MappingTarget MarketData entity, MarketDataModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/OHLCVMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/OHLCVMapper.java deleted file mode 100644 index 08a5fa8..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/OHLCVMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.am.common.amcommondata.service.mapper; - -import com.am.common.amcommondata.domain.common.OHLCV; -import com.am.common.amcommondata.model.asset.common.OHLCVModel; -import org.mapstruct.Mapper; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface OHLCVMapper { - - OHLCV toEntity(OHLCVModel model); - - OHLCVModel toModel(OHLCV entity); - - OHLCV updateEntity(@MappingTarget OHLCV entity, OHLCVModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/PortfolioMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/PortfolioMapper.java deleted file mode 100644 index 29214d1..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/PortfolioMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.am.common.amcommondata.service.mapper; - -import com.am.common.amcommondata.domain.portfolio.Portfolio; -import com.am.common.amcommondata.domain.asset.Asset; -import com.am.common.amcommondata.model.PortfolioModel; -import com.am.common.amcommondata.model.asset.AssetModel; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.Set; -import java.util.stream.Collectors; - -@Mapper(componentModel = "spring", - uses = {AssetMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public abstract class PortfolioMapper { - - @Autowired - protected AssetMapper assetMapper; - - @Mapping(target = "totalValue", expression = "java(calculateTotalValue(entity))") - @Mapping(target = "assetCount", expression = "java(entity.getAssets().size())") - @Mapping(target = "assetTypes", expression = "java(entity.getAssets().stream().map(asset -> asset.getAssetType().name()).collect(java.util.stream.Collectors.toSet()))") - @Mapping(target = "status", expression = "java(determinePortfolioStatus(entity))") - @Mapping(target = "assets", expression = "java(mapAssets(entity.getAssets()))") - public abstract PortfolioModel toModel(Portfolio entity); - - @Mapping(target = "assets", ignore = true) - public abstract Portfolio toEntity(PortfolioModel model); - - @Mapping(target = "assets", ignore = true) - public abstract Portfolio updateEntity(@MappingTarget Portfolio entity, PortfolioModel model); - - protected Double calculateTotalValue(Portfolio portfolio) { - if (portfolio == null || portfolio.getAssets() == null) { - return 0.0; - } - return portfolio.getAssets().stream() - .filter(asset -> asset.getCurrentValue() != null) - .mapToDouble(asset -> asset.getCurrentValue()) - .sum(); - } - - protected String determinePortfolioStatus(Portfolio portfolio) { - if (portfolio == null || portfolio.getAssets() == null || portfolio.getAssets().isEmpty()) { - return "EMPTY"; - } - return "ACTIVE"; - } - - protected Set mapAssets(Set assets) { - if (assets == null) { - return null; - } - return assets.stream() - .map(assetMapper::toModel) - .collect(Collectors.toSet()); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/TimeSeriesDataMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/TimeSeriesDataMapper.java deleted file mode 100644 index b8e6c73..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/TimeSeriesDataMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.am.common.amcommondata.service.mapper; - -import com.am.common.amcommondata.domain.common.TimeSeriesData; -import com.am.common.amcommondata.model.asset.common.TimeSeriesDataModel; -import org.mapstruct.Mapper; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {OHLCVMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface TimeSeriesDataMapper { - - TimeSeriesData toEntity(TimeSeriesDataModel model); - - TimeSeriesDataModel toModel(TimeSeriesData entity); - - TimeSeriesData updateEntity(@MappingTarget TimeSeriesData entity, TimeSeriesDataModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/BondMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/BondMapper.java deleted file mode 100644 index 210941c..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/BondMapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.bond.Bond; -import com.am.common.amcommondata.model.asset.bond.BondModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface BondMapper { - - Bond toEntity(BondModel model); - - BondModel toModel(Bond entity); - - Bond updateEntity(@MappingTarget Bond entity, BondModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/CryptocurrencyMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/CryptocurrencyMapper.java deleted file mode 100644 index 6dfd25c..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/CryptocurrencyMapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.cryptocurrency.Cryptocurrency; -import com.am.common.amcommondata.model.asset.cryptocurrency.CryptocurrencyModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface CryptocurrencyMapper { - - Cryptocurrency toEntity(CryptocurrencyModel model); - - CryptocurrencyModel toModel(Cryptocurrency entity); - - Cryptocurrency updateEntity(@MappingTarget Cryptocurrency entity, CryptocurrencyModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/EquityMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/EquityMapper.java deleted file mode 100644 index 7cfef58..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/EquityMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.equity.Equity; -import com.am.common.amcommondata.model.asset.equity.EquityModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; - -import java.util.UUID; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface EquityMapper { - - @Mapping(target = "id", ignore = true) - Equity toEntity(EquityModel model); - - EquityModel toModel(Equity entity); - - @Mapping(target = "id", ignore = true) - Equity updateEntity(@MappingTarget Equity entity, EquityModel model); - - default UUID map(Long value) { - return value == null ? null : new UUID(0, value); - } - - default Long map(UUID value) { - return value == null ? null : value.getLeastSignificantBits(); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/FixedDepositMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/FixedDepositMapper.java deleted file mode 100644 index e52649c..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/FixedDepositMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.fixeddeposit.FixedDeposit; -import com.am.common.amcommondata.model.asset.fixeddeposit.FixedDepositModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; - -import java.util.UUID; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", uses = { - MarketDataMapper.class }, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface FixedDepositMapper { - - FixedDeposit toEntity(FixedDepositModel model); - - FixedDepositModel toModel(FixedDeposit entity); - - FixedDeposit updateEntity(@MappingTarget FixedDeposit entity, FixedDepositModel model); - - default UUID map(Long value) { - return value == null ? null : new UUID(0, value); - } - - default Long map(UUID value) { - return value == null ? null : value.getLeastSignificantBits(); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/GoldMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/GoldMapper.java deleted file mode 100644 index a6c16b0..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/GoldMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.gold.Gold; -import com.am.common.amcommondata.model.asset.gold.GoldModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; - -import java.util.UUID; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", uses = { - MarketDataMapper.class }, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface GoldMapper { - - @Mapping(target = "id", ignore = true) - Gold toEntity(GoldModel model); - - GoldModel toModel(Gold entity); - - @Mapping(target = "id", ignore = true) - Gold updateEntity(@MappingTarget Gold entity, GoldModel model); - - default UUID map(Long value) { - return value == null ? null : new UUID(0, value); - } - - default Long map(UUID value) { - return value == null ? null : value.getLeastSignificantBits(); - } -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/MutualFundMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/MutualFundMapper.java deleted file mode 100644 index 112cecd..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/MutualFundMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.mutualfund.MutualFund; -import com.am.common.amcommondata.model.asset.mutualfund.MutualFundModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface MutualFundMapper { - - @Mapping(target = "id", ignore = true) - MutualFund toEntity(MutualFundModel model); - - MutualFundModel toModel(MutualFund entity); - - @Mapping(target = "id", ignore = true) - MutualFund updateEntity(@MappingTarget MutualFund entity, MutualFundModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/NPSMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/NPSMapper.java deleted file mode 100644 index 9e3d872..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/NPSMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.pension.NPS; -import com.am.common.amcommondata.model.asset.pension.NPSModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface NPSMapper { - - @Mapping(target = "id", ignore = true) - NPS toEntity(NPSModel model); - - NPSModel toModel(NPS entity); - - @Mapping(target = "id", ignore = true) - NPS updateEntity(@MappingTarget NPS entity, NPSModel model); -} diff --git a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/RealEstateMapper.java b/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/RealEstateMapper.java deleted file mode 100644 index cca9110..0000000 --- a/am-common-data-service/src/main/java/com/am/common/amcommondata/service/mapper/asset/RealEstateMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.am.common.amcommondata.service.mapper.asset; - -import com.am.common.amcommondata.domain.asset.realestate.RealEstate; -import com.am.common.amcommondata.model.asset.realestate.RealEstateModel; -import com.am.common.amcommondata.service.mapper.MarketDataMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; - -@Mapper(componentModel = "spring", - uses = {MarketDataMapper.class}, - nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -public interface RealEstateMapper { - - @Mapping(target = "id", ignore = true) - RealEstate toEntity(RealEstateModel model); - - RealEstateModel toModel(RealEstate entity); - - @Mapping(target = "id", ignore = true) - RealEstate updateEntity(@MappingTarget RealEstate entity, RealEstateModel model); -} diff --git a/am-common-data-service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/am-common-data-service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..167ab3f --- /dev/null +++ b/am-common-data-service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.am.common.amcommondata.config.SecurityServiceAutoConfiguration diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9e409fc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +version: '3.8' + +services: + influxdb: + image: influxdb:2.7 + container_name: am_influxdb + ports: + - "8086:8086" + environment: + - DOCKER_INFLUXDB_INIT_MODE=setup + - DOCKER_INFLUXDB_INIT_USERNAME=admin + - DOCKER_INFLUXDB_INIT_PASSWORD=password + - DOCKER_INFLUXDB_INIT_ORG=am_investment + - DOCKER_INFLUXDB_INIT_BUCKET=market_data + - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-super-secret-auth-token + volumes: + - influxdb_data:/var/lib/influxdb2 + - influxdb_config:/etc/influxdb2 + networks: + - am_network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8086/health"] + interval: 30s + timeout: 10s + retries: 5 + + chronograf: + image: chronograf:latest + container_name: am_chronograf + ports: + - "8888:8888" + environment: + - INFLUXDB_URL=http://influxdb:8086 + - INFLUXDB_TOKEN=my-super-secret-auth-token + - INFLUXDB_ORG=am_investment + depends_on: + - influxdb + volumes: + - chronograf_data:/var/lib/chronograf + networks: + - am_network + +volumes: + influxdb_data: + influxdb_config: + chronograf_data: + +networks: + am_network: + name: am_network diff --git a/pom.xml b/pom.xml index 245414c..46b07ee 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,15 @@ com.am.common am-common-data-parent - 1.2.0-SNAPSHOT + 1.2.10-SNAPSHOT pom am-common-data-model - am-common-data-persistence + am-common-data-service + am-common-data-app + am-common-data-mongo @@ -21,13 +23,16 @@ ${java.version} ${java.version} UTF-8 - 2.7.0 + 3.1.0 1.5.3.Final - 1.18.22 + 1.18.30 3.1.0 3.0.2 - 2.13.0 - 1.7.32 + 2.15.0 + 2.0.7 + 1.19.3 + 42.7.1 + 3.24.2 @@ -38,6 +43,11 @@ spring-boot-starter-web ${spring-boot.version} + + org.springframework.boot + spring-boot-starter-data-mongodb + ${spring-boot.version} + org.springframework.boot spring-boot-starter-data-jpa @@ -100,11 +110,37 @@ ${slf4j.version} + + + - junit - junit - 4.13.2 + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + org.testcontainers + postgresql + ${testcontainers.version} + test + + + org.assertj + assertj-core + ${assertj.version} test