mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2026-01-20 16:51:03 -05:00
adding comments
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<ProductOffer> 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<ProductOffer> 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<ProductOffer> 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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user