From 7c73298f1740adbdddba239aeb120f70e72aef93 Mon Sep 17 00:00:00 2001 From: Don Strawsburg Date: Thu, 1 Jan 2026 00:41:44 -0500 Subject: [PATCH] adding comments --- .../battlbuilder/dto/EmailRequestDto.java | 41 +- .../goforward/battlbuilder/model/Account.java | 184 ++++++++ .../battlbuilder/model/AuthToken.java | 102 +++++ .../goforward/battlbuilder/model/Brand.java | 110 +++++ .../goforward/battlbuilder/model/Build.java | 129 ++++++ .../battlbuilder/model/BuildItem.java | 124 +++++ .../battlbuilder/model/EmailRequest.java | 183 +++++++- .../battlbuilder/model/Merchant.java | 117 +++++ .../battlbuilder/model/Platform.java | 83 ++++ .../goforward/battlbuilder/model/Product.java | 429 ++++++++++++++++++ .../battlbuilder/model/ProductOffer.java | 170 +++++++ .../goforward/battlbuilder/model/User.java | 263 ++++++++++- 12 files changed, 1930 insertions(+), 5 deletions(-) diff --git a/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java b/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java index 4c375db..99b74fd 100644 --- a/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java +++ b/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java @@ -1,31 +1,70 @@ package group.goforward.battlbuilder.dto; -// DTO for request +/** + * Data Transfer Object (DTO) for email request operations. + * This class encapsulates the data required to send an email, including + * the recipient address, subject line, and message body. + */ public class EmailRequestDto { + /** The email address of the recipient. */ private String recipient; + + /** The subject line of the email. */ private String subject; + + /** The body content of the email message. */ private String body; + /** + * Gets the recipient email address. + * + * @return the recipient email address + */ public String getRecipient() { return recipient; } + /** + * Sets the recipient email address. + * + * @param recipient the recipient email address to set + */ public void setRecipient(String recipient) { this.recipient = recipient; } + /** + * Gets the email subject line. + * + * @return the email subject line + */ public String getSubject() { return subject; } + /** + * Sets the email subject line. + * + * @param subject the email subject line to set + */ public void setSubject(String subject) { this.subject = subject; } + /** + * Gets the email body content. + * + * @return the email body content + */ public String getBody() { return body; } + /** + * Sets the email body content. + * + * @param body the email body content to set + */ public void setBody(String body) { this.body = body; } diff --git a/src/main/java/group/goforward/battlbuilder/model/Account.java b/src/main/java/group/goforward/battlbuilder/model/Account.java index f898479..8303f3b 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Account.java +++ b/src/main/java/group/goforward/battlbuilder/model/Account.java @@ -9,184 +9,368 @@ import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; import java.util.UUID; +/** + * Entity representing an OAuth account linked to a user. + * This class stores authentication and authorization information for third-party + * OAuth providers (e.g., Google, Facebook, GitHub) including access tokens, + * refresh tokens, and session state. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "accounts") public class Account { + /** The primary key identifier for the account. */ @Id @ColumnDefault("gen_random_uuid()") @Column(name = "id", nullable = false) private UUID id; + /** A unique identifier (UUID) for the account. */ @ColumnDefault("gen_random_uuid()") @Column(name = "uuid") private UUID uuid; + /** The UUID of the user this account belongs to. */ @Column(name = "user_id", nullable = false) private UUID userId; + /** The type of OAuth account (e.g., "oauth", "oidc"). */ @Column(name = "type", nullable = false, length = Integer.MAX_VALUE) private String type; + /** The OAuth provider name (e.g., "google", "github", "facebook"). */ @Column(name = "provider", nullable = false, length = Integer.MAX_VALUE) private String provider; + /** The account ID from the OAuth provider. */ @Column(name = "provider_account_id", nullable = false, length = Integer.MAX_VALUE) private String providerAccountId; + /** The OAuth refresh token used to obtain new access tokens. */ @Column(name = "refresh_token", length = Integer.MAX_VALUE) private String refreshToken; + /** The OAuth access token for API requests to the provider. */ @Column(name = "access_token", length = Integer.MAX_VALUE) private String accessToken; + /** The expiration timestamp of the access token (Unix timestamp). */ @Column(name = "expires_at") private Integer expiresAt; + /** The type of token (e.g., "Bearer"). */ @Column(name = "token_type", length = Integer.MAX_VALUE) private String tokenType; + /** The OpenID Connect ID token (if applicable). */ @Column(name = "id_token", length = Integer.MAX_VALUE) private String idToken; + /** The OAuth session state for maintaining session continuity. */ @Column(name = "session_state", length = Integer.MAX_VALUE) private String sessionState; + /** The OAuth scopes granted to this account. */ @Column(name = "scope", length = Integer.MAX_VALUE) private String scope; + /** The timestamp when this account was created. */ @Column(name = "created_at") private OffsetDateTime createdAt; + /** The timestamp when this account was last updated. */ @Column(name = "updated_at") private OffsetDateTime updatedAt; + /** The timestamp when this account was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private OffsetDateTime deletedAt; + /** + * Gets the primary key identifier for the account. + * + * @return the account ID + */ public UUID getId() { return id; } + /** + * Sets the primary key identifier for the account. + * + * @param id the account ID to set + */ public void setId(UUID id) { this.id = id; } + /** + * Gets the unique identifier (UUID) for the account. + * + * @return the account UUID + */ public UUID getUuid() { return uuid; } + /** + * Sets the unique identifier (UUID) for the account. + * + * @param uuid the account UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the UUID of the user this account belongs to. + * + * @return the user UUID + */ public UUID getUserId() { return userId; } + /** + * Sets the UUID of the user this account belongs to. + * + * @param userId the user UUID to set + */ public void setUserId(UUID userId) { this.userId = userId; } + /** + * Gets the type of OAuth account. + * + * @return the account type (e.g., "oauth", "oidc") + */ public String getType() { return type; } + /** + * Sets the type of OAuth account. + * + * @param type the account type to set + */ public void setType(String type) { this.type = type; } + /** + * Gets the OAuth provider name. + * + * @return the provider name (e.g., "google", "github", "facebook") + */ public String getProvider() { return provider; } + /** + * Sets the OAuth provider name. + * + * @param provider the provider name to set + */ public void setProvider(String provider) { this.provider = provider; } + /** + * Gets the account ID from the OAuth provider. + * + * @return the provider account ID + */ public String getProviderAccountId() { return providerAccountId; } + /** + * Sets the account ID from the OAuth provider. + * + * @param providerAccountId the provider account ID to set + */ public void setProviderAccountId(String providerAccountId) { this.providerAccountId = providerAccountId; } + /** + * Gets the OAuth refresh token. + * + * @return the refresh token, or null if not available + */ public String getRefreshToken() { return refreshToken; } + /** + * Sets the OAuth refresh token. + * + * @param refreshToken the refresh token to set + */ public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } + /** + * Gets the OAuth access token. + * + * @return the access token, or null if not available + */ public String getAccessToken() { return accessToken; } + /** + * Sets the OAuth access token. + * + * @param accessToken the access token to set + */ public void setAccessToken(String accessToken) { this.accessToken = accessToken; } + /** + * Gets the expiration timestamp of the access token. + * + * @return the expiration timestamp as a Unix timestamp, or null if not set + */ public Integer getExpiresAt() { return expiresAt; } + /** + * Sets the expiration timestamp of the access token. + * + * @param expiresAt the expiration timestamp as a Unix timestamp + */ public void setExpiresAt(Integer expiresAt) { this.expiresAt = expiresAt; } + /** + * Gets the token type. + * + * @return the token type (e.g., "Bearer"), or null if not set + */ public String getTokenType() { return tokenType; } + /** + * Sets the token type. + * + * @param tokenType the token type to set + */ public void setTokenType(String tokenType) { this.tokenType = tokenType; } + /** + * Gets the OpenID Connect ID token. + * + * @return the ID token, or null if not available + */ public String getIdToken() { return idToken; } + /** + * Sets the OpenID Connect ID token. + * + * @param idToken the ID token to set + */ public void setIdToken(String idToken) { this.idToken = idToken; } + /** + * Gets the OAuth session state. + * + * @return the session state, or null if not set + */ public String getSessionState() { return sessionState; } + /** + * Sets the OAuth session state. + * + * @param sessionState the session state to set + */ public void setSessionState(String sessionState) { this.sessionState = sessionState; } + /** + * Gets the OAuth scopes granted to this account. + * + * @return the scopes, or null if not set + */ public String getScope() { return scope; } + /** + * Sets the OAuth scopes granted to this account. + * + * @param scope the scopes to set + */ public void setScope(String scope) { this.scope = scope; } + /** + * Gets the timestamp when this account was created. + * + * @return the creation timestamp, or null if not set + */ public OffsetDateTime getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when this account was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this account was last updated. + * + * @return the last update timestamp, or null if not set + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when this account was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this account was soft-deleted. + * + * @return the deletion timestamp, or null if the account is not deleted + */ public OffsetDateTime getDeletedAt() { return deletedAt; } + /** + * Sets the timestamp when this account was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } diff --git a/src/main/java/group/goforward/battlbuilder/model/AuthToken.java b/src/main/java/group/goforward/battlbuilder/model/AuthToken.java index 0a5984c..72c20e7 100644 --- a/src/main/java/group/goforward/battlbuilder/model/AuthToken.java +++ b/src/main/java/group/goforward/battlbuilder/model/AuthToken.java @@ -3,6 +3,13 @@ package group.goforward.battlbuilder.model; import jakarta.persistence.*; import java.time.OffsetDateTime; +/** + * Entity representing an authentication token. + * Tokens are used for beta verification, magic login links, and password resets. + * Tokens are hashed before storage and can be consumed and expired. + * + * @see jakarta.persistence.Entity + */ @Entity @Table( name = "auth_tokens", @@ -13,60 +20,155 @@ import java.time.OffsetDateTime; ) public class AuthToken { + /** + * Enumeration of token types. + */ public enum TokenType { + /** Token for beta access verification. */ BETA_VERIFY, + /** Token for magic link login. */ MAGIC_LOGIN, + /** Token for password reset. */ PASSWORD_RESET } + /** The primary key identifier for the token. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + /** The email address associated with this token. */ @Column(nullable = false) private String email; + /** The type of token. */ @Enumerated(EnumType.STRING) @Column(nullable = false, length = 32) private TokenType type; + /** The hashed token value. */ @Column(name = "token_hash", nullable = false, length = 64) private String tokenHash; + /** The timestamp when this token expires. */ @Column(name = "expires_at", nullable = false) private OffsetDateTime expiresAt; + /** The timestamp when this token was consumed/used. */ @Column(name = "consumed_at") private OffsetDateTime consumedAt; + /** The timestamp when this token was created. */ @Column(name = "created_at", nullable = false) private OffsetDateTime createdAt; // getters/setters + /** + * Gets the primary key identifier for the token. + * + * @return the token ID + */ public Long getId() { return id; } + /** + * Gets the email address associated with this token. + * + * @return the email address + */ public String getEmail() { return email; } + + /** + * Sets the email address associated with this token. + * + * @param email the email address to set + */ public void setEmail(String email) { this.email = email; } + /** + * Gets the type of token. + * + * @return the token type + */ public TokenType getType() { return type; } + + /** + * Sets the type of token. + * + * @param type the token type to set + */ public void setType(TokenType type) { this.type = type; } + /** + * Gets the hashed token value. + * + * @return the token hash + */ public String getTokenHash() { return tokenHash; } + + /** + * Sets the hashed token value. + * + * @param tokenHash the token hash to set + */ public void setTokenHash(String tokenHash) { this.tokenHash = tokenHash; } + /** + * Gets the timestamp when this token expires. + * + * @return the expiration timestamp + */ public OffsetDateTime getExpiresAt() { return expiresAt; } + + /** + * Sets the timestamp when this token expires. + * + * @param expiresAt the expiration timestamp to set + */ public void setExpiresAt(OffsetDateTime expiresAt) { this.expiresAt = expiresAt; } + /** + * Gets the timestamp when this token was consumed/used. + * + * @return the consumed timestamp, or null if not yet consumed + */ public OffsetDateTime getConsumedAt() { return consumedAt; } + + /** + * Sets the timestamp when this token was consumed/used. + * + * @param consumedAt the consumed timestamp to set + */ public void setConsumedAt(OffsetDateTime consumedAt) { this.consumedAt = consumedAt; } + /** + * Gets the timestamp when this token was created. + * + * @return the creation timestamp + */ public OffsetDateTime getCreatedAt() { return createdAt; } + + /** + * Sets the timestamp when this token was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Checks if this token has been consumed/used. + * + * @return true if the token has been consumed, false otherwise + */ @Transient public boolean isConsumed() { return consumedAt != null; } + /** + * Checks if this token has expired at the given time. + * + * @param now the current time to check against + * @return true if the token has expired, false otherwise + */ @Transient public boolean isExpired(OffsetDateTime now) { return expiresAt.isBefore(now); } } \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/model/Brand.java b/src/main/java/group/goforward/battlbuilder/model/Brand.java index ffdab93..47a7fd0 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Brand.java +++ b/src/main/java/group/goforward/battlbuilder/model/Brand.java @@ -7,40 +7,59 @@ import org.hibernate.annotations.UpdateTimestamp; import java.time.Instant; import java.util.UUID; +/** + * Entity representing a brand/manufacturer of products. + * Brands are associated with products and contain information such as name, website, and logo. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "brands") public class Brand { + /** The primary key identifier for the brand. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + /** The brand name. Must be unique. */ @Column(nullable = false, unique = true) private String name; + /** A unique identifier (UUID) for the brand. */ @Column(nullable = false, unique = true) private UUID uuid; + /** The URL-friendly slug for the brand. Must be unique. */ @Column(unique = true) private String slug; + /** The brand's website URL. */ @Column(name = "website") private String website; + /** The URL of the brand's logo image. */ @Column(name = "logo_url") private String logoUrl; + /** The timestamp when this brand was created. */ @CreationTimestamp @Column(name = "created_at", nullable = false, updatable = false) private Instant createdAt; + /** The timestamp when this brand was last updated. */ @UpdateTimestamp @Column(name = "updated_at", nullable = false) private Instant updatedAt; + /** The timestamp when this brand was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private Instant deletedAt; + /** + * Lifecycle hook called before persisting a new entity. + * Initializes the UUID and generates a slug from the name if not already set. + */ @PrePersist public void prePersist() { if (uuid == null) { @@ -52,74 +71,165 @@ public class Brand { } // Getters and Setters + + /** + * Gets the primary key identifier for the brand. + * + * @return the brand ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the brand. + * + * @param id the brand ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the brand name. + * + * @return the brand name + */ public String getName() { return name; } + /** + * Sets the brand name. + * + * @param name the brand name to set + */ public void setName(String name) { this.name = name; } + /** + * Gets the unique identifier (UUID) for the brand. + * + * @return the brand UUID + */ public UUID getUuid() { return uuid; } + /** + * Sets the unique identifier (UUID) for the brand. + * + * @param uuid the brand UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the URL-friendly slug for the brand. + * + * @return the slug + */ public String getSlug() { return slug; } + /** + * Sets the URL-friendly slug for the brand. + * + * @param slug the slug to set + */ public void setSlug(String slug) { this.slug = slug; } + /** + * Gets the brand's website URL. + * + * @return the website URL, or null if not set + */ public String getWebsite() { return website; } + /** + * Sets the brand's website URL. + * + * @param website the website URL to set + */ public void setWebsite(String website) { this.website = website; } + /** + * Gets the URL of the brand's logo image. + * + * @return the logo URL, or null if not set + */ public String getLogoUrl() { return logoUrl; } + /** + * Sets the URL of the brand's logo image. + * + * @param logoUrl the logo URL to set + */ public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; } + /** + * Gets the timestamp when this brand was created. + * + * @return the creation timestamp + */ public Instant getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when this brand was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this brand was last updated. + * + * @return the last update timestamp + */ public Instant getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when this brand was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this brand was soft-deleted. + * + * @return the deletion timestamp, or null if the brand is not deleted + */ public Instant getDeletedAt() { return deletedAt; } + /** + * Sets the timestamp when this brand was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(Instant deletedAt) { this.deletedAt = deletedAt; } diff --git a/src/main/java/group/goforward/battlbuilder/model/Build.java b/src/main/java/group/goforward/battlbuilder/model/Build.java index e857cf8..0ba34f6 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Build.java +++ b/src/main/java/group/goforward/battlbuilder/model/Build.java @@ -7,6 +7,12 @@ import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; import java.util.UUID; +/** + * Entity representing a build (firearm configuration) created by a user. + * A build contains a collection of parts and can be public or private. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "builds") public class Build { @@ -14,6 +20,8 @@ public class Build { // ----------------------------------------------------- // Primary key (Postgres GENERATED ALWAYS AS IDENTITY) // ----------------------------------------------------- + + /** The primary key identifier for the build. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) @@ -22,6 +30,8 @@ public class Build { // ----------------------------------------------------- // UUID (DB default gen_random_uuid()) // ----------------------------------------------------- + + /** A unique identifier (UUID) for the build. */ @ColumnDefault("gen_random_uuid()") @Column(name = "uuid", nullable = false) private UUID uuid; @@ -30,18 +40,24 @@ public class Build { // Ownership (nullable; ON DELETE SET NULL) // NOTE: Keeping this as Integer for now to avoid forcing a User entity relationship // ----------------------------------------------------- + + /** The ID of the user who owns this build. Can be null if the user is deleted. */ @Column(name = "user_id") private Integer userId; // ----------------------------------------------------- // Main fields // ----------------------------------------------------- + + /** The title of the build. */ @Column(name = "title", nullable = false) private String title; + /** A description of the build. */ @Column(name = "description") private String description; + /** Whether this build is publicly visible. Defaults to false. */ @ColumnDefault("false") @Column(name = "is_public", nullable = false) private Boolean isPublic = false; @@ -49,20 +65,29 @@ public class Build { // ----------------------------------------------------- // Timestamps // ----------------------------------------------------- + + /** The timestamp when this build was created. */ @ColumnDefault("now()") @Column(name = "created_at", nullable = false) private OffsetDateTime createdAt; + /** The timestamp when this build was last updated. */ @ColumnDefault("now()") @Column(name = "updated_at", nullable = false) private OffsetDateTime updatedAt; + /** The timestamp when this build was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private OffsetDateTime deletedAt; // ----------------------------------------------------- // Hibernate lifecycle // ----------------------------------------------------- + + /** + * Lifecycle hook called before persisting a new entity. + * Initializes the UUID and timestamps if not already set. + */ @PrePersist public void prePersist() { if (uuid == null) uuid = UUID.randomUUID(); @@ -72,6 +97,10 @@ public class Build { if (isPublic == null) isPublic = false; } + /** + * Lifecycle hook called before updating an existing entity. + * Updates the updatedAt timestamp. + */ @PreUpdate public void preUpdate() { updatedAt = OffsetDateTime.now(); @@ -80,30 +109,130 @@ public class Build { // ----------------------------------------------------- // Getters / Setters // ----------------------------------------------------- + + /** + * Gets the primary key identifier for the build. + * + * @return the build ID + */ public Integer getId() { return id; } + + /** + * Sets the primary key identifier for the build. + * + * @param id the build ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the unique identifier (UUID) for the build. + * + * @return the build UUID + */ public UUID getUuid() { return uuid; } + + /** + * Sets the unique identifier (UUID) for the build. + * + * @param uuid the build UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the ID of the user who owns this build. + * + * @return the user ID, or null if the user has been deleted + */ public Integer getUserId() { return userId; } + + /** + * Sets the ID of the user who owns this build. + * + * @param userId the user ID to set + */ public void setUserId(Integer userId) { this.userId = userId; } + /** + * Gets the title of the build. + * + * @return the title + */ public String getTitle() { return title; } + + /** + * Sets the title of the build. + * + * @param title the title to set + */ public void setTitle(String title) { this.title = title; } + /** + * Gets the description of the build. + * + * @return the description, or null if not set + */ public String getDescription() { return description; } + + /** + * Sets the description of the build. + * + * @param description the description to set + */ public void setDescription(String description) { this.description = description; } + /** + * Gets whether this build is publicly visible. + * + * @return true if the build is public, false otherwise + */ public Boolean getIsPublic() { return isPublic; } + + /** + * Sets whether this build is publicly visible. + * + * @param isPublic true to make the build public, false to make it private + */ public void setIsPublic(Boolean isPublic) { this.isPublic = isPublic; } + /** + * Gets the timestamp when this build was created. + * + * @return the creation timestamp + */ public OffsetDateTime getCreatedAt() { return createdAt; } + + /** + * Sets the timestamp when this build was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this build was last updated. + * + * @return the last update timestamp + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + + /** + * Sets the timestamp when this build was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this build was soft-deleted. + * + * @return the deletion timestamp, or null if the build is not deleted + */ public OffsetDateTime getDeletedAt() { return deletedAt; } + + /** + * Sets the timestamp when this build was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } } \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/model/BuildItem.java b/src/main/java/group/goforward/battlbuilder/model/BuildItem.java index 225f28d..a4bfd1a 100644 --- a/src/main/java/group/goforward/battlbuilder/model/BuildItem.java +++ b/src/main/java/group/goforward/battlbuilder/model/BuildItem.java @@ -9,57 +9,77 @@ import org.hibernate.annotations.OnDeleteAction; import java.time.OffsetDateTime; import java.util.UUID; +/** + * Entity representing an item (product) in a build. + * A build item links a product to a build, specifying the slot, position, and quantity. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "build_items") public class BuildItem { + /** The primary key identifier for the build item. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** A unique identifier (UUID) for the build item. */ @NotNull @ColumnDefault("gen_random_uuid()") @Column(name = "uuid", nullable = false) private UUID uuid; + /** The build this item belongs to. */ @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "build_id", nullable = false) private Build build; + /** The product used in this build item. */ @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "product_id", nullable = false) private Product product; + /** The slot/role this product fills in the build (e.g., "upper_receiver", "barrel"). */ @NotNull @Column(name = "slot", nullable = false, length = Integer.MAX_VALUE) private String slot; + /** The position/order of this item within the slot. Defaults to 0. */ @NotNull @ColumnDefault("0") @Column(name = "\"position\"", nullable = false) private Integer position; + /** The quantity of this product in the build. Defaults to 1. */ @NotNull @ColumnDefault("1") @Column(name = "quantity", nullable = false) private Integer quantity; + /** The timestamp when this build item was created. */ @NotNull @ColumnDefault("now()") @Column(name = "created_at", nullable = false) private OffsetDateTime createdAt; + /** The timestamp when this build item was last updated. */ @ColumnDefault("CURRENT_TIMESTAMP") @Column(name = "updated_at") private OffsetDateTime updatedAt; + /** The timestamp when this build item was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private OffsetDateTime deletedAt; + /** + * Lifecycle hook called before persisting a new entity. + * Initializes the UUID and timestamps if not already set. + */ @PrePersist protected void onCreate() { if (this.uuid == null) this.uuid = java.util.UUID.randomUUID(); @@ -67,87 +87,191 @@ public class BuildItem { if (this.updatedAt == null) this.updatedAt = this.createdAt; } + /** + * Lifecycle hook called before updating an existing entity. + * Updates the updatedAt timestamp. + */ @PreUpdate protected void onUpdate() { this.updatedAt = OffsetDateTime.now(); } + /** + * Gets the primary key identifier for the build item. + * + * @return the build item ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the build item. + * + * @param id the build item ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the unique identifier (UUID) for the build item. + * + * @return the build item UUID + */ public UUID getUuid() { return uuid; } + /** + * Sets the unique identifier (UUID) for the build item. + * + * @param uuid the build item UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the build this item belongs to. + * + * @return the build + */ public Build getBuild() { return build; } + /** + * Sets the build this item belongs to. + * + * @param build the build to set + */ public void setBuild(Build build) { this.build = build; } + /** + * Gets the product used in this build item. + * + * @return the product + */ public Product getProduct() { return product; } + /** + * Sets the product used in this build item. + * + * @param product the product to set + */ public void setProduct(Product product) { this.product = product; } + /** + * Gets the slot/role this product fills in the build. + * + * @return the slot (e.g., "upper_receiver", "barrel") + */ public String getSlot() { return slot; } + /** + * Sets the slot/role this product fills in the build. + * + * @param slot the slot to set + */ public void setSlot(String slot) { this.slot = slot; } + /** + * Gets the position/order of this item within the slot. + * + * @return the position + */ public Integer getPosition() { return position; } + /** + * Sets the position/order of this item within the slot. + * + * @param position the position to set + */ public void setPosition(Integer position) { this.position = position; } + /** + * Gets the quantity of this product in the build. + * + * @return the quantity + */ public Integer getQuantity() { return quantity; } + /** + * Sets the quantity of this product in the build. + * + * @param quantity the quantity to set + */ public void setQuantity(Integer quantity) { this.quantity = quantity; } + /** + * Gets the timestamp when this build item was created. + * + * @return the creation timestamp + */ public OffsetDateTime getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when this build item was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this build item was last updated. + * + * @return the last update timestamp, or null if not set + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when this build item was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this build item was soft-deleted. + * + * @return the deletion timestamp, or null if the build item is not deleted + */ public OffsetDateTime getDeletedAt() { return deletedAt; } + /** + * Sets the timestamp when this build item was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } diff --git a/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java b/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java index ec77e8e..e6af124 100644 --- a/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java +++ b/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java @@ -4,6 +4,12 @@ import jakarta.persistence.*; import java.time.LocalDateTime; +/** + * Entity representing an email request/queue entry. + * Tracks email sending status, delivery, and engagement metrics (opens, clicks). + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "email_requests") @NamedQueries({ @@ -22,51 +28,68 @@ import java.time.LocalDateTime; }) public class EmailRequest { + /** The primary key identifier for the email request. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + /** The email address of the recipient. */ @Column(nullable = false) private String recipient; + /** The email subject line. */ @Column(nullable = false) private String subject; + /** The email body content. */ @Column(columnDefinition = "TEXT") private String body; + /** The template key used to generate this email (if applicable). */ @Column(name = "template_key", length = 100) private String templateKey; + /** The status of the email (PENDING, SENT, FAILED). */ @Enumerated(EnumType.STRING) @Column(nullable = false) private EmailStatus status; // PENDING, SENT, FAILED + /** The timestamp when the email was sent. */ @Column(name = "sent_at") private LocalDateTime sentAt; + /** The error message if the email failed to send. */ @Column(name = "error_message") private String errorMessage; + /** The timestamp when the email was first opened. */ @Column(name = "opened_at") private LocalDateTime openedAt; + /** The number of times the email has been opened. Defaults to 0. */ @Column(name = "open_count", nullable = false) private Integer openCount = 0; + /** The timestamp when a link in the email was first clicked. */ @Column(name = "clicked_at") private LocalDateTime clickedAt; + /** The number of times links in the email have been clicked. Defaults to 0. */ @Column(name = "click_count", nullable = false) private Integer clickCount = 0; + /** The timestamp when this email request was created. */ @Column(name = "created_at", nullable = false, updatable = false) private LocalDateTime createdAt; - // ✅ should be updatable + /** The timestamp when this email request was last updated. */ @Column(name = "updated_at", nullable = false) private LocalDateTime updatedAt; + /** + * Lifecycle hook called before persisting a new entity. + * Initializes timestamps and default values. + */ @PrePersist protected void onCreate() { LocalDateTime now = LocalDateTime.now(); @@ -78,6 +101,10 @@ public class EmailRequest { if (clickCount == null) clickCount = 0; } + /** + * Lifecycle hook called before updating an existing entity. + * Updates the updatedAt timestamp. + */ @PreUpdate protected void onUpdate() { updatedAt = LocalDateTime.now(); @@ -85,45 +112,199 @@ public class EmailRequest { // ===== Getters / Setters ===== + /** + * Gets the primary key identifier for the email request. + * + * @return the email request ID + */ public Long getId() { return id; } + + /** + * Sets the primary key identifier for the email request. + * + * @param id the email request ID to set + */ public void setId(Long id) { this.id = id; } + /** + * Gets the email address of the recipient. + * + * @return the recipient email address + */ public String getRecipient() { return recipient; } + + /** + * Sets the email address of the recipient. + * + * @param recipient the recipient email address to set + */ public void setRecipient(String recipient) { this.recipient = recipient; } + /** + * Gets the email subject line. + * + * @return the subject + */ public String getSubject() { return subject; } + + /** + * Sets the email subject line. + * + * @param subject the subject to set + */ public void setSubject(String subject) { this.subject = subject; } + /** + * Gets the email body content. + * + * @return the body, or null if not set + */ public String getBody() { return body; } + + /** + * Sets the email body content. + * + * @param body the body to set + */ public void setBody(String body) { this.body = body; } + /** + * Gets the template key used to generate this email. + * + * @return the template key, or null if not set + */ public String getTemplateKey() { return templateKey; } + + /** + * Sets the template key used to generate this email. + * + * @param templateKey the template key to set + */ public void setTemplateKey(String templateKey) { this.templateKey = templateKey; } + /** + * Gets the status of the email. + * + * @return the email status (PENDING, SENT, FAILED) + */ public EmailStatus getStatus() { return status; } + + /** + * Sets the status of the email. + * + * @param status the email status to set + */ public void setStatus(EmailStatus status) { this.status = status; } + /** + * Gets the timestamp when the email was sent. + * + * @return the sent timestamp, or null if not yet sent + */ public LocalDateTime getSentAt() { return sentAt; } + + /** + * Sets the timestamp when the email was sent. + * + * @param sentAt the sent timestamp to set + */ public void setSentAt(LocalDateTime sentAt) { this.sentAt = sentAt; } + /** + * Gets the error message if the email failed to send. + * + * @return the error message, or null if no error + */ public String getErrorMessage() { return errorMessage; } + + /** + * Sets the error message if the email failed to send. + * + * @param errorMessage the error message to set + */ public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + /** + * Gets the timestamp when the email was first opened. + * + * @return the opened timestamp, or null if never opened + */ public LocalDateTime getOpenedAt() { return openedAt; } + + /** + * Sets the timestamp when the email was first opened. + * + * @param openedAt the opened timestamp to set + */ public void setOpenedAt(LocalDateTime openedAt) { this.openedAt = openedAt; } + /** + * Gets the number of times the email has been opened. + * + * @return the open count + */ public Integer getOpenCount() { return openCount; } + + /** + * Sets the number of times the email has been opened. + * + * @param openCount the open count to set + */ public void setOpenCount(Integer openCount) { this.openCount = openCount; } + /** + * Gets the timestamp when a link in the email was first clicked. + * + * @return the clicked timestamp, or null if never clicked + */ public LocalDateTime getClickedAt() { return clickedAt; } + + /** + * Sets the timestamp when a link in the email was first clicked. + * + * @param clickedAt the clicked timestamp to set + */ public void setClickedAt(LocalDateTime clickedAt) { this.clickedAt = clickedAt; } + /** + * Gets the number of times links in the email have been clicked. + * + * @return the click count + */ public Integer getClickCount() { return clickCount; } + + /** + * Sets the number of times links in the email have been clicked. + * + * @param clickCount the click count to set + */ public void setClickCount(Integer clickCount) { this.clickCount = clickCount; } + /** + * Gets the timestamp when this email request was created. + * + * @return the creation timestamp + */ public LocalDateTime getCreatedAt() { return createdAt; } + + /** + * Sets the timestamp when this email request was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this email request was last updated. + * + * @return the last update timestamp + */ public LocalDateTime getUpdatedAt() { return updatedAt; } + + /** + * Sets the timestamp when this email request was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } } \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/model/Merchant.java b/src/main/java/group/goforward/battlbuilder/model/Merchant.java index f757e47..12984fc 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Merchant.java +++ b/src/main/java/group/goforward/battlbuilder/model/Merchant.java @@ -5,41 +5,58 @@ import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; +/** + * Entity representing a merchant/retailer that offers products. + * Merchants are integrated via AvantLink and have product feeds that are imported + * to create product offers. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "merchants") public class Merchant { + /** The primary key identifier for the merchant. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** The merchant name. */ @Column(name = "name", nullable = false, length = Integer.MAX_VALUE) private String name; + /** The AvantLink merchant ID. */ @Column(name = "avantlink_mid", nullable = false, length = Integer.MAX_VALUE) private String avantlinkMid; + /** The URL of the product feed for full imports. */ @Column(name = "feed_url", nullable = false, length = Integer.MAX_VALUE) private String feedUrl; + /** The URL of the offer feed for price/availability updates. */ @Column(name = "offer_feed_url") private String offerFeedUrl; + /** The timestamp when the last full product import was completed. */ @Column(name = "last_full_import_at") private OffsetDateTime lastFullImportAt; + /** The timestamp when the last offer sync was completed. */ @Column(name = "last_offer_sync_at") private OffsetDateTime lastOfferSyncAt; + /** Whether this merchant is active. Defaults to true. */ @ColumnDefault("true") @Column(name = "is_active", nullable = false) private Boolean isActive = true; + /** The timestamp when this merchant was created. */ @ColumnDefault("now()") @Column(name = "created_at", nullable = false) private OffsetDateTime createdAt; + /** The timestamp when this merchant was last updated. */ @ColumnDefault("now()") @Column(name = "updated_at", nullable = false) private OffsetDateTime updatedAt; @@ -48,82 +65,182 @@ public class Merchant { // GETTERS & SETTERS // ----------------------- + /** + * Gets the primary key identifier for the merchant. + * + * @return the merchant ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the merchant. + * + * @param id the merchant ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the merchant name. + * + * @return the merchant name + */ public String getName() { return name; } + /** + * Sets the merchant name. + * + * @param name the merchant name to set + */ public void setName(String name) { this.name = name; } + /** + * Gets the AvantLink merchant ID. + * + * @return the AvantLink merchant ID + */ public String getAvantlinkMid() { return avantlinkMid; } + /** + * Sets the AvantLink merchant ID. + * + * @param avantlinkMid the AvantLink merchant ID to set + */ public void setAvantlinkMid(String avantlinkMid) { this.avantlinkMid = avantlinkMid; } + /** + * Gets the URL of the product feed for full imports. + * + * @return the feed URL + */ public String getFeedUrl() { return feedUrl; } + /** + * Sets the URL of the product feed for full imports. + * + * @param feedUrl the feed URL to set + */ public void setFeedUrl(String feedUrl) { this.feedUrl = feedUrl; } + /** + * Gets the URL of the offer feed for price/availability updates. + * + * @return the offer feed URL, or null if not set + */ public String getOfferFeedUrl() { return offerFeedUrl; } + /** + * Sets the URL of the offer feed for price/availability updates. + * + * @param offerFeedUrl the offer feed URL to set + */ public void setOfferFeedUrl(String offerFeedUrl) { this.offerFeedUrl = offerFeedUrl; } + /** + * Gets the timestamp when the last full product import was completed. + * + * @return the last full import timestamp, or null if never imported + */ public OffsetDateTime getLastFullImportAt() { return lastFullImportAt; } + /** + * Sets the timestamp when the last full product import was completed. + * + * @param lastFullImportAt the last full import timestamp to set + */ public void setLastFullImportAt(OffsetDateTime lastFullImportAt) { this.lastFullImportAt = lastFullImportAt; } + /** + * Gets the timestamp when the last offer sync was completed. + * + * @return the last offer sync timestamp, or null if never synced + */ public OffsetDateTime getLastOfferSyncAt() { return lastOfferSyncAt; } + /** + * Sets the timestamp when the last offer sync was completed. + * + * @param lastOfferSyncAt the last offer sync timestamp to set + */ public void setLastOfferSyncAt(OffsetDateTime lastOfferSyncAt) { this.lastOfferSyncAt = lastOfferSyncAt; } + /** + * Gets whether this merchant is active. + * + * @return true if active, false otherwise + */ public Boolean getIsActive() { return isActive; } + /** + * Sets whether this merchant is active. + * + * @param active true to activate the merchant, false to deactivate + */ public void setIsActive(Boolean active) { this.isActive = active; } + /** + * Gets the timestamp when this merchant was created. + * + * @return the creation timestamp + */ public OffsetDateTime getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when this merchant was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this merchant was last updated. + * + * @return the last update timestamp + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when this merchant was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } diff --git a/src/main/java/group/goforward/battlbuilder/model/Platform.java b/src/main/java/group/goforward/battlbuilder/model/Platform.java index a2627d8..93633e0 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Platform.java +++ b/src/main/java/group/goforward/battlbuilder/model/Platform.java @@ -6,89 +6,172 @@ import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; +/** + * Entity representing a firearm platform (e.g., AR-15, AK-47). + * Platforms are used to categorize products and builds. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "platforms") public class Platform { + /** The primary key identifier for the platform. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** The unique key/identifier for the platform (e.g., "ar-15", "ak-47"). */ @NotNull @Column(name = "key", nullable = false, length = Integer.MAX_VALUE) private String key; + /** The display label for the platform (e.g., "AR-15", "AK-47"). */ @NotNull @Column(name = "label", nullable = false, length = Integer.MAX_VALUE) private String label; + /** The timestamp when this platform was created. */ @ColumnDefault("CURRENT_TIMESTAMP") @Column(name = "created_at") private OffsetDateTime createdAt; + /** The timestamp when this platform was last updated. */ @ColumnDefault("CURRENT_TIMESTAMP") @Column(name = "updated_at") private OffsetDateTime updatedAt; + /** The timestamp when this platform was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private OffsetDateTime deletedAt; + /** Whether this platform is active. Defaults to true. */ @ColumnDefault("true") @Column(name = "is_active", nullable = false) private Boolean isActive = true; + /** + * Gets the primary key identifier for the platform. + * + * @return the platform ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the platform. + * + * @param id the platform ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the unique key/identifier for the platform. + * + * @return the platform key (e.g., "ar-15", "ak-47") + */ public String getKey() { return key; } + /** + * Sets the unique key/identifier for the platform. + * + * @param key the platform key to set + */ public void setKey(String key) { this.key = key; } + /** + * Gets the display label for the platform. + * + * @return the platform label (e.g., "AR-15", "AK-47") + */ public String getLabel() { return label; } + /** + * Sets the display label for the platform. + * + * @param label the platform label to set + */ public void setLabel(String label) { this.label = label; } + /** + * Gets the timestamp when this platform was created. + * + * @return the creation timestamp, or null if not set + */ public OffsetDateTime getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when this platform was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this platform was last updated. + * + * @return the last update timestamp, or null if not set + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when this platform was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this platform was soft-deleted. + * + * @return the deletion timestamp, or null if the platform is not deleted + */ public OffsetDateTime getDeletedAt() { return deletedAt; } + /** + * Sets the timestamp when this platform was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } + /** + * Gets whether this platform is active. + * + * @return true if active, false otherwise + */ public Boolean getIsActive() { return isActive; } + /** + * Sets whether this platform is active. + * + * @param active true to activate the platform, false to deactivate + */ public void setIsActive(Boolean active) { this.isActive = active; } diff --git a/src/main/java/group/goforward/battlbuilder/model/Product.java b/src/main/java/group/goforward/battlbuilder/model/Product.java index 7b20308..73734e2 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Product.java +++ b/src/main/java/group/goforward/battlbuilder/model/Product.java @@ -29,125 +29,194 @@ import java.util.HashSet; " LEFT JOIN FETCH p.offers o" + " WHERE p.platform = :platform" + " AND p.deletedAt IS NULL") +/** + * Entity representing a product in the system. + * This class stores product information including brand, classification, pricing, + * images, and administrative settings. Products can be associated with multiple + * offers from different merchants. + * + * @see jakarta.persistence.Entity + */ public class Product { + /** The primary key identifier for the product. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** A unique identifier (UUID) for the product. */ @Column(name = "uuid", nullable = false, updatable = false) private UUID uuid; + /** The brand that manufactures this product. */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "brand_id", nullable = false) private Brand brand; + /** The product name. */ @Column(name = "name", nullable = false) private String name; + /** The URL-friendly slug for the product. */ @Column(name = "slug", nullable = false) private String slug; + /** The Manufacturer Part Number (MPN). */ @Column(name = "mpn") private String mpn; + /** The Universal Product Code (UPC). */ @Column(name = "upc") private String upc; + /** The platform this product is designed for (e.g., "AR-15", "AK-47"). */ @Column(name = "platform") private String platform; + /** The role/function of this part in a build (e.g., "upper_receiver", "barrel"). */ @Column(name = "part_role") private String partRole; // --- classification provenance --- + /** The source of the part role classification. */ @Enumerated(EnumType.STRING) @Column(name = "part_role_source", nullable = false) private PartRoleSource partRoleSource = PartRoleSource.UNKNOWN; + /** The version of the classifier used to classify this product. */ @Column(name = "classifier_version", length = 32) private String classifierVersion; + /** The reason for the classification decision. */ @Column(name = "classification_reason", length = 512) private String classificationReason; + /** The timestamp when this product was classified. */ @Column(name = "classified_at") private Instant classifiedAt; + /** Whether the part role is locked from automatic reclassification. */ @Column(name = "part_role_locked", nullable = false) private Boolean partRoleLocked = false; + /** The product configuration type. */ @Column(name = "configuration") @Enumerated(EnumType.STRING) private ProductConfiguration configuration; + /** A short description of the product. */ @Column(name = "short_description") private String shortDescription; + /** A detailed description of the product. */ @Column(name = "description") private String description; + /** The URL of the main product image. */ @Column(name = "main_image_url") private String mainImageUrl; + /** The URL of the BattlBuilder-hosted image. */ @Column(name = "battl_image_url") private String battlImageUrl; + /** The timestamp when this product was created. */ @Column(name = "created_at", nullable = false) private Instant createdAt; + /** The timestamp when this product was last updated. */ @Column(name = "updated_at") private Instant updatedAt; + /** The timestamp when this product was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private Instant deletedAt; + /** The raw category key from the import source. */ @Column(name = "raw_category_key") private String rawCategoryKey; + /** Whether the platform assignment is locked from automatic changes. */ @Column(name = "platform_locked", nullable = false) private Boolean platformLocked = false; + /** The import status of this product. */ @Enumerated(EnumType.STRING) @Column(name = "import_status", nullable = false) private ImportStatus importStatus = ImportStatus.MAPPED; + /** The set of offers available for this product from various merchants. */ @OneToMany(mappedBy = "product", fetch = FetchType.LAZY) private Set offers = new HashSet<>(); + /** The caliber this product is designed for. */ @Column(name = "caliber") private String caliber; + /** + * Gets the caliber this product is designed for. + * + * @return the caliber, or null if not set + */ public String getCaliber() { return caliber; } + + /** + * Sets the caliber this product is designed for. + * + * @param caliber the caliber to set + */ public void setCaliber(String caliber) { this.caliber = caliber; } + /** The caliber group this product belongs to. */ @Column(name = "caliber_group") private String caliberGroup; + /** + * Gets the caliber group this product belongs to. + * + * @return the caliber group, or null if not set + */ public String getCaliberGroup() { return caliberGroup; } + + /** + * Sets the caliber group this product belongs to. + * + * @param caliberGroup the caliber group to set + */ public void setCaliberGroup(String caliberGroup) { this.caliberGroup = caliberGroup; } /* Admin Management */ + + /** The visibility status of this product. */ @Enumerated(EnumType.STRING) @Column(name = "visibility", nullable = false) private ProductVisibility visibility = ProductVisibility.PUBLIC; + /** Whether this product is eligible to be used in the builder. */ @Column(name = "builder_eligible", nullable = false) private Boolean builderEligible = true; + /** Whether this product is locked by an administrator. */ @Column(name = "admin_locked", nullable = false) private Boolean adminLocked = false; + /** The status of this product. */ @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) private ProductStatus status = ProductStatus.ACTIVE; + /** Administrative notes about this product. */ @Column(name = "admin_note") private String adminNote; // --- lifecycle hooks --- + + /** + * Lifecycle hook called before persisting a new entity. + * Initializes the UUID and timestamps if not already set. + */ @PrePersist public void prePersist() { if (uuid == null) uuid = UUID.randomUUID(); @@ -156,122 +225,476 @@ public class Product { if (updatedAt == null) updatedAt = now; } + /** + * Lifecycle hook called before updating an existing entity. + * Updates the updatedAt timestamp. + */ @PreUpdate public void preUpdate() { updatedAt = Instant.now(); } // --- getters & setters --- + + /** + * Gets the primary key identifier for the product. + * + * @return the product ID + */ public Integer getId() { return id; } + + /** + * Sets the primary key identifier for the product. + * + * @param id the product ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the unique identifier (UUID) for the product. + * + * @return the product UUID + */ public UUID getUuid() { return uuid; } + + /** + * Sets the unique identifier (UUID) for the product. + * + * @param uuid the product UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the brand that manufactures this product. + * + * @return the brand + */ public Brand getBrand() { return brand; } + + /** + * Sets the brand that manufactures this product. + * + * @param brand the brand to set + */ public void setBrand(Brand brand) { this.brand = brand; } + /** + * Gets the product name. + * + * @return the product name + */ public String getName() { return name; } + + /** + * Sets the product name. + * + * @param name the product name to set + */ public void setName(String name) { this.name = name; } + /** + * Gets the URL-friendly slug for the product. + * + * @return the slug + */ public String getSlug() { return slug; } + + /** + * Sets the URL-friendly slug for the product. + * + * @param slug the slug to set + */ public void setSlug(String slug) { this.slug = slug; } + /** + * Gets the Manufacturer Part Number (MPN). + * + * @return the MPN, or null if not set + */ public String getMpn() { return mpn; } + + /** + * Sets the Manufacturer Part Number (MPN). + * + * @param mpn the MPN to set + */ public void setMpn(String mpn) { this.mpn = mpn; } + /** + * Gets the Universal Product Code (UPC). + * + * @return the UPC, or null if not set + */ public String getUpc() { return upc; } + + /** + * Sets the Universal Product Code (UPC). + * + * @param upc the UPC to set + */ public void setUpc(String upc) { this.upc = upc; } + /** + * Gets the platform this product is designed for. + * + * @return the platform (e.g., "AR-15", "AK-47"), or null if not set + */ public String getPlatform() { return platform; } + + /** + * Sets the platform this product is designed for. + * + * @param platform the platform to set + */ public void setPlatform(String platform) { this.platform = platform; } + /** + * Gets the role/function of this part in a build. + * + * @return the part role (e.g., "upper_receiver", "barrel"), or null if not set + */ public String getPartRole() { return partRole; } + + /** + * Sets the role/function of this part in a build. + * + * @param partRole the part role to set + */ public void setPartRole(String partRole) { this.partRole = partRole; } + /** + * Gets the product configuration type. + * + * @return the configuration, or null if not set + */ public ProductConfiguration getConfiguration() { return configuration; } + + /** + * Sets the product configuration type. + * + * @param configuration the configuration to set + */ public void setConfiguration(ProductConfiguration configuration) { this.configuration = configuration; } + /** + * Gets the short description of the product. + * + * @return the short description, or null if not set + */ public String getShortDescription() { return shortDescription; } + + /** + * Sets the short description of the product. + * + * @param shortDescription the short description to set + */ public void setShortDescription(String shortDescription) { this.shortDescription = shortDescription; } + /** + * Gets the detailed description of the product. + * + * @return the description, or null if not set + */ public String getDescription() { return description; } + + /** + * Sets the detailed description of the product. + * + * @param description the description to set + */ public void setDescription(String description) { this.description = description; } + /** + * Gets the URL of the main product image. + * + * @return the main image URL, or null if not set + */ public String getMainImageUrl() { return mainImageUrl; } + + /** + * Sets the URL of the main product image. + * + * @param mainImageUrl the main image URL to set + */ public void setMainImageUrl(String mainImageUrl) { this.mainImageUrl = mainImageUrl; } + /** + * Gets the URL of the BattlBuilder-hosted image. + * + * @return the Battl image URL, or null if not set + */ public String getBattlImageUrl() {return battlImageUrl; } + /** + * Sets the URL of the BattlBuilder-hosted image. + * + * @param battlImageUrl the Battl image URL to set + */ public void setBattlImageUrl(String battlImageUrl) {this.battlImageUrl = battlImageUrl; } + + /** + * Gets the timestamp when this product was created. + * + * @return the creation timestamp + */ public Instant getCreatedAt() { return createdAt; } + + /** + * Sets the timestamp when this product was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when this product was last updated. + * + * @return the last update timestamp, or null if not set + */ public Instant getUpdatedAt() { return updatedAt; } + + /** + * Sets the timestamp when this product was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when this product was soft-deleted. + * + * @return the deletion timestamp, or null if the product is not deleted + */ public Instant getDeletedAt() { return deletedAt; } + + /** + * Sets the timestamp when this product was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(Instant deletedAt) { this.deletedAt = deletedAt; } + /** + * Gets whether the platform assignment is locked. + * + * @return true if the platform is locked, false otherwise + */ public Boolean getPlatformLocked() { return platformLocked; } + + /** + * Sets whether the platform assignment is locked. + * + * @param platformLocked true to lock the platform, false to unlock + */ public void setPlatformLocked(Boolean platformLocked) { this.platformLocked = platformLocked; } + /** + * Gets the raw category key from the import source. + * + * @return the raw category key, or null if not set + */ public String getRawCategoryKey() { return rawCategoryKey; } + + /** + * Sets the raw category key from the import source. + * + * @param rawCategoryKey the raw category key to set + */ public void setRawCategoryKey(String rawCategoryKey) { this.rawCategoryKey = rawCategoryKey; } + /** + * Gets the import status of this product. + * + * @return the import status + */ public ImportStatus getImportStatus() { return importStatus; } + + /** + * Sets the import status of this product. + * + * @param importStatus the import status to set + */ public void setImportStatus(ImportStatus importStatus) { this.importStatus = importStatus; } + /** + * Gets the set of offers available for this product. + * + * @return the set of product offers + */ public Set getOffers() { return offers; } + + /** + * Sets the set of offers available for this product. + * + * @param offers the set of product offers to set + */ public void setOffers(Set offers) { this.offers = offers; } + /** + * Gets the source of the part role classification. + * + * @return the part role source + */ public PartRoleSource getPartRoleSource() { return partRoleSource; } + + /** + * Sets the source of the part role classification. + * + * @param partRoleSource the part role source to set + */ public void setPartRoleSource(PartRoleSource partRoleSource) { this.partRoleSource = partRoleSource; } + /** + * Gets the version of the classifier used to classify this product. + * + * @return the classifier version, or null if not set + */ public String getClassifierVersion() { return classifierVersion; } + + /** + * Sets the version of the classifier used to classify this product. + * + * @param classifierVersion the classifier version to set + */ public void setClassifierVersion(String classifierVersion) { this.classifierVersion = classifierVersion; } + /** + * Gets the reason for the classification decision. + * + * @return the classification reason, or null if not set + */ public String getClassificationReason() { return classificationReason; } + + /** + * Sets the reason for the classification decision. + * + * @param classificationReason the classification reason to set + */ public void setClassificationReason(String classificationReason) { this.classificationReason = classificationReason; } + /** + * Gets the timestamp when this product was classified. + * + * @return the classification timestamp, or null if not set + */ public Instant getClassifiedAt() { return classifiedAt; } + + /** + * Sets the timestamp when this product was classified. + * + * @param classifiedAt the classification timestamp to set + */ public void setClassifiedAt(Instant classifiedAt) { this.classifiedAt = classifiedAt; } + /** + * Gets whether the part role is locked from automatic reclassification. + * + * @return true if the part role is locked, false otherwise + */ public Boolean getPartRoleLocked() { return partRoleLocked; } + + /** + * Sets whether the part role is locked from automatic reclassification. + * + * @param partRoleLocked true to lock the part role, false to unlock + */ public void setPartRoleLocked(Boolean partRoleLocked) { this.partRoleLocked = partRoleLocked; } // --- Admin Getters/setters + + /** + * Gets the visibility status of this product. + * + * @return the visibility status + */ public ProductVisibility getVisibility() { return visibility; } + + /** + * Sets the visibility status of this product. + * + * @param visibility the visibility status to set + */ public void setVisibility(ProductVisibility visibility) { this.visibility = visibility; } + /** + * Gets whether this product is eligible to be used in the builder. + * + * @return true if builder eligible, false otherwise + */ public Boolean getBuilderEligible() { return builderEligible; } + + /** + * Sets whether this product is eligible to be used in the builder. + * + * @param builderEligible true to make builder eligible, false otherwise + */ public void setBuilderEligible(Boolean builderEligible) { this.builderEligible = builderEligible; } + /** + * Gets whether this product is locked by an administrator. + * + * @return true if admin locked, false otherwise + */ public Boolean getAdminLocked() { return adminLocked; } + + /** + * Sets whether this product is locked by an administrator. + * + * @param adminLocked true to lock the product, false to unlock + */ public void setAdminLocked(Boolean adminLocked) { this.adminLocked = adminLocked; } + /** + * Gets the status of this product. + * + * @return the product status + */ public ProductStatus getStatus() { return status; } + + /** + * Sets the status of this product. + * + * @param status the product status to set + */ public void setStatus(ProductStatus status) { this.status = status; } + /** + * Gets the administrative notes about this product. + * + * @return the admin note, or null if not set + */ public String getAdminNote() { return adminNote; } + + /** + * Sets the administrative notes about this product. + * + * @param adminNote the admin note to set + */ public void setAdminNote(String adminNote) { this.adminNote = adminNote; } + // --- computed helpers --- + /** + * Gets the best (lowest) offer price for this product. + * Compares sale prices first, then retail prices if sale price is not available. + * + * @return the best offer price, or BigDecimal.ZERO if no offers are available + */ public BigDecimal getBestOfferPrice() { if (offers == null || offers.isEmpty()) return BigDecimal.ZERO; @@ -284,6 +707,12 @@ public class Product { .orElse(BigDecimal.ZERO); } + /** + * Gets the affiliate URL for the best (lowest priced) offer. + * Compares sale prices first, then retail prices if sale price is not available. + * + * @return the affiliate URL for the best offer, or null if no offers are available + */ public String getBestOfferBuyUrl() { if (offers == null || offers.isEmpty()) return null; diff --git a/src/main/java/group/goforward/battlbuilder/model/ProductOffer.java b/src/main/java/group/goforward/battlbuilder/model/ProductOffer.java index 7219cce..10d145b 100644 --- a/src/main/java/group/goforward/battlbuilder/model/ProductOffer.java +++ b/src/main/java/group/goforward/battlbuilder/model/ProductOffer.java @@ -8,54 +8,74 @@ import org.hibernate.annotations.OnDeleteAction; import java.math.BigDecimal; import java.time.OffsetDateTime; +/** + * Entity representing a product offer from a merchant. + * An offer contains pricing information, availability, and purchase URL for a product + * from a specific merchant. Products can have multiple offers from different merchants. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "product_offers") public class ProductOffer { + /** The primary key identifier for the offer. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** The product this offer is for. */ @ManyToOne(fetch = FetchType.LAZY, optional = false) @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "product_id", nullable = false) private Product product; + /** The merchant offering this product. */ @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "merchant_id", nullable = false) private Merchant merchant; + /** The AvantLink product ID for this offer. */ @Column(name = "avantlink_product_id", nullable = false) private String avantlinkProductId; + /** The SKU (Stock Keeping Unit) for this offer. */ @Column(name = "sku") private String sku; + /** The UPC (Universal Product Code) for this offer. */ @Column(name = "upc") private String upc; + /** The URL to purchase this product from the merchant. */ @Column(name = "buy_url", nullable = false) private String buyUrl; + /** The current price of the product. */ @Column(name = "price", nullable = false, precision = 10, scale = 2) private BigDecimal price; + /** The original/retail price before any discounts. */ @Column(name = "original_price", precision = 10, scale = 2) private BigDecimal originalPrice; + /** The currency code (e.g., "USD"). Defaults to "USD". */ @ColumnDefault("'USD'") @Column(name = "currency", nullable = false) private String currency; + /** Whether the product is currently in stock. Defaults to false. */ @ColumnDefault("true") @Column(name = "in_stock", nullable = false) private Boolean inStock = false; + /** The timestamp when this offer was last seen/updated. */ @ColumnDefault("now()") @Column(name = "last_seen_at", nullable = false) private OffsetDateTime lastSeenAt; + /** The timestamp when this offer was first seen/created. */ @ColumnDefault("now()") @Column(name = "first_seen_at", nullable = false) private OffsetDateTime firstSeenAt; @@ -64,106 +84,236 @@ public class ProductOffer { // Getters & setters // ----------------------------------------------------- + /** + * Gets the primary key identifier for the offer. + * + * @return the offer ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the offer. + * + * @param id the offer ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the product this offer is for. + * + * @return the product + */ public Product getProduct() { return product; } + /** + * Sets the product this offer is for. + * + * @param product the product to set + */ public void setProduct(Product product) { this.product = product; } + /** + * Gets the merchant offering this product. + * + * @return the merchant + */ public Merchant getMerchant() { return merchant; } + /** + * Sets the merchant offering this product. + * + * @param merchant the merchant to set + */ public void setMerchant(Merchant merchant) { this.merchant = merchant; } + /** + * Gets the AvantLink product ID for this offer. + * + * @return the AvantLink product ID + */ public String getAvantlinkProductId() { return avantlinkProductId; } + /** + * Sets the AvantLink product ID for this offer. + * + * @param avantlinkProductId the AvantLink product ID to set + */ public void setAvantlinkProductId(String avantlinkProductId) { this.avantlinkProductId = avantlinkProductId; } + /** + * Gets the SKU (Stock Keeping Unit) for this offer. + * + * @return the SKU, or null if not set + */ public String getSku() { return sku; } + /** + * Sets the SKU (Stock Keeping Unit) for this offer. + * + * @param sku the SKU to set + */ public void setSku(String sku) { this.sku = sku; } + /** + * Gets the UPC (Universal Product Code) for this offer. + * + * @return the UPC, or null if not set + */ public String getUpc() { return upc; } + /** + * Sets the UPC (Universal Product Code) for this offer. + * + * @param upc the UPC to set + */ public void setUpc(String upc) { this.upc = upc; } + /** + * Gets the URL to purchase this product from the merchant. + * + * @return the buy URL + */ public String getBuyUrl() { return buyUrl; } + /** + * Sets the URL to purchase this product from the merchant. + * + * @param buyUrl the buy URL to set + */ public void setBuyUrl(String buyUrl) { this.buyUrl = buyUrl; } + /** + * Gets the current price of the product. + * + * @return the price + */ public BigDecimal getPrice() { return price; } + /** + * Sets the current price of the product. + * + * @param price the price to set + */ public void setPrice(BigDecimal price) { this.price = price; } + /** + * Gets the original/retail price before any discounts. + * + * @return the original price, or null if not set + */ public BigDecimal getOriginalPrice() { return originalPrice; } + /** + * Sets the original/retail price before any discounts. + * + * @param originalPrice the original price to set + */ public void setOriginalPrice(BigDecimal originalPrice) { this.originalPrice = originalPrice; } + /** + * Gets the currency code. + * + * @return the currency code (e.g., "USD") + */ public String getCurrency() { return currency; } + /** + * Sets the currency code. + * + * @param currency the currency code to set + */ public void setCurrency(String currency) { this.currency = currency; } + /** + * Gets whether the product is currently in stock. + * + * @return true if in stock, false otherwise + */ public Boolean getInStock() { return inStock; } + /** + * Sets whether the product is currently in stock. + * + * @param inStock true if in stock, false otherwise + */ public void setInStock(Boolean inStock) { this.inStock = inStock; } + /** + * Gets the timestamp when this offer was last seen/updated. + * + * @return the last seen timestamp + */ public OffsetDateTime getLastSeenAt() { return lastSeenAt; } + /** + * Sets the timestamp when this offer was last seen/updated. + * + * @param lastSeenAt the last seen timestamp to set + */ public void setLastSeenAt(OffsetDateTime lastSeenAt) { this.lastSeenAt = lastSeenAt; } + /** + * Gets the timestamp when this offer was first seen/created. + * + * @return the first seen timestamp + */ public OffsetDateTime getFirstSeenAt() { return firstSeenAt; } + /** + * Sets the timestamp when this offer was first seen/created. + * + * @param firstSeenAt the first seen timestamp to set + */ public void setFirstSeenAt(OffsetDateTime firstSeenAt) { this.firstSeenAt = firstSeenAt; } @@ -172,18 +322,38 @@ public class ProductOffer { // Helper Methods (used by Product entity) // ----------------------------------------------------- + /** + * Gets the sale price (current price). + * + * @return the sale price + */ public BigDecimal getSalePrice() { return price; } + /** + * Gets the retail price (original price if available, otherwise current price). + * + * @return the retail price + */ public BigDecimal getRetailPrice() { return originalPrice != null ? originalPrice : price; } + /** + * Gets the affiliate URL for purchasing this product. + * + * @return the affiliate URL (same as buy URL) + */ public String getAffiliateUrl() { return buyUrl; } + /** + * Gets the effective price (sale price if lower than original, otherwise current price). + * + * @return the effective price + */ public BigDecimal getEffectivePrice() { if (price != null && originalPrice != null && price.compareTo(originalPrice) < 0) { return price; diff --git a/src/main/java/group/goforward/battlbuilder/model/User.java b/src/main/java/group/goforward/battlbuilder/model/User.java index f504e7f..3a32019 100644 --- a/src/main/java/group/goforward/battlbuilder/model/User.java +++ b/src/main/java/group/goforward/battlbuilder/model/User.java @@ -7,90 +7,116 @@ import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; import java.util.UUID; +/** + * Entity representing a user in the system. + * This class stores user authentication, profile, and account management information + * including email verification, password reset tokens, login tracking, and Terms of Service acceptance. + * + * @see jakarta.persistence.Entity + */ @Entity @Table(name = "users") public class User { + /** The primary key identifier for the user. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; + /** A unique identifier (UUID) for the user. */ @NotNull @ColumnDefault("gen_random_uuid()") @Column(name = "uuid", nullable = false) private UUID uuid; + /** The user's email address. */ @NotNull @Column(name = "email", nullable = false, length = Integer.MAX_VALUE) private String email; - // password can be null for magic-link / beta users + /** The hashed password. Can be null for magic-link or beta users. */ @Column(name = "password_hash", length = Integer.MAX_VALUE, nullable = true) private String passwordHash; + /** The user's display name. */ @Column(name = "display_name", length = Integer.MAX_VALUE) private String displayName; + /** The user's role (e.g., "USER", "ADMIN"). Defaults to "USER". */ @NotNull @ColumnDefault("'USER'") @Column(name = "role", nullable = false, length = Integer.MAX_VALUE) private String role; + /** Whether the user account is active. Defaults to true. */ @NotNull @ColumnDefault("true") @Column(name = "is_active", nullable = false) private boolean isActive = true; + /** The timestamp when the user account was created. */ @NotNull @ColumnDefault("now()") @Column(name = "created_at", nullable = false) private OffsetDateTime createdAt; + /** The timestamp when the user account was last updated. */ @NotNull @ColumnDefault("now()") @Column(name = "updated_at", nullable = false) private OffsetDateTime updatedAt; + /** The timestamp when the user account was soft-deleted (null if not deleted). */ @Column(name = "deleted_at") private OffsetDateTime deletedAt; - // NEW FIELDS - + /** The timestamp when the user's email was verified. */ @Column(name = "email_verified_at") private OffsetDateTime emailVerifiedAt; + /** The token used for email verification. */ @Column(name = "verification_token", length = Integer.MAX_VALUE) private String verificationToken; + /** The token used for password reset. */ @Column(name = "reset_password_token", length = Integer.MAX_VALUE) private String resetPasswordToken; + /** The expiration timestamp for the password reset token. */ @Column(name = "reset_password_expires_at") private OffsetDateTime resetPasswordExpiresAt; + /** The timestamp of the user's last login. */ @Column(name = "last_login_at") private OffsetDateTime lastLoginAt; + /** The total number of times the user has logged in. Defaults to 0. */ @ColumnDefault("0") @Column(name = "login_count", nullable = false) private Integer loginCount = 0; + /** The timestamp when the user accepted the Terms of Service. */ @Column(name = "tos_accepted_at") private OffsetDateTime tosAcceptedAt; + /** The version of the Terms of Service that was accepted. */ @Column(name = "tos_version", length = 32) private String tosVersion; + /** The IP address from which the Terms of Service were accepted. */ @Column(name = "tos_ip", length = 64) private String tosIp; + /** The user agent string from when the Terms of Service were accepted. */ @Column(name = "tos_user_agent", columnDefinition = "TEXT") private String tosUserAgent; + /** The user's username. */ @Column(name = "username", length = 32) private String username; + /** The timestamp when the user's password was last set. */ @Column(name = "password_set_at") private OffsetDateTime passwordSetAt; @@ -98,179 +124,410 @@ public class User { // --- Getters / setters --- + /** + * Gets the primary key identifier for the user. + * + * @return the user ID + */ public Integer getId() { return id; } + /** + * Sets the primary key identifier for the user. + * + * @param id the user ID to set + */ public void setId(Integer id) { this.id = id; } + /** + * Gets the unique identifier (UUID) for the user. + * + * @return the user UUID + */ public UUID getUuid() { return uuid; } + /** + * Sets the unique identifier (UUID) for the user. + * + * @param uuid the user UUID to set + */ public void setUuid(UUID uuid) { this.uuid = uuid; } + /** + * Gets the user's email address. + * + * @return the email address + */ public String getEmail() { return email; } + /** + * Sets the user's email address. + * + * @param email the email address to set + */ public void setEmail(String email) { this.email = email; } + /** + * Gets the hashed password. + * + * @return the password hash, or null if not set (e.g., for magic-link users) + */ public String getPasswordHash() { return passwordHash; } + /** + * Sets the hashed password. + * + * @param passwordHash the password hash to set + */ public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; } + /** + * Gets the user's display name. + * + * @return the display name, or null if not set + */ public String getDisplayName() { return displayName; } + /** + * Sets the user's display name. + * + * @param displayName the display name to set + */ public void setDisplayName(String displayName) { this.displayName = displayName; } + /** + * Gets the user's role. + * + * @return the role (e.g., "USER", "ADMIN") + */ public String getRole() { return role; } + /** + * Sets the user's role. + * + * @param role the role to set + */ public void setRole(String role) { this.role = role; } + /** + * Checks if the user account is active. + * + * @return true if the account is active, false otherwise + */ public boolean isActive() { return isActive; } + /** + * Sets whether the user account is active. + * + * @param active true to activate the account, false to deactivate + */ public void setActive(boolean active) { this.isActive = active; } + /** + * Gets the timestamp when the user account was created. + * + * @return the creation timestamp + */ public OffsetDateTime getCreatedAt() { return createdAt; } + /** + * Sets the timestamp when the user account was created. + * + * @param createdAt the creation timestamp to set + */ public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } + /** + * Gets the timestamp when the user account was last updated. + * + * @return the last update timestamp + */ public OffsetDateTime getUpdatedAt() { return updatedAt; } + /** + * Sets the timestamp when the user account was last updated. + * + * @param updatedAt the last update timestamp to set + */ public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } + /** + * Gets the timestamp when the user account was soft-deleted. + * + * @return the deletion timestamp, or null if the account is not deleted + */ public OffsetDateTime getDeletedAt() { return deletedAt; } + /** + * Sets the timestamp when the user account was soft-deleted. + * + * @param deletedAt the deletion timestamp to set (null to mark as not deleted) + */ public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } + /** + * Gets the timestamp when the user's email was verified. + * + * @return the email verification timestamp, or null if not verified + */ public OffsetDateTime getEmailVerifiedAt() { return emailVerifiedAt; } + /** + * Sets the timestamp when the user's email was verified. + * + * @param emailVerifiedAt the email verification timestamp to set + */ public void setEmailVerifiedAt(OffsetDateTime emailVerifiedAt) { this.emailVerifiedAt = emailVerifiedAt; } + /** + * Gets the token used for email verification. + * + * @return the verification token, or null if not set + */ public String getVerificationToken() { return verificationToken; } + /** + * Sets the token used for email verification. + * + * @param verificationToken the verification token to set + */ public void setVerificationToken(String verificationToken) { this.verificationToken = verificationToken; } + /** + * Gets the token used for password reset. + * + * @return the password reset token, or null if not set + */ public String getResetPasswordToken() { return resetPasswordToken; } + /** + * Sets the token used for password reset. + * + * @param resetPasswordToken the password reset token to set + */ public void setResetPasswordToken(String resetPasswordToken) { this.resetPasswordToken = resetPasswordToken; } + /** + * Gets the expiration timestamp for the password reset token. + * + * @return the expiration timestamp, or null if not set + */ public OffsetDateTime getResetPasswordExpiresAt() { return resetPasswordExpiresAt; } + /** + * Sets the expiration timestamp for the password reset token. + * + * @param resetPasswordExpiresAt the expiration timestamp to set + */ public void setResetPasswordExpiresAt(OffsetDateTime resetPasswordExpiresAt) { this.resetPasswordExpiresAt = resetPasswordExpiresAt; } + /** + * Gets the timestamp of the user's last login. + * + * @return the last login timestamp, or null if the user has never logged in + */ public OffsetDateTime getLastLoginAt() { return lastLoginAt; } + /** + * Sets the timestamp of the user's last login. + * + * @param lastLoginAt the last login timestamp to set + */ public void setLastLoginAt(OffsetDateTime lastLoginAt) { this.lastLoginAt = lastLoginAt; } + /** + * Gets the total number of times the user has logged in. + * + * @return the login count + */ public Integer getLoginCount() { return loginCount; } + /** + * Sets the total number of times the user has logged in. + * + * @param loginCount the login count to set + */ public void setLoginCount(Integer loginCount) { this.loginCount = loginCount; } + /** + * Gets the user's username. + * + * @return the username, or null if not set + */ public String getUsername() { return username; } + + /** + * Sets the user's username. + * + * @param username the username to set + */ public void setUsername(String username) { this.username = username; } // --- ToS acceptance --- + /** + * Gets the timestamp when the user accepted the Terms of Service. + * + * @return the ToS acceptance timestamp, or null if not accepted + */ public OffsetDateTime getTosAcceptedAt() { return tosAcceptedAt; } + /** + * Sets the timestamp when the user accepted the Terms of Service. + * + * @param tosAcceptedAt the ToS acceptance timestamp to set + */ public void setTosAcceptedAt(OffsetDateTime tosAcceptedAt) { this.tosAcceptedAt = tosAcceptedAt; } + /** + * Gets the version of the Terms of Service that was accepted. + * + * @return the ToS version, or null if not set + */ public String getTosVersion() { return tosVersion; } + /** + * Sets the version of the Terms of Service that was accepted. + * + * @param tosVersion the ToS version to set + */ public void setTosVersion(String tosVersion) { this.tosVersion = tosVersion; } + /** + * Gets the IP address from which the Terms of Service were accepted. + * + * @return the IP address, or null if not set + */ public String getTosIp() { return tosIp; } + /** + * Sets the IP address from which the Terms of Service were accepted. + * + * @param tosIp the IP address to set + */ public void setTosIp(String tosIp) { this.tosIp = tosIp; } + /** + * Gets the user agent string from when the Terms of Service were accepted. + * + * @return the user agent string, or null if not set + */ public String getTosUserAgent() { return tosUserAgent; } + /** + * Sets the user agent string from when the Terms of Service were accepted. + * + * @param tosUserAgent the user agent string to set + */ public void setTosUserAgent(String tosUserAgent) { this.tosUserAgent = tosUserAgent; } + /** + * Gets the timestamp when the user's password was last set. + * + * @return the password set timestamp, or null if not set + */ public OffsetDateTime getPasswordSetAt() { return passwordSetAt; } + + /** + * Sets the timestamp when the user's password was last set. + * + * @param passwordSetAt the password set timestamp to set + */ public void setPasswordSetAt(OffsetDateTime passwordSetAt) { this.passwordSetAt = passwordSetAt; } // convenience helpers + /** + * Checks if the user's email has been verified. + * + * @return true if the email is verified (emailVerifiedAt is not null), false otherwise + */ @Transient public boolean isEmailVerified() { return emailVerifiedAt != null; } + /** + * Increments the login count by one. + * If the login count is null, it is initialized to 0 before incrementing. + */ public void incrementLoginCount() { if (loginCount == null) { loginCount = 0;