refactor(utils): extract reading_time helper

This commit is contained in:
xfy 2026-06-17 16:25:22 +08:00
parent e683efe0ea
commit 15e7e2578d
6 changed files with 29 additions and 6 deletions

View File

@ -93,7 +93,7 @@ pub async fn create_post(
// 计算字数与阅读时长,随文章一并持久化,供列表查询直接使用。
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。
let published_at = if post_status == PostStatus::Published {

View File

@ -8,7 +8,7 @@ use crate::api::error::AppError;
#[cfg(feature = "server")]
use crate::models::post::{Post, PostListItem, PostStatus};
#[cfg(feature = "server")]
use crate::utils::text::count_words;
use crate::utils::text::{count_words, reading_time};
/// 复用认证模块的当前 admin 用户获取逻辑。
#[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)
} else {
let wc = count_words(&content_md);
(wc, (wc / 200).max(1))
(wc, reading_time(wc))
};
let content_html: Option<String> = row.get("content_html");

View File

@ -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 reading_time = (word_count / 200).max(1);
let reading_time = crate::utils::text::reading_time(word_count);
match client
.execute(

View File

@ -18,7 +18,8 @@ use crate::models::post::PostStats;
/// 获取文章统计信息。
///
/// 需要 admin 权限;优先命中缓存,未命中时分别统计总数、草稿数与已发布数。
/// 需要 admin 权限;优先命中缓存,未命中时通过单次条件聚合查询同时统计
/// 未删除文章总数、草稿数与已发布数。
#[server(GetPostStats, "/api")]
pub async fn get_post_stats() -> Result<PostStatsResponse, ServerFnError> {
let _user = get_current_admin_user().await?;

View File

@ -57,7 +57,7 @@ pub async fn update_post(
// 重新计算字数与阅读时长,保持与正文同步。
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)?;

View File

@ -72,6 +72,13 @@ pub fn count_words(md: &str) -> u32 {
count.max(1)
}
/// 由字数估算阅读时长(分钟)。
///
/// 按每分钟 200 字计算,至少返回 1 分钟。
pub fn reading_time(word_count: u32) -> u32 {
(word_count / 200).max(1)
}
/// 自动生成文本摘要,取去除 Markdown 后的前 200 个字符。
pub fn auto_summary(md: &str) -> String {
let plain = strip_markdown(md);
@ -158,6 +165,21 @@ mod tests {
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]
fn auto_summary_truncates_at_200_chars() {
let long_md: String = "a ".repeat(200);