refactor(utils): extract reading_time helper
This commit is contained in:
parent
e683efe0ea
commit
15e7e2578d
@ -93,7 +93,7 @@ pub async fn create_post(
|
|||||||
|
|
||||||
// 计算字数与阅读时长,随文章一并持久化,供列表查询直接使用。
|
// 计算字数与阅读时长,随文章一并持久化,供列表查询直接使用。
|
||||||
let word_count = crate::utils::text::count_words(&content_md);
|
let word_count = crate::utils::text::count_words(&content_md);
|
||||||
let reading_time = (word_count / 200).max(1);
|
let reading_time = crate::utils::text::reading_time(word_count);
|
||||||
|
|
||||||
// 发布状态的文章设置当前发布时间;草稿则为 None。
|
// 发布状态的文章设置当前发布时间;草稿则为 None。
|
||||||
let published_at = if post_status == PostStatus::Published {
|
let published_at = if post_status == PostStatus::Published {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use crate::api::error::AppError;
|
|||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use crate::models::post::{Post, PostListItem, PostStatus};
|
use crate::models::post::{Post, PostListItem, PostStatus};
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use crate::utils::text::count_words;
|
use crate::utils::text::{count_words, reading_time};
|
||||||
|
|
||||||
/// 复用认证模块的当前 admin 用户获取逻辑。
|
/// 复用认证模块的当前 admin 用户获取逻辑。
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
@ -110,7 +110,7 @@ pub(super) async fn row_to_post_full(
|
|||||||
(stored_word_count as u32, stored_reading_time as u32)
|
(stored_word_count as u32, stored_reading_time as u32)
|
||||||
} else {
|
} else {
|
||||||
let wc = count_words(&content_md);
|
let wc = count_words(&content_md);
|
||||||
(wc, (wc / 200).max(1))
|
(wc, reading_time(wc))
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_html: Option<String> = row.get("content_html");
|
let content_html: Option<String> = row.get("content_html");
|
||||||
|
|||||||
@ -76,7 +76,7 @@ pub async fn rebuild_content_html(rebuild_all: bool) -> Result<RebuildResult, Se
|
|||||||
};
|
};
|
||||||
|
|
||||||
let word_count = crate::utils::text::count_words(&content_md);
|
let word_count = crate::utils::text::count_words(&content_md);
|
||||||
let reading_time = (word_count / 200).max(1);
|
let reading_time = crate::utils::text::reading_time(word_count);
|
||||||
|
|
||||||
match client
|
match client
|
||||||
.execute(
|
.execute(
|
||||||
|
|||||||
@ -18,7 +18,8 @@ use crate::models::post::PostStats;
|
|||||||
|
|
||||||
/// 获取文章统计信息。
|
/// 获取文章统计信息。
|
||||||
///
|
///
|
||||||
/// 需要 admin 权限;优先命中缓存,未命中时分别统计总数、草稿数与已发布数。
|
/// 需要 admin 权限;优先命中缓存,未命中时通过单次条件聚合查询同时统计
|
||||||
|
/// 未删除文章总数、草稿数与已发布数。
|
||||||
#[server(GetPostStats, "/api")]
|
#[server(GetPostStats, "/api")]
|
||||||
pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
|
||||||
let _user = get_current_admin_user().await?;
|
let _user = get_current_admin_user().await?;
|
||||||
|
|||||||
@ -57,7 +57,7 @@ pub async fn update_post(
|
|||||||
|
|
||||||
// 重新计算字数与阅读时长,保持与正文同步。
|
// 重新计算字数与阅读时长,保持与正文同步。
|
||||||
let word_count = crate::utils::text::count_words(&content_md);
|
let word_count = crate::utils::text::count_words(&content_md);
|
||||||
let reading_time = (word_count / 200).max(1);
|
let reading_time = crate::utils::text::reading_time(word_count);
|
||||||
|
|
||||||
let tx = client.transaction().await.map_err(AppError::tx)?;
|
let tx = client.transaction().await.map_err(AppError::tx)?;
|
||||||
|
|
||||||
|
|||||||
@ -72,6 +72,13 @@ pub fn count_words(md: &str) -> u32 {
|
|||||||
count.max(1)
|
count.max(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 由字数估算阅读时长(分钟)。
|
||||||
|
///
|
||||||
|
/// 按每分钟 200 字计算,至少返回 1 分钟。
|
||||||
|
pub fn reading_time(word_count: u32) -> u32 {
|
||||||
|
(word_count / 200).max(1)
|
||||||
|
}
|
||||||
|
|
||||||
/// 自动生成文本摘要,取去除 Markdown 后的前 200 个字符。
|
/// 自动生成文本摘要,取去除 Markdown 后的前 200 个字符。
|
||||||
pub fn auto_summary(md: &str) -> String {
|
pub fn auto_summary(md: &str) -> String {
|
||||||
let plain = strip_markdown(md);
|
let plain = strip_markdown(md);
|
||||||
@ -158,6 +165,21 @@ mod tests {
|
|||||||
assert_eq!(count_words(""), 1);
|
assert_eq!(count_words(""), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reading_time_defaults_to_one() {
|
||||||
|
assert_eq!(reading_time(0), 1);
|
||||||
|
assert_eq!(reading_time(1), 1);
|
||||||
|
assert_eq!(reading_time(199), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reading_time_scales_by_two_hundred() {
|
||||||
|
assert_eq!(reading_time(200), 1);
|
||||||
|
assert_eq!(reading_time(201), 1);
|
||||||
|
assert_eq!(reading_time(400), 2);
|
||||||
|
assert_eq!(reading_time(1000), 5);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_summary_truncates_at_200_chars() {
|
fn auto_summary_truncates_at_200_chars() {
|
||||||
let long_md: String = "a ".repeat(200);
|
let long_md: String = "a ".repeat(200);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user