域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

文章详情

本章将实现博客文章的详情显示功能。

数据库视图

CREATE VIEW v_topic_cat_detail AS
  SELECT t.id, title, html, hit, dateline,category_id,t.is_del,
         c.name AS category_name
    FROM
      topics AS t
      INNER JOIN categories AS c
          ON t.category_id=c.id
   WHERE c.is_del = false
         ;

该视图和v_topic_cat_list唯一不同的地方在于,它要的是topicshtml字段。

数据模型

// src/model.rs
#[derive(PostgresMapper, Serialize)]
#[pg_mapper(table="v_topic_cat_detail")]
pub struct TopicDetail{
    pub id:i64,
    pub title: String,
    pub category_id:i32,
    pub html:String,
    pub hit:i32,
    pub dateline:time::SystemTime,
    pub is_del:bool,
    pub category_name:String,
}
impl TopicDetail {
    pub fn dateline(&self) ->String {
        dateline(self.dateline.clone())
    }
}

fn dateline(dt:time::SystemTime) -> String {
    let ts = dt.duration_since(time::UNIX_EPOCH).unwrap_or(time::Duration::from_secs(0)).as_secs() as i64;
    Local.timestamp(ts, 0).format("%Y/%m/%d %H:%M:%S").to_string()
}

这里新增了 dateline()函数,用于将时间以字符串的形式显示。为了代码重用,TopicList::dateline()也重构为调用该函数

// src/model.rs
impl TopicList {
    pub fn dateline(&self) ->String {
        dateline(self.dateline.clone())
    }
}

模板

视图类

文章详情的视图类位于src/view/frontend/topic.rs,请自行查看。

handler

topic::detail()用于通过id获取文章详情。

数据库操作

pub async fn detail(client: &Client, id: i64) -> Result<TopicDetail> {
    super::execute(client, "UPDATE topics SET hit=hit+1 WHERE id=$1", &[&id]).await ?;
    let sql = "SELECT id,title,category_id,html,hit,dateline,is_del,category_name FROM v_topic_cat_detail WHERE is_del=false and id=$1 LIMIT 1";
    super::query_row(client, sql, &[&id]).await
}

首先,使文章的浏览次数加1,然后返回文章的数据。

原因在于,我们需要返回的数据来自于视图,如果:

另外,此处不需要用事务,因为如果UPDATE出错,那么整个函数都退出了(返回值是Err(AppError))——除非你把SELECT写在UPDATE前面。

路由

本章代码位于09/文章详情分支。

要查看完整内容,请先登录