making it AP complaint
Some checks failed
CI / Check / Test (push) Has been cancelled

This commit is contained in:
2026-06-30 02:25:42 +02:00
parent 943a0abe54
commit 5ae38c834a
66 changed files with 3635 additions and 2369 deletions

View File

@@ -112,6 +112,13 @@ pub enum DomainEvent {
user_id: UserId,
year: u16,
},
UserDeleted {
user_id: UserId,
},
UserAccountMoved {
user_id: UserId,
new_actor_url: String,
},
}
#[async_trait]

View File

@@ -36,6 +36,8 @@ pub trait MovieRepository: Send + Sync {
page: &PageParams,
filter: &MovieFilter,
) -> Result<Paginated<MovieSummary>, DomainError>;
/// Returns all movies that have an external_metadata_id set. Used for deduplication.
async fn list_movies_with_external_id(&self) -> Result<Vec<Movie>, DomainError>;
}
#[async_trait]
@@ -68,3 +70,15 @@ pub trait MovieEnrichmentClient: Send + Sync {
external_metadata_id: &str,
) -> Result<MovieProfile, DomainError>;
}
#[async_trait]
pub trait MovieDeduplicator: Send + Sync {
/// Atomically re-points all foreign keys (reviews, watchlist entries, movie profiles)
/// from `old_id` to `canonical_id`, upserts the canonical movie record, then deletes
/// the old duplicate. Returns the number of rows re-pointed across all tables.
async fn merge_into_canonical(
&self,
old_id: &MovieId,
canonical: &Movie,
) -> Result<u64, DomainError>;
}

View File

@@ -74,6 +74,10 @@ pub trait LocalApContentQuery: Send + Sync {
) -> Result<Vec<WatchlistWithMovie>, DomainError>;
async fn get_review_by_id(&self, review_id: &ReviewId) -> Result<Option<Review>, DomainError>;
async fn get_movie_by_id(&self, movie_id: &MovieId) -> Result<Option<Movie>, DomainError>;
async fn get_movie_by_external_metadata_id(
&self,
external_id: &str,
) -> Result<Option<Movie>, DomainError>;
async fn count_local_posts(&self) -> Result<u64, DomainError>;
async fn get_local_reviews_for_movie(
&self,
@@ -90,4 +94,5 @@ pub trait LocalApContentQuery: Send + Sync {
user_id: &UserId,
year: u16,
) -> Result<Option<(Goal, u32)>, DomainError>;
async fn list_goals_for_user(&self, user_id: &UserId) -> Result<Vec<Goal>, DomainError>;
}

View File

@@ -138,6 +138,17 @@ impl MovieRepository for InMemoryMovieRepository {
offset: 0,
})
}
async fn list_movies_with_external_id(&self) -> Result<Vec<Movie>, DomainError> {
Ok(self
.store
.lock()
.unwrap()
.values()
.filter(|m| m.external_metadata_id().is_some())
.cloned()
.collect())
}
}
// ── InMemoryReviewRepository ──────────────────────────────────────────────────

View File

@@ -20,6 +20,19 @@ macro_rules! uuid_id {
}
uuid_id!(MovieId);
impl MovieId {
/// Derives a stable, deterministic UUID from an external metadata ID (e.g. `tmdb:12345`).
/// All instances that know a movie by the same external ID will produce the same MovieId,
/// enabling remote and local reviews to be linked to the same movie record.
pub fn from_external(external_id: &crate::value_objects::ExternalMetadataId) -> Self {
Self(Uuid::new_v5(
&Uuid::NAMESPACE_URL,
external_id.value().as_bytes(),
))
}
}
uuid_id!(ReviewId);
uuid_id!(UserId);
uuid_id!(ImportSessionId);