Skip to content

Commit

Permalink
第10节:查看与新增评论
Browse files Browse the repository at this point in the history
  • Loading branch information
za-songguo committed Jan 31, 2023
1 parent b6edfd2 commit d7935dd
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 22 deletions.
1 change: 0 additions & 1 deletion client/.gitignore

This file was deleted.

1 change: 0 additions & 1 deletion server/.gitignore

This file was deleted.

2 changes: 2 additions & 0 deletions server/src/comment/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod view;
pub mod new;
44 changes: 44 additions & 0 deletions server/src/comment/new.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use ntex::web::types::{Json, State};
use std::sync::Arc;

use crate::{
errors::CustomError,
models::{comment::Comment, user::User},
AppState,
};

/// 新增评论
/// 需要用户权限
pub async fn new_comment(
user: User,
comment: Json<Comment>,
state: State<Arc<AppState>>,
) -> Result<String, CustomError> {
let db_pool = &state.db_pool;

let user_id = user.id;
let article_id = match comment.article {
Some(id) => id,
None => return Err(CustomError::BadRequest("请提供要评论的文章的 ID".into())),
};

// 如果要评论的文章不存在
if sqlx::query!("SELECT id FROM articles WHERE id = $1", article_id as i32)
.fetch_optional(db_pool)
.await?
.is_none()
{
return Err(CustomError::BadRequest("要评论的文章不存在".into()));
}

sqlx::query!(
"INSERT INTO comments (user_id, content, article) VALUES ($1, $2, $3)",
user_id,
comment.content,
article_id as i32
)
.execute(db_pool)
.await?;

Ok("新增评论成功!".into())
}
40 changes: 40 additions & 0 deletions server/src/comment/view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::sync::Arc;

use ntex::web::types::{Json, Path, State};

use crate::{
errors::CustomError,
models::{comment::Comment, user::GithubUserInfo},
AppState,
};
/// 通过文章 ID 获取该文章的所有评论(包含发表评论的用户的信息)
pub async fn get_comments_for_article(
article_id: Path<(u32,)>,
state: State<Arc<AppState>>,
) -> Result<Json<Vec<Comment>>, CustomError> {
let db_pool = &state.db_pool;

let article_id = article_id.0;

// 查找对应文章的所有评论,拿到它们的 user_id, content, date 和 users 表里相同 user_id(对应的是 users 表里的 id) 的记录的 name, avatar_url
let comments = sqlx::query!(
"SELECT comments.user_id, comments.content, comments.date, users.name, users.avatar_url FROM comments JOIN users ON comments.user_id = users.id WHERE comments.article = $1", article_id as i32
)
.fetch_all(db_pool)
.await?
.iter()
.map(|i| Comment {
id: None,
user: Some(GithubUserInfo {
id: i.user_id,
login: i.name.clone(),
avatar_url: i.avatar_url.clone(),
}),
content: i.content.clone(),
date: Some(i.date),
article: None,
})
.collect::<Vec<Comment>>();

Ok(Json(comments))
}
29 changes: 19 additions & 10 deletions server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
mod article;
mod comment;
mod errors;
mod models;
mod user;

use article::{delete, edit, new, search, view};
use user::login;

use ntex::web::{self, middleware, App, HttpServer};
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};
use std::{env, sync::Arc};
Expand Down Expand Up @@ -49,12 +47,23 @@ async fn main() {
fn route(_state: Arc<AppState>, cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/article")
.route("/{id}", web::get().to(view::get_article))
.route("", web::post().to(new::new_article))
.route("", web::put().to(edit::edit_article))
.route("/{id}", web::delete().to(delete::delete_article))
.route("/search/{keyword}", web::get().to(search::search_article)),
.route("/{id}", web::get().to(article::view::get_article))
.route("", web::post().to(article::new::new_article))
.route("", web::put().to(article::edit::edit_article))
.route("/{id}", web::delete().to(article::delete::delete_article))
.route(
"/search/{keyword}",
web::get().to(article::search::search_article),
),
)
.service(web::scope("/articles").route("", web::get().to(view::get_articles_preview)))
.service(web::scope("/user").route("/login", web::post().to(login::github_login)));
.service(web::scope("/articles").route("", web::get().to(article::view::get_articles_preview)))
.service(web::scope("/user").route("/login", web::post().to(user::login::github_login)))
.service(
web::scope("/comment")
.route(
"/{id}",
web::get().to(comment::view::get_comments_for_article),
)
.route("", web::post().to(comment::new::new_comment)),
);
}
19 changes: 19 additions & 0 deletions server/src/models/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};

use super::user::GithubUserInfo;

/// 评论
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Comment {
// 评论 ID
pub id: Option<u32>,
/// 发表评论的用户的信息
/// 实现 Serialize 和 Deserialize
pub user: Option<GithubUserInfo>,
/// 评论内容
pub content: String,
/// 评论日期
pub date: Option<chrono::NaiveDate>,
/// 评论的文章
pub article: Option<u32>,
}
1 change: 1 addition & 0 deletions server/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod comment;
pub mod user;
pub mod article;
18 changes: 8 additions & 10 deletions server/src/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ntex::{
web::{ErrorRenderer, FromRequest},
};
use reqwest::Client;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

use crate::{errors::CustomError, AppState};

Expand All @@ -23,7 +23,7 @@ pub struct AccessToken {
}

/// Github 返回的用户信息
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GithubUserInfo {
/// Github 用户 ID
pub id: i32,
Expand All @@ -36,20 +36,22 @@ pub struct GithubUserInfo {
/// 网站的所有用户(包括管理员)(用于身份验证)
#[derive(Debug, Clone)]
pub struct User {
pub access_token: String,
pub id: i32,
}

/// 网站的管理员(用于身份验证)
#[derive(Debug, Clone)]
pub struct Admin {
pub access_token: String,
pub id: i32,
}

// 实现 FromRequest trait
// 可以从请求中提取用户数据并且验证用户的身份
// async fn handler(user: User / admin: Admin)
// 这样就可以为具体的 handler 添加身份认证了

// 通过验证的用户都是存在于我们的数据库中的,可以通过 ID 查到

impl<E: ErrorRenderer> FromRequest<E> for User {
type Error = CustomError;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
Expand Down Expand Up @@ -88,9 +90,7 @@ impl<E: ErrorRenderer> FromRequest<E> for User {
));
}

Ok(Self {
access_token: access_token.value().to_string(),
})
Ok(Self { id: user_id })
};

Box::pin(fut)
Expand Down Expand Up @@ -145,9 +145,7 @@ impl<E: ErrorRenderer> FromRequest<E> for Admin {
));
}

Ok(Self {
access_token: access_token.value().to_string(),
})
Ok(Self { id: user_id })
};

Box::pin(fut)
Expand Down

0 comments on commit d7935dd

Please sign in to comment.