You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

317 lines
8.7 KiB

#![allow(clippy::all)]
use crate::db_conn::Db;
use crate::libs::diesel_logger::LoggingConnection;
use crate::rds_conn::RdsConn;
use crate::rds_models::PostCache;
use crate::schema::*;
use chrono::{offset::Utc, DateTime};
use diesel::{
insert_into, BoolExpressionMethods, ExpressionMethods, QueryDsl, QueryResult, RunQueryDsl,
TextExpressionMethods,
};
use rocket::serde::{Deserialize, Serialize};
no_arg_sql_function!(RANDOM, (), "Represents the sql RANDOM() function");
macro_rules! get {
($table:ident) => {
pub async fn get(db: &Db, id: i32) -> QueryResult<Self> {
let pid = id;
db.run(move |c| $table::table.find(pid).first(with_log!((c))))
.await
}
};
}
macro_rules! get_multi {
($table:ident) => {
pub async fn get_multi(db: &Db, ids: Vec<i32>) -> QueryResult<Vec<Self>> {
// can use eq(any()) for postgres
db.run(move |c| {
$table::table
.filter($table::id.eq_any(ids))
.filter($table::is_deleted.eq(false))
.order($table::id.desc())
.load(with_log!(c))
})
.await
}
};
}
macro_rules! set_deleted {
($table:ident) => {
pub async fn set_deleted(&self, db: &Db) -> QueryResult<usize> {
let pid = self.id;
db.run(move |c| {
diesel::update($table::table.find(pid))
.set($table::is_deleted.eq(true))
.execute(with_log!(c))
})
.await
}
};
}
macro_rules! base_query {
($table:ident) => {
$table::table
.into_boxed()
.filter($table::is_deleted.eq(false))
};
}
macro_rules! with_log {
($c: expr) => {
&LoggingConnection::new($c)
};
}
#[derive(Queryable, Insertable)]
pub struct Comment {
pub id: i32,
pub author_hash: String,
pub author_title: String,
pub is_tmp: bool,
pub content: String,
pub create_time: DateTime<Utc>,
pub is_deleted: bool,
pub allow_search: bool,
pub post_id: i32,
}
#[derive(Queryable, Insertable, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Post {
pub id: i32,
pub author_hash: String,
pub content: String,
pub cw: String,
pub author_title: String,
pub is_tmp: bool,
pub n_attentions: i32,
pub n_comments: i32,
pub create_time: DateTime<Utc>,
pub last_comment_time: DateTime<Utc>,
pub is_deleted: bool,
pub is_reported: bool,
pub hot_score: i32,
pub allow_search: bool,
}
#[derive(Queryable, Insertable)]
pub struct User {
pub id: i32,
pub name: String,
pub token: String,
pub is_admin: bool,
}
#[derive(Insertable)]
#[table_name = "posts"]
pub struct NewPost {
pub content: String,
pub cw: String,
pub author_hash: String,
pub author_title: String,
pub is_tmp: bool,
pub n_attentions: i32,
pub allow_search: bool,
// TODO: tags
}
impl Post {
get!(posts);
get_multi!(posts);
set_deleted!(posts);
pub async fn get_with_cache(db: &Db, rconn: &RdsConn, id: i32) -> QueryResult<Self> {
let mut cacher = PostCache::init(&id, &rconn);
if let Some(p) = cacher.get().await {
dbg!("hint and use post cache");
Ok(p)
} else {
let p = Self::get(db, id).await?;
cacher.set(&p).await;
Ok(p)
}
}
pub async fn gets_by_page(
db: &Db,
order_mode: u8,
start: i64,
limit: i64,
) -> QueryResult<Vec<Self>> {
db.run(move |c| {
let mut query = base_query!(posts);
if order_mode > 0 {
query = query.filter(posts::is_reported.eq(false))
}
query = match order_mode {
0 => query.order(posts::id.desc()),
1 => query.order(posts::last_comment_time.desc()),
2 => query.order(posts::hot_score.desc()),
3 => query.order(RANDOM),
_ => panic!("Wrong order mode!"),
};
query.offset(start).limit(limit).load(with_log!(c))
})
.await
}
pub async fn search(
db: &Db,
search_mode: u8,
search_text: String,
start: i64,
limit: i64,
) -> QueryResult<Vec<Self>> {
let search_text2 = search_text.replace("%", "\\%");
db.run(move |c| {
let pat;
let mut query = base_query!(posts)
.distinct()
.left_join(comments::table);
// 先用搜索+缓存,性能有问题了再真的做tag表
query = match search_mode {
0 => {
pat = format!("%#{}%", &search_text2);
query
.filter(posts::cw.eq(&search_text))
.or_filter(posts::cw.eq(format!("#{}", &search_text)))
.or_filter(posts::content.like(&pat))
.or_filter(comments::content.like(&pat).and(comments::is_deleted.eq(false)))
}
1 => {
pat = format!("%{}%", search_text2.replace(" ", "%"));
query
.filter(posts::content.like(&pat).or(comments::content.like(&pat)))
.filter(posts::allow_search.eq(true))
}
2 => query
.filter(posts::author_title.eq(&search_text))
.or_filter(comments::author_title.eq(&search_text)),
_ => panic!("Wrong search mode!"),
};
query
.order(posts::id.desc())
.offset(start)
.limit(limit)
.load(with_log!(c))
})
.await
}
pub async fn create(db: &Db, new_post: NewPost) -> QueryResult<Self> {
// TODO: tags
db.run(move |c| {
insert_into(posts::table)
.values(&new_post)
.get_result(with_log!(c))
})
.await
}
pub async fn update_cw(&self, db: &Db, new_cw: String) -> QueryResult<usize> {
let pid = self.id;
db.run(move |c| {
diesel::update(posts::table.find(pid))
.set(posts::cw.eq(new_cw))
.execute(with_log!(c))
})
.await
}
pub async fn change_n_comments(&self, db: &Db, delta: i32) -> QueryResult<Self> {
let pid = self.id;
db.run(move |c| {
diesel::update(posts::table.find(pid))
.set(posts::n_comments.eq(posts::n_comments + delta))
.get_result(with_log!(c))
})
.await
}
pub async fn change_n_attentions(&self, db: &Db, delta: i32) -> QueryResult<Self> {
let pid = self.id;
db.run(move |c| {
diesel::update(posts::table.find(pid))
.set(posts::n_attentions.eq(posts::n_attentions + delta))
.get_result(with_log!(c))
})
.await
}
pub async fn change_hot_score(&self, db: &Db, delta: i32) -> QueryResult<Self> {
let pid = self.id;
db.run(move |c| {
diesel::update(posts::table.find(pid))
.set(posts::hot_score.eq(posts::hot_score + delta))
.get_result(with_log!(c))
})
.await
}
pub async fn set_instance_cache(&self, rconn: &RdsConn) {
PostCache::init(&self.id, rconn).set(self).await;
}
pub async fn refresh_cache(&self, rconn: &RdsConn, is_new: bool) {
self.set_instance_cache(rconn).await;
}
}
impl User {
pub async fn get_by_token(db: &Db, token: &str) -> Option<Self> {
let token = token.to_string();
db.run(move |c| {
users::table
.filter(users::token.eq(token))
.first(with_log!(c))
})
.await
.ok()
}
}
#[derive(Insertable)]
#[table_name = "comments"]
pub struct NewComment {
pub content: String,
pub author_hash: String,
pub author_title: String,
pub is_tmp: bool,
pub post_id: i32,
}
impl Comment {
get!(comments);
set_deleted!(comments);
pub async fn create(db: &Db, new_comment: NewComment) -> QueryResult<Self> {
db.run(move |c| {
insert_into(comments::table)
.values(&new_comment)
.get_result(with_log!(c))
})
.await
}
pub async fn gets_by_post_id(db: &Db, post_id: i32) -> QueryResult<Vec<Self>> {
let pid = post_id;
db.run(move |c| {
comments::table
.filter(comments::post_id.eq(pid))
.order(comments::id)
.load(with_log!(c))
})
.await
}
}