use super::*; use crate::mapper::*; impl SpacetimeClient { pub async fn list_public_work_gallery_entries( &self, ) -> Result, SpacetimeClientError> { self.read_after_connect("list_public_work_gallery_entries", move |connection| { let recent_play_counts = public_work_recent_play_counts_by_source(connection); let mut entries = connection .db() .public_work_gallery_entry() .iter() .collect::>(); sort_public_work_gallery_entries(&mut entries); Ok(entries .into_iter() .map(|entry| { let recent_play_count_7d = recent_play_counts .get(&(entry.source_type.clone(), entry.profile_id.clone())) .copied() .unwrap_or(0); map_public_work_gallery_entry(entry, recent_play_count_7d) }) .collect()) }) .await } pub async fn list_public_work_detail_entries( &self, ) -> Result, SpacetimeClientError> { self.read_after_connect("list_public_work_detail_entries", move |connection| { let recent_play_counts = public_work_recent_play_counts_by_source(connection); let mut entries = connection .db() .public_work_detail_entry() .iter() .collect::>(); sort_public_work_detail_entries(&mut entries); Ok(entries .into_iter() .map(|entry| { let recent_play_count_7d = recent_play_counts .get(&(entry.source_type.clone(), entry.profile_id.clone())) .copied() .unwrap_or(0); map_public_work_gallery_entry_to_detail_entry(entry, recent_play_count_7d) }) .collect()) }) .await } pub async fn get_public_work_detail_by_code( &self, public_work_code: String, ) -> Result { let public_work_code = public_work_code.trim().to_string(); if public_work_code.is_empty() { return Err(SpacetimeClientError::validation_failed( "publicWorkCode 不能为空", )); } self.read_after_connect("get_public_work_detail_by_code", move |connection| { let recent_play_counts = public_work_recent_play_counts_by_source(connection); let entry = connection .db() .public_work_detail_entry() .iter() .find(|entry| { entry .public_work_code .eq_ignore_ascii_case(&public_work_code) }) .ok_or_else(|| SpacetimeClientError::Procedure("公开作品不存在".to_string()))?; let recent_play_count_7d = recent_play_counts .get(&(entry.source_type.clone(), entry.profile_id.clone())) .copied() .unwrap_or(0); Ok(map_public_work_gallery_entry_to_detail_entry( entry, recent_play_count_7d, )) }) .await } pub async fn get_public_work_detail_by_source_profile( &self, source_type: String, profile_id: String, ) -> Result { let source_type = source_type.trim().to_string(); let profile_id = profile_id.trim().to_string(); if source_type.is_empty() || profile_id.is_empty() { return Err(SpacetimeClientError::validation_failed( "sourceType 和 profileId 不能为空", )); } self.read_after_connect( "get_public_work_detail_by_source_profile", move |connection| { let recent_play_counts = public_work_recent_play_counts_by_source(connection); let entry = connection .db() .public_work_detail_entry() .iter() .find(|entry| { entry.source_type == source_type && entry.profile_id == profile_id }) .ok_or_else(|| SpacetimeClientError::Procedure("公开作品不存在".to_string()))?; let recent_play_count_7d = recent_play_counts .get(&(entry.source_type.clone(), entry.profile_id.clone())) .copied() .unwrap_or(0); Ok(map_public_work_gallery_entry_to_detail_entry( entry, recent_play_count_7d, )) }, ) .await } } fn sort_public_work_gallery_entries(entries: &mut [PublicWorkGalleryEntry]) { entries.sort_by(|left, right| { right .sort_time_micros .cmp(&left.sort_time_micros) .then_with(|| left.source_type.cmp(&right.source_type)) .then_with(|| left.profile_id.cmp(&right.profile_id)) }); } fn sort_public_work_detail_entries(entries: &mut [PublicWorkDetailEntry]) { entries.sort_by(|left, right| { right .sort_time_micros .cmp(&left.sort_time_micros) .then_with(|| left.source_type.cmp(&right.source_type)) .then_with(|| left.profile_id.cmp(&right.profile_id)) }); } fn public_work_recent_play_counts_by_source( connection: &DbConnection, ) -> HashMap<(String, String), u32> { let current_day = current_public_work_day(); let first_day = current_day - (PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS - 1); let mut counts = HashMap::new(); for row in connection.db().public_work_play_daily_stat().iter() { if row.played_day < first_day || row.played_day > current_day { continue; } let entry = counts .entry((row.source_type, row.profile_id)) .or_insert(0_u32); *entry = entry.saturating_add(row.play_count); } counts }