From 1a3b44f3495fb3a9989f003d2fbd96c0e1525edd Mon Sep 17 00:00:00 2001 From: red binder Date: Thu, 18 Jun 2026 16:26:10 +0200 Subject: [PATCH] implement recommended courses finally --- ...f7914f641fb379982b5e52b760b87e0cca945.json | 116 +++++++++++++++ ...2f3a77f61ff0d3fbe54cf34e967fbb852cbb8.json | 40 +++++ rnex-core/src/nex/datastore.rs | 140 +++++++++++++++++- rnex-core/src/rmc/protocols/datastore.rs | 8 +- 4 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 rnex-core/.sqlx/query-77ab7a78fbe302864ac617ac06ff7914f641fb379982b5e52b760b87e0cca945.json create mode 100644 rnex-core/.sqlx/query-8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8.json diff --git a/rnex-core/.sqlx/query-77ab7a78fbe302864ac617ac06ff7914f641fb379982b5e52b760b87e0cca945.json b/rnex-core/.sqlx/query-77ab7a78fbe302864ac617ac06ff7914f641fb379982b5e52b760b87e0cca945.json new file mode 100644 index 0000000..152e72a --- /dev/null +++ b/rnex-core/.sqlx/query-77ab7a78fbe302864ac617ac06ff7914f641fb379982b5e52b760b87e0cca945.json @@ -0,0 +1,116 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n object.data_id,\n object.owner,\n object.size,\n object.name,\n object.data_type,\n object.meta_binary,\n object.permission,\n object.permission_recipients,\n object.delete_permission,\n object.delete_permission_recipients,\n object.period,\n object.refer_data_id,\n object.flag,\n object.tags,\n object.creation_date,\n object.update_date,\n ranking.value\n FROM datastore.objects object\n JOIN datastore.object_custom_rankings ranking\n ON\n object.data_id = ranking.data_id AND\n object.upload_completed = TRUE AND\n object.deleted = FALSE AND\n object.under_review = FALSE AND\n ranking.application_id = 0\n ORDER BY RANDOM()\n LIMIT 100\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "data_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "owner", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "size", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "data_type", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "meta_binary", + "type_info": "Bytea" + }, + { + "ordinal": 6, + "name": "permission", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "permission_recipients", + "type_info": "Int4Array" + }, + { + "ordinal": 8, + "name": "delete_permission", + "type_info": "Int4" + }, + { + "ordinal": 9, + "name": "delete_permission_recipients", + "type_info": "Int4Array" + }, + { + "ordinal": 10, + "name": "period", + "type_info": "Int4" + }, + { + "ordinal": 11, + "name": "refer_data_id", + "type_info": "Int8" + }, + { + "ordinal": 12, + "name": "flag", + "type_info": "Int4" + }, + { + "ordinal": 13, + "name": "tags", + "type_info": "TextArray" + }, + { + "ordinal": 14, + "name": "creation_date", + "type_info": "Timestamp" + }, + { + "ordinal": 15, + "name": "update_date", + "type_info": "Timestamp" + }, + { + "ordinal": 16, + "name": "value", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ] + }, + "hash": "77ab7a78fbe302864ac617ac06ff7914f641fb379982b5e52b760b87e0cca945" +} diff --git a/rnex-core/.sqlx/query-8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8.json b/rnex-core/.sqlx/query-8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8.json new file mode 100644 index 0000000..8597a5a --- /dev/null +++ b/rnex-core/.sqlx/query-8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT slot, total_value, count, initial_value FROM datastore.object_ratings WHERE data_id=$1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "slot", + "type_info": "Int2" + }, + { + "ordinal": 1, + "name": "total_value", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "count", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "initial_value", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + true, + false, + true + ] + }, + "hash": "8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8" +} diff --git a/rnex-core/src/nex/datastore.rs b/rnex-core/src/nex/datastore.rs index 9dd2898..3851268 100644 --- a/rnex-core/src/nex/datastore.rs +++ b/rnex-core/src/nex/datastore.rs @@ -1,4 +1,4 @@ -use futures::TryFutureExt; +use futures::TryStreamExt; use rnex_core::nex::user::User; use rnex_core::PID; use rnex_core::executables::common::{ @@ -685,6 +685,37 @@ async fn change_meta_object_check(param: &DataStoreChangeMetaParam) -> Result<() Ok(()) } +async fn get_rating_with_slot_data_id(dataid: u64) -> Result, ErrorCode> { + check_object_availability(dataid, 0).await?; + + let rows = sqlx::query!( + r#" + SELECT slot, total_value, count, initial_value FROM datastore.object_ratings WHERE data_id=$1 + "#, + dataid as i64 + ) + .fetch_all(get_db()) + .await + .map_err(|e| { + log::error!("DB Error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + + let ratings = rows + .into_iter() + .map(|row| RatingInfoWithSlot { + slot: row.slot as i8, + rating: RatingInfo { + total_value: row.total_value.unwrap_or(0), + count: row.count as u32, + initial_value: row.initial_value.unwrap_or(0), + }, + }) + .collect::>(); + + Ok(ratings) +} + impl DataStore for User { async fn get_meta(&self, metaparam: GetMetaParam) -> Result { let mut meta_info = if metaparam.dataid != 0 { @@ -1346,4 +1377,111 @@ impl DataStore for User { Ok(()) } + + async fn recommended_course_search_object(&self, course_search_param: DataStoreSearchParam, extra_data: Vec) -> Result, ErrorCode> { + let mut courses = Vec::new(); + + let mut stream = sqlx::query!( + r#" + SELECT + object.data_id, + object.owner, + object.size, + object.name, + object.data_type, + object.meta_binary, + object.permission, + object.permission_recipients, + object.delete_permission, + object.delete_permission_recipients, + object.period, + object.refer_data_id, + object.flag, + object.tags, + object.creation_date, + object.update_date, + ranking.value + FROM datastore.objects object + JOIN datastore.object_custom_rankings ranking + ON + object.data_id = ranking.data_id AND + object.upload_completed = TRUE AND + object.deleted = FALSE AND + object.under_review = FALSE AND + ranking.application_id = 0 + ORDER BY RANDOM() + LIMIT 100 + "# + ) + .fetch(get_db()); + + while let Some(row) = stream.try_next().await.map_err(|e| { + eprintln!("stream error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })? { + + let permission = Permission { + permission: row.permission.unwrap_or(0) as u8, + recipient_ids: row.permission_recipients.unwrap_or_default(), + }; + + let del_permission = Permission { + permission: row.delete_permission.unwrap_or(0) as u8, + recipient_ids: row.delete_permission_recipients.unwrap_or_default(), + }; + + let meta_binary = row.meta_binary + .map(|bytes| QBuffer(bytes)) + .unwrap_or_default(); + + let created_time = row.creation_date + .map(|t| KerberosDateTime::from_u64(t.assume_utc().unix_timestamp() as u64)) + .unwrap_or_else(|| KerberosDateTime::from_u64(0)); + + let updated_time = row.update_date + .map(|t| KerberosDateTime::from_u64(t.assume_utc().unix_timestamp() as u64)) + .unwrap_or_else(|| KerberosDateTime::from_u64(0)); + + let referred_time = row.creation_date + .map(|t| KerberosDateTime::from_u64(t.assume_utc().unix_timestamp() as u64)) + .unwrap_or_else(|| KerberosDateTime::from_u64(0)); + + let mut meta_info = GetMetaInfo { + dataid: row.data_id as u64, + owner: row.owner.unwrap_or(0), + size: row.size.unwrap_or(0) as u32, + name: row.name.unwrap_or_default(), + data_type: row.data_type.unwrap_or(0) as u16, + meta_binary, + permission, + del_permission, + period: row.period.unwrap_or(0) as u16, + status: 0, + referred_count: 0, + refer_dat_id: row.refer_data_id.unwrap_or(0) as u32, + flag: row.flag.unwrap_or(0) as u32, + tags: row.tags.unwrap_or_default(), + expire_time: KerberosDateTime::from_u64(0x9C3F3E0000), + created_time, + updated_time, + referred_time, + ratings: Vec::new(), + }; + + match get_rating_with_slot_data_id(row.data_id as u64).await { + Ok(ratings) => meta_info.ratings = ratings, + Err(e) => return Err(e), + } + + let course = DataStoreCustomRankingResult { + order: 0, + score: row.value.unwrap_or(0) as u32, + meta_info, + }; + + courses.push(course); + } + + Ok(courses) + } } diff --git a/rnex-core/src/rmc/protocols/datastore.rs b/rnex-core/src/rmc/protocols/datastore.rs index 5aac7c7..a5e5439 100644 --- a/rnex-core/src/rmc/protocols/datastore.rs +++ b/rnex-core/src/rmc/protocols/datastore.rs @@ -20,7 +20,7 @@ pub struct Permission { pub recipient_ids: Vec, } -#[derive(RmcSerialize, Clone)] +#[derive(RmcSerialize, Clone, Default)] #[rmc_struct(0)] pub struct RatingInfoWithSlot { pub slot: i8, @@ -313,6 +313,12 @@ pub trait DataStore { course_search_param: DataStoreSearchParam, extra_data: Vec, ) -> Result, ErrorCode>; + #[method_id(66)] + async fn recommended_course_search_object( + &self, + course_search_param: DataStoreSearchParam, + extra_data: Vec, + ) -> Result, ErrorCode>; #[method_id(74)] async fn get_application_config_string( &self,