Add public work read model and smooth puzzle transitions
This commit is contained in:
171
server-rs/crates/spacetime-client/src/public_work.rs
Normal file
171
server-rs/crates/spacetime-client/src/public_work.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use super::*;
|
||||
use crate::mapper::*;
|
||||
|
||||
impl SpacetimeClient {
|
||||
pub async fn list_public_work_gallery_entries(
|
||||
&self,
|
||||
) -> Result<Vec<PublicWorkGalleryEntryRecord>, 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::<Vec<_>>();
|
||||
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<Vec<PublicWorkDetailEntryRecord>, 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::<Vec<_>>();
|
||||
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<PublicWorkDetailEntryRecord, SpacetimeClientError> {
|
||||
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<PublicWorkDetailEntryRecord, SpacetimeClientError> {
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user