use json::{object, array, JsonValue}; use actix_web::{HttpResponse, HttpRequest}; use rusqlite::params; use std::sync::Mutex; use std::thread; use lazy_static::lazy_static; use crate::encryption; use crate::sql::SQLite; use crate::router::global; lazy_static! { static ref DATABASE: SQLite = SQLite::new("live_statistics.db", setup_tables); } pub struct Live { pub live_id: i32, pub normal_failed: i64, pub normal_pass: i64, pub hard_failed: i64, pub hard_pass: i64, pub expert_failed: i64, pub expert_pass: i64, pub master_failed: i64, pub master_pass: i64, } fn setup_tables(conn: &SQLite) { conn.lock_and_exec("CREATE TABLE IF NOT EXISTS lives ( live_id INT NOT NULL PRIMARY KEY, normal_failed BIGINT NOT NULL, normal_pass BIGINT NOT NULL, hard_failed BIGINT NOT NULL, hard_pass BIGINT NOT NULL, expert_failed BIGINT NOT NULL, expert_pass BIGINT NOT NULL, master_failed BIGINT NOT NULL, master_pass BIGINT NOT NULL )", params!()); conn.lock_and_exec("CREATE TABLE IF NOT EXISTS scores ( live_id INT NOT NULL PRIMARY KEY, score_data TEXT NOT NULL )", params!()); } fn update_live_score(id: i64, uid: i64, score: i64) { if uid == 0 || score == 0 { return; } let info = DATABASE.lock_and_select("SELECT score_data FROM scores WHERE live_id=?1", params!(id)).unwrap_or(String::from("[]")); let scores = json::parse(&info).unwrap(); let mut result = array![]; let mut current = 0; let mut added = false; for i in 0..10 { if current >= 10 { break; } if scores[i].is_empty() && !added { added = true; result.push(object!{user: uid, score: score}).unwrap(); } if scores[i].is_empty() { break; } if scores[i]["score"].as_i64().unwrap() < score && !added { added = true; result.push(object!{user: uid, score: score}).unwrap(); current += 1; if current >= 10 { break; } } if scores[i]["user"].as_i64().unwrap() == uid && !added { return; } if scores[i]["user"].as_i64().unwrap() == uid { continue; } result.push(scores[i].clone()).unwrap(); current += 1; } if added { if DATABASE.lock_and_select("SELECT live_id FROM scores WHERE live_id=?1", params!(id)).is_ok() { DATABASE.lock_and_exec("UPDATE scores SET score_data=?1 WHERE live_id=?2", params!(json::stringify(result), id)); } else { DATABASE.lock_and_exec("INSERT INTO scores (score_data, live_id) VALUES (?1, ?2)", params!(json::stringify(result), id)); } } } pub fn live_completed(id: i64, level: i32, failed: bool, score: i64, uid: i64) { update_live_score(id, uid, score); match DATABASE.get_live_data(id) { Ok(info) => { let value = format!("{}_{}", if 1 == level { "normal" } else if 2 == level { "hard" } else if 3 == level { "expert" } else { "master" }, if failed { "failed" } else { "pass" } ); let new_info = if 1 == level && failed { info.normal_failed } else if 1 == level && !failed { info.normal_pass } else if 2 == level && failed { info.hard_failed } else if 2 == level && !failed { info.hard_pass } else if 3 == level && failed { info.expert_failed } else if 3 == level && !failed { info.expert_pass } else if 4 == level && failed { info.master_failed } else if 4 == level && !failed { info.master_pass } else { return; }; DATABASE.lock_and_exec(&format!("UPDATE lives SET {}=?1 WHERE live_id=?2", value), params!(new_info + 1, info.live_id)); }, Err(_) => { DATABASE.lock_and_exec("INSERT INTO lives (live_id, normal_failed, normal_pass, hard_failed, hard_pass, expert_failed, expert_pass, master_failed, master_pass) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", params!( id, if 1 == level && failed { 1 } else { 0 }, if 1 == level && !failed { 1 } else { 0 }, if 2 == level && failed { 1 } else { 0 }, if 2 == level && !failed { 1 } else { 0 }, if 3 == level && failed { 1 } else { 0 }, if 3 == level && !failed { 1 } else { 0 }, if 4 == level && failed { 1 } else { 0 }, if 4 == level && !failed { 1 } else { 0 } )); }, }; } lazy_static! { static ref CACHED_DATA: Mutex> = Mutex::new(None); static ref LIVE_LIST: JsonValue = { let mut info = object!{}; let items = json::parse(include_str!("json/live.json")).unwrap(); for (_i, data) in items.members().enumerate() { info[data["id"].to_string()] = data["masterMusicId"].clone(); } info }; } pub fn get_live_id(id: i64) -> i64 { LIVE_LIST[id.to_string()].as_i64().unwrap() } fn get_pass_percent(failed: i64, pass: i64) -> String { let total = (failed + pass) as f64; if failed + pass == 0 { return String::from("--:--%"); } let pass = pass as f64; format!("{:.2}%", pass / total * 100.0) } fn get_json() -> JsonValue { let lives = DATABASE.lock_and_select_all("SELECT live_id FROM lives", params!()).unwrap(); let mut rates = array![]; let mut ids = array![]; for (_i, id) in lives.members().enumerate() { let info = DATABASE.get_live_data(id.as_i64().unwrap()); if !info.is_ok() { continue; } let info = info.unwrap(); let to_push = object!{ master_live_id: info.live_id, normal: get_pass_percent(info.normal_failed, info.normal_pass), hard: get_pass_percent(info.hard_failed, info.hard_pass), expert: get_pass_percent(info.expert_failed, info.expert_pass), master: get_pass_percent(info.master_failed, info.master_pass) }; ids.push(get_live_id(info.live_id.into())).unwrap(); rates.push(to_push).unwrap(); } object!{ "cache": { "all_user_clear_rate": rates, "master_music_ids": ids, "event_live_list": [] }, "last_updated": global::timestamp() } } fn get_clearrate_json() -> JsonValue { loop { match CACHED_DATA.lock() { Ok(mut result) => { if result.is_none() { result.replace(get_json()); } let cache = result.as_ref().unwrap(); let rv = cache["cache"].clone(); if cache["last_updated"].as_u64().unwrap() + (60 * 60) < global::timestamp() { thread::spawn(|| { loop { match CACHED_DATA.lock() { Ok(mut result) => { let new = get_json(); result.replace(new.clone()); break; } Err(_) => { std::thread::sleep(std::time::Duration::from_millis(15)); } } } }); } return rv; } Err(_) => { std::thread::sleep(std::time::Duration::from_millis(15)); } } } } pub fn clearrate(req: HttpRequest) -> HttpResponse { let resp = object!{ "code": 0, "server_time": global::timestamp(), "data": get_clearrate_json() }; global::send(resp, req) } pub fn ranking(req: HttpRequest, body: String) -> HttpResponse { let body = json::parse(&encryption::decrypt_packet(&body).unwrap()).unwrap(); let live = body["master_live_id"].as_i64().unwrap(); let info = DATABASE.lock_and_select("SELECT score_data FROM scores WHERE live_id=?1", params!(live)).unwrap_or(String::from("[]")); let scores = json::parse(&info).unwrap(); let mut rank = array![]; for (i, data) in scores.members().enumerate() { let user = global::get_user(data["user"].as_i64().unwrap(), &object![], false); rank.push(object!{ rank: i + 1, user: user["user"].clone(), score: data["score"].as_i64().unwrap(), favorite_card: user["favorite_card"].clone(), guest_smile_card: user["guest_smile_card"].clone(), guest_cool_card: user["guest_cool_card"].clone(), guest_pure_card: user["guest_pure_card"].clone() }).unwrap(); } let resp = object!{ "code": 0, "server_time": global::timestamp(), "data": { "ranking_list": rank } }; global::send(resp, req) }