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::post::ps2outputs;
|
||||||
use crate::api::{CurrentUser, JsonApi, PolicyError::*, Ugc};
|
use crate::api::{CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::*;
|
use crate::rds_models::*;
|
||||||
use crate::schema;
|
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::serde::json::json;
|
use rocket::serde::json::json;
|
||||||
use rocket::serde::json::serde_json;
|
use rocket::serde::json::serde_json;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||||
use crate::cache::BlockDictCache;
|
use crate::cache::BlockDictCache;
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::*;
|
use crate::rds_models::*;
|
||||||
use crate::schema;
|
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
|
||||||
use futures::{future, join};
|
use futures::{future, join};
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::serde::{json::json, Serialize};
|
use rocket::serde::{json::json, Serialize};
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
#![allow(clippy::unnecessary_lazy_evaluations)]
|
#![allow(clippy::unnecessary_lazy_evaluations)]
|
||||||
|
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::random_hasher::RandomHasher;
|
use crate::random_hasher::RandomHasher;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::*;
|
use crate::rds_models::*;
|
||||||
use crate::schema;
|
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::outcome::try_outcome;
|
use rocket::outcome::try_outcome;
|
||||||
use rocket::request::{FromRequest, Outcome, Request};
|
use rocket::request::{FromRequest, Outcome, Request};
|
||||||
@@ -300,6 +297,7 @@ pub mod attention;
|
|||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
|
pub mod reaction;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod systemlog;
|
pub mod systemlog;
|
||||||
pub mod upload;
|
pub mod upload;
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
use crate::api::{ApiError, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||||
use crate::cache::*;
|
use crate::cache::*;
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::*;
|
use crate::rds_models::*;
|
||||||
use crate::schema;
|
|
||||||
use chrono::offset::Local;
|
use chrono::offset::Local;
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::serde::json::json;
|
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::api::{Api, CurrentUser, JsonApi, PolicyError::*, Ugc};
|
||||||
use crate::cache::*;
|
use crate::cache::*;
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::*;
|
use crate::rds_models::*;
|
||||||
use crate::schema;
|
|
||||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::futures::future::{self, OptionFuture};
|
use rocket::futures::future::{self, OptionFuture};
|
||||||
use rocket::serde::{
|
use rocket::serde::{
|
||||||
@@ -51,6 +48,9 @@ pub struct PostOutput {
|
|||||||
is_blocked: bool,
|
is_blocked: bool,
|
||||||
//blocked_count: Option<i32>,
|
//blocked_count: Option<i32>,
|
||||||
poll: Option<Value>,
|
poll: Option<Value>,
|
||||||
|
up_votes: i32,
|
||||||
|
down_votes: i32,
|
||||||
|
reaction_status: i32, // -1, 0, 1
|
||||||
// for old version frontend
|
// for old version frontend
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
likenum: i32,
|
likenum: i32,
|
||||||
@@ -117,6 +117,9 @@ async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> Api
|
|||||||
} else {
|
} else {
|
||||||
None
|
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
|
// for old version frontend
|
||||||
timestamp: p.create_time.timestamp(),
|
timestamp: p.create_time.timestamp(),
|
||||||
likenum: p.n_attentions,
|
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 {
|
macro_rules! post_cache_key {
|
||||||
($id: expr) => {
|
($id: expr) => {
|
||||||
format!("hole_v2:cache:post:{}", $id)
|
format!("hole_v2:cache:post:{}:v2", $id)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ async fn main() {
|
|||||||
"/_api/v2",
|
"/_api/v2",
|
||||||
routes![
|
routes![
|
||||||
api::attention::set_notification,
|
api::attention::set_notification,
|
||||||
|
api::reaction::reaction,
|
||||||
api::comment::add_comment,
|
api::comment::add_comment,
|
||||||
api::operation::set_title,
|
api::operation::set_title,
|
||||||
api::upload::local_upload,
|
api::upload::local_upload,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use crate::cache::*;
|
use crate::cache::*;
|
||||||
use crate::db_conn::{Conn, Db};
|
use crate::db_conn::{Conn, Db};
|
||||||
use crate::libs::diesel_logger::LoggingConnection;
|
|
||||||
use crate::random_hasher::random_string;
|
use crate::random_hasher::random_string;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::schema::*;
|
use crate::schema::*;
|
||||||
@@ -60,6 +59,8 @@ macro_rules! op_to_col_expr {
|
|||||||
|
|
||||||
macro_rules! update {
|
macro_rules! update {
|
||||||
($obj:expr, $table:ident, $db:expr, $({ $col:ident, $op:ident $v:expr }), + ) => {{
|
($obj:expr, $table:ident, $db:expr, $({ $col:ident, $op:ident $v:expr }), + ) => {{
|
||||||
|
use crate::schema;
|
||||||
|
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
let id = $obj.id;
|
let id = $obj.id;
|
||||||
$obj = $db
|
$obj = $db
|
||||||
.run(move |c| {
|
.run(move |c| {
|
||||||
@@ -83,9 +84,10 @@ macro_rules! base_query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! with_log {
|
macro_rules! with_log {
|
||||||
($c: expr) => {
|
($c: expr) => {{
|
||||||
|
use crate::libs::diesel_logger::LoggingConnection;
|
||||||
&LoggingConnection::new($c)
|
&LoggingConnection::new($c)
|
||||||
};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
|
#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
|
||||||
@@ -120,6 +122,8 @@ pub struct Post {
|
|||||||
pub hot_score: i32,
|
pub hot_score: i32,
|
||||||
pub allow_search: bool,
|
pub allow_search: bool,
|
||||||
pub room_id: i32,
|
pub room_id: i32,
|
||||||
|
pub up_votes: i32,
|
||||||
|
pub down_votes: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
|
#[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_SYSTEMLOG: &str = "hole_v2:systemlog_list";
|
||||||
const KEY_BANNED_USERS: &str = "hole_v2:banned_user_hash_list";
|
const KEY_BANNED_USERS: &str = "hole_v2:banned_user_hash_list";
|
||||||
const KEY_BLOCKED_COUNTER: &str = "hole_v2:blocked_counter";
|
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)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub enum LogType {
|
pub enum LogType {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ table! {
|
|||||||
hot_score -> Int4,
|
hot_score -> Int4,
|
||||||
allow_search -> Bool,
|
allow_search -> Bool,
|
||||||
room_id -> Int4,
|
room_id -> Int4,
|
||||||
|
up_votes -> Int4,
|
||||||
|
down_votes -> Int4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user