refactor: fix HIGH+MEDIUM architectural violations from code review
HIGH: fix watch_medium data-loss bug, standardize error handling on ApiError, fix dep direction (rss/template-askama no longer dep on application), extract ImageFetcher port (remove reqwest from app layer), move event construction from save_review to use case, extract infra-wiring crate (DbPool/EventBusBackend dedup), deduplicate presentation helpers (encode_error, export streaming, multipart parsing) MEDIUM: split LocalApContentQuery god-trait 10→3 methods, dedup movie resolution orchestration, add RemoteActorDto/PersonDto mappers, move AppConfig to infra-wiring, fix SocialQueryPort Uuid→UserId, replace stringly-typed api-types with domain enums, move count_reviews_in_year to StatsRepository, dedup event publisher cfg blocks, extract should_enrich, move group_by_month to application, dedup count_local_posts, add FederationFlags Default, TUI input helper + ShowError rename + typed auth errors, api-types cleanup (UserSettingsDto/UserProfileBase/PreviewRowData) 102 files changed, -681 lines net
This commit is contained in:
@@ -6,7 +6,7 @@ use domain::{
|
||||
events::DomainEvent,
|
||||
models::MovieProfile,
|
||||
ports::{
|
||||
EventHandler, MovieEnrichmentClient, MovieProfileRepository, MovieRepository,
|
||||
EventHandler, ImageFetcher, MovieEnrichmentClient, MovieProfileRepository, MovieRepository,
|
||||
ObjectStorage, PersonCommand, SearchCommand,
|
||||
},
|
||||
};
|
||||
@@ -22,7 +22,7 @@ pub struct MovieEnrichmentHandler {
|
||||
person_command: Arc<dyn PersonCommand>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
object_storage: Arc<dyn ObjectStorage>,
|
||||
http: reqwest::Client,
|
||||
image_fetcher: Arc<dyn ImageFetcher>,
|
||||
}
|
||||
|
||||
impl MovieEnrichmentHandler {
|
||||
@@ -33,6 +33,7 @@ impl MovieEnrichmentHandler {
|
||||
person_command: Arc<dyn PersonCommand>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
object_storage: Arc<dyn ObjectStorage>,
|
||||
image_fetcher: Arc<dyn ImageFetcher>,
|
||||
) -> Self {
|
||||
Self {
|
||||
enrichment_client,
|
||||
@@ -41,7 +42,7 @@ impl MovieEnrichmentHandler {
|
||||
person_command,
|
||||
search_command,
|
||||
object_storage,
|
||||
http: reqwest::Client::new(),
|
||||
image_fetcher,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +56,13 @@ impl MovieEnrichmentHandler {
|
||||
continue;
|
||||
}
|
||||
let url = format!("https://image.tmdb.org/t/p/w185{path}");
|
||||
match self.http.get(&url).send().await {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
if let Ok(bytes) = resp.bytes().await
|
||||
&& let Err(e) = self.object_storage.store(&key, &bytes).await
|
||||
{
|
||||
match self.image_fetcher.fetch_image(&url).await {
|
||||
Ok(bytes) => {
|
||||
if let Err(e) = self.object_storage.store(&key, &bytes).await {
|
||||
tracing::debug!("cast photo store failed for {path}: {e}");
|
||||
}
|
||||
}
|
||||
_ => tracing::debug!("cast photo download failed for {path}"),
|
||||
Err(_) => tracing::debug!("cast photo download failed for {path}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ pub mod merge_duplicates;
|
||||
pub mod queries;
|
||||
pub mod reindex_search;
|
||||
pub mod request_enrichment;
|
||||
pub mod resolve;
|
||||
pub mod search_cleanup;
|
||||
pub mod sync_poster;
|
||||
|
||||
|
||||
51
crates/application/src/movies/resolve.rs
Normal file
51
crates/application/src/movies/resolve.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::Movie,
|
||||
ports::{EventPublisher, MetadataClient, MovieRepository},
|
||||
value_objects::MovieId,
|
||||
};
|
||||
|
||||
use crate::diary::commands::MovieInput;
|
||||
use crate::diary::movie_resolver::{MovieResolver, MovieResolverDeps};
|
||||
|
||||
/// Resolves a movie from input, persists it, and publishes `MovieDiscovered` if new.
|
||||
///
|
||||
/// Returns `(movie, is_new_movie)`.
|
||||
pub async fn resolve_and_persist_movie(
|
||||
input: &MovieInput,
|
||||
movie_repo: &dyn MovieRepository,
|
||||
metadata_client: &dyn MetadataClient,
|
||||
event_publisher: &dyn EventPublisher,
|
||||
) -> Result<(Movie, bool), DomainError> {
|
||||
let (movie, is_new) = if let Some(id) = input.movie_id {
|
||||
let movie_id = MovieId::from_uuid(id);
|
||||
let movie = movie_repo
|
||||
.get_movie_by_id(&movie_id)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Movie {id}")))?;
|
||||
(movie, false)
|
||||
} else {
|
||||
let deps = MovieResolverDeps {
|
||||
repository: movie_repo,
|
||||
metadata_client,
|
||||
};
|
||||
MovieResolver::default_pipeline()
|
||||
.resolve(input, &deps)
|
||||
.await?
|
||||
};
|
||||
|
||||
if is_new {
|
||||
movie_repo.upsert_movie(&movie).await?;
|
||||
if let Some(ext_id) = movie.external_metadata_id() {
|
||||
let _ = event_publisher
|
||||
.publish(&DomainEvent::MovieDiscovered {
|
||||
movie_id: movie.id().clone(),
|
||||
external_metadata_id: ext_id.clone(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((movie, is_new))
|
||||
}
|
||||
Reference in New Issue
Block a user