feat: upvote & downvote
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE posts
|
||||
DROP COLUMN up_votes,
|
||||
DROP COLUMN down_votes
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE posts
|
||||
ADD COLUMN up_votes INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN down_votes INTEGER NOT NULL DEFAULT 0
|
||||
@@ -1,12 +1,9 @@
|
||||
use crate::api::post::ps2outputs;
|
||||
use crate::api::{CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::form::Form;
|
||||
use rocket::serde::json::json;
|
||||
use rocket::serde::json::serde_json;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||
use crate::cache::BlockDictCache;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use futures::{future, join};
|
||||
use rocket::form::Form;
|
||||
use rocket::serde::{json::json, Serialize};
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
#![allow(clippy::unnecessary_lazy_evaluations)]
|
||||
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::random_hasher::RandomHasher;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::http::Status;
|
||||
use rocket::outcome::try_outcome;
|
||||
use rocket::request::{FromRequest, Outcome, Request};
|
||||
@@ -300,6 +297,7 @@ pub mod attention;
|
||||
pub mod comment;
|
||||
pub mod operation;
|
||||
pub mod post;
|
||||
pub mod reaction;
|
||||
pub mod search;
|
||||
pub mod systemlog;
|
||||
pub mod upload;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||
use crate::cache::*;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use chrono::offset::Local;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::form::Form;
|
||||
use rocket::serde::json::json;
|
||||
|
||||
|
||||
@@ -3,12 +3,9 @@ use crate::api::vote::get_poll_dict;
|
||||
use crate::api::{Api, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||
use crate::cache::*;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::form::Form;
|
||||
use rocket::futures::future::{self, OptionFuture};
|
||||
use rocket::serde::{
|
||||
@@ -51,6 +48,9 @@ pub struct PostOutput {
|
||||
is_blocked: bool,
|
||||
//blocked_count: Option<i32>,
|
||||
poll: Option<Value>,
|
||||
up_votes: i32,
|
||||
down_votes: i32,
|
||||
reaction_status: i32, // -1, 0, 1
|
||||
// for old version frontend
|
||||
timestamp: i64,
|
||||
likenum: i32,
|
||||
@@ -117,6 +117,9 @@ async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> Api
|
||||
} else {
|
||||
None
|
||||
},
|
||||
up_votes: p.up_votes,
|
||||
down_votes: p.down_votes,
|
||||
reaction_status: get_user_post_reaction_status(rconn, p.id, &user.namehash).await?,
|
||||
// for old version frontend
|
||||
timestamp: p.create_time.timestamp(),
|
||||
likenum: p.n_attentions,
|
||||
|
||||
65
src/api/reaction.rs
Normal file
65
src/api/reaction.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use crate::api::{CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||
use crate::db_conn::Db;
|
||||
use crate::models::*;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::*;
|
||||
use rocket::form::Form;
|
||||
use rocket::serde::json::json;
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct ReactionInput {
|
||||
#[field(validate = range(-1..2))]
|
||||
status: i32,
|
||||
}
|
||||
|
||||
#[post("/post/<pid>/reaction", data = "<ri>")]
|
||||
pub async fn reaction(
|
||||
pid: i32,
|
||||
ri: Form<ReactionInput>,
|
||||
user: CurrentUser,
|
||||
db: Db,
|
||||
rconn: RdsConn,
|
||||
) -> JsonApi {
|
||||
user.id.ok_or(YouAreTmp)?;
|
||||
|
||||
let mut p = Post::get(&db, &rconn, pid).await?;
|
||||
p.check_permission(&user, "r")?;
|
||||
let mut r_up = Reaction::init(pid, 1, &rconn);
|
||||
let mut r_down = Reaction::init(pid, -1, &rconn);
|
||||
|
||||
let (delta_up, delta_down): (i32, i32) = match ri.status {
|
||||
1 => (
|
||||
r_up.add(&user.namehash).await? as i32,
|
||||
-(r_down.rem(&user.namehash).await? as i32),
|
||||
),
|
||||
-1 => (
|
||||
-(r_up.rem(&user.namehash).await? as i32),
|
||||
r_down.add(&user.namehash).await? as i32,
|
||||
),
|
||||
_ => (
|
||||
-(r_up.rem(&user.namehash).await? as i32),
|
||||
-(r_down.rem(&user.namehash).await? as i32),
|
||||
),
|
||||
};
|
||||
|
||||
if delta_up != 0 || delta_down != 0 {
|
||||
update!(
|
||||
p,
|
||||
posts,
|
||||
&db,
|
||||
{ up_votes, add delta_up },
|
||||
{ down_votes, add delta_down }
|
||||
);
|
||||
|
||||
p.refresh_cache(&rconn, false).await;
|
||||
}
|
||||
|
||||
Ok(json!({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"up_votes": p.up_votes,
|
||||
"down_votes": p.down_votes,
|
||||
"reaction_status": ri.status,
|
||||
},
|
||||
}))
|
||||
}
|
||||
@@ -17,7 +17,7 @@ const CUT_LENGTH: isize = 100;
|
||||
|
||||
macro_rules! post_cache_key {
|
||||
($id: expr) => {
|
||||
format!("hole_v2:cache:post:{}", $id)
|
||||
format!("hole_v2:cache:post:{}:v2", $id)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ async fn main() {
|
||||
"/_api/v2",
|
||||
routes![
|
||||
api::attention::set_notification,
|
||||
api::reaction::reaction,
|
||||
api::comment::add_comment,
|
||||
api::operation::set_title,
|
||||
api::upload::local_upload,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
use crate::cache::*;
|
||||
use crate::db_conn::{Conn, Db};
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::random_hasher::random_string;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::schema::*;
|
||||
@@ -60,6 +59,8 @@ macro_rules! op_to_col_expr {
|
||||
|
||||
macro_rules! update {
|
||||
($obj:expr, $table:ident, $db:expr, $({ $col:ident, $op:ident $v:expr }), + ) => {{
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
let id = $obj.id;
|
||||
$obj = $db
|
||||
.run(move |c| {
|
||||
@@ -83,9 +84,10 @@ macro_rules! base_query {
|
||||
}
|
||||
|
||||
macro_rules! with_log {
|
||||
($c: expr) => {
|
||||
($c: expr) => {{
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
&LoggingConnection::new($c)
|
||||
};
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
|
||||
@@ -120,6 +122,8 @@ pub struct Post {
|
||||
pub hot_score: i32,
|
||||
pub allow_search: bool,
|
||||
pub room_id: i32,
|
||||
pub up_votes: i32,
|
||||
pub down_votes: i32,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
|
||||
|
||||
@@ -48,6 +48,14 @@ macro_rules! add {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! rem {
|
||||
($vtype:ty) => {
|
||||
pub async fn rem(&mut self, v: $vtype) -> RedisResult<usize> {
|
||||
self.rconn.srem(&self.key, v).await
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const KEY_SYSTEMLOG: &str = "hole_v2:systemlog_list";
|
||||
const KEY_BANNED_USERS: &str = "hole_v2:banned_user_hash_list";
|
||||
const KEY_BLOCKED_COUNTER: &str = "hole_v2:blocked_counter";
|
||||
@@ -106,6 +114,34 @@ impl Attention {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reaction {
|
||||
key: String,
|
||||
rconn: RdsConn,
|
||||
}
|
||||
|
||||
impl Reaction {
|
||||
init!(i32, i32, "hole_v2:reaction:{}:{}");
|
||||
|
||||
add!(&str);
|
||||
|
||||
rem!(&str);
|
||||
|
||||
has!(&str);
|
||||
}
|
||||
|
||||
pub async fn get_user_post_reaction_status(
|
||||
rconn: &RdsConn,
|
||||
pid: i32,
|
||||
namehash: &str,
|
||||
) -> RedisResult<i32> {
|
||||
for rt in [-1, 1] {
|
||||
if Reaction::init(pid, rt, rconn).has(namehash).await? {
|
||||
return Ok(rt);
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub enum LogType {
|
||||
|
||||
@@ -29,6 +29,8 @@ table! {
|
||||
hot_score -> Int4,
|
||||
allow_search -> Bool,
|
||||
room_id -> Int4,
|
||||
up_votes -> Int4,
|
||||
down_votes -> Int4,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user