From b48ed4d903752c55cff19f6b728d8c5ed4b0f7a1 Mon Sep 17 00:00:00 2001 From: tone Date: Sat, 27 Dec 2025 13:48:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E5=8D=9A=E5=AE=A2?= =?UTF-8?q?=E9=A1=B5=E4=BB=A5=E6=94=AF=E6=8C=81slug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/src/blog/blog.controller.ts | 16 ++++++++++------ apps/backend/src/blog/blog.service.ts | 9 ++++++++- .../app/(with-header-footer)/blog/[id]/page.tsx | 15 +++------------ .../app/(with-header-footer)/blog/page.tsx | 10 +++------- apps/frontend/lib/api/endpoints/blog.server.ts | 9 +++++---- 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/apps/backend/src/blog/blog.controller.ts b/apps/backend/src/blog/blog.controller.ts index c2a84d4..0d90a85 100644 --- a/apps/backend/src/blog/blog.controller.ts +++ b/apps/backend/src/blog/blog.controller.ts @@ -31,12 +31,16 @@ export class BlogController { return this.blogService.list(); } - @Get(':id') - async getBlog( - @Param('id', new ParseUUIDPipe({ version: '4' })) id: string, + @Get(':id/slug') + async getBlogBySlug( + @Param('id') slug: string, @Query('p') password?: string, ) { - const blog = await this.blogService.findById(id); + if (slug.trim().length === 0) { + throw new BadRequestException('文章不存在'); + } + + const blog = await this.blogService.findBySlug(slug); if (!blog) throw new BadRequestException('文章不存在或无权限访问'); if (!blog.permissions.includes(BlogPermission.Public)) { @@ -46,7 +50,7 @@ export class BlogController { } else { // 判断密码是否正确 if ( - !password || + typeof password !== 'string' || this.blogService.hashPassword(password) !== blog.password_hash ) { throw new BadRequestException('文章不存在或无权限访问'); @@ -57,7 +61,7 @@ export class BlogController { const blogDataRes = await fetch(`${blog.contentUrl}`); const blogContent = await blogDataRes.text(); - await this.blogService.incrementViewCount(id); + this.blogService.incrementViewCount(blog.id).catch(() => null); return { id: blog.id, title: blog.title, diff --git a/apps/backend/src/blog/blog.service.ts b/apps/backend/src/blog/blog.service.ts index 41bb138..ad43570 100644 --- a/apps/backend/src/blog/blog.service.ts +++ b/apps/backend/src/blog/blog.service.ts @@ -35,13 +35,14 @@ export class BlogService { return i; } - const { createdAt, updatedAt, deletedAt, id, title, viewCount, description } = i; + const { createdAt, updatedAt, deletedAt, id, title, viewCount, description, slug } = i; return { createdAt, updatedAt, deletedAt, id, title, + slug, viewCount, description, }; @@ -96,6 +97,12 @@ export class BlogService { return await this.blogRepository.findOneBy({ id }); } + async findBySlug(slug: string) { + return this.blogRepository.findOne({ + where: { slug } + }) + } + async incrementViewCount(id: string) { await this.blogRepository.increment({ id }, 'viewCount', 1); } diff --git a/apps/frontend/app/(with-header-footer)/blog/[id]/page.tsx b/apps/frontend/app/(with-header-footer)/blog/[id]/page.tsx index 6a2edcb..069b8db 100644 --- a/apps/frontend/app/(with-header-footer)/blog/[id]/page.tsx +++ b/apps/frontend/app/(with-header-footer)/blog/[id]/page.tsx @@ -27,17 +27,8 @@ async function parseBlogParams({ params: paramsPromise, searchParams: searchPara } } - const hex = Array.from(base62.decode(params.id as string)).map(b => b.toString(16).padStart(2, '0')).join(''); - const id = [ - hex.slice(0, 8), - hex.slice(8, 12), - hex.slice(12, 16), - hex.slice(16, 20), - hex.slice(20, 32) - ].join('-'); - return { - id, + id: params.id, p: searchParams.p, } } @@ -50,7 +41,7 @@ async function getBlog(paramsResult: ReturnType) { } } else { try { - const data = await BlogAPI.getBlog(`${id}`, p); + const data = await BlogAPI.getBlogBySlug(`${id}`, p); return { data, } @@ -83,7 +74,7 @@ export default async function Page({ params, searchParams }: PageRouteProps) { let { errorMsg } = res; const data = errorMsg ? null - : await BlogAPI.getBlog(`${id}`, p).catch(e => handleAPIError(e, ({ message }) => { errorMsg = message; return null })); + : await BlogAPI.getBlogBySlug(`${id}`, p).catch(e => handleAPIError(e, ({ message }) => { errorMsg = message; return null })); return (
diff --git a/apps/frontend/app/(with-header-footer)/blog/page.tsx b/apps/frontend/app/(with-header-footer)/blog/page.tsx index 566cefc..b82f870 100644 --- a/apps/frontend/app/(with-header-footer)/blog/page.tsx +++ b/apps/frontend/app/(with-header-footer)/blog/page.tsx @@ -4,7 +4,6 @@ import { AlertTitle, } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; -import { base62 } from "@/lib/utils"; import { BlogAPI } from "@/lib/api/server"; import { handleAPIError } from "@/lib/api/common"; @@ -18,13 +17,10 @@ const formatNumber = (num: number): string => { return num.toString(); }; -const getBlogDetailUrl = (id: string): string => { - const cleanId = id.replace(/-/g, ''); - const encoded = base62.encode(Buffer.from(cleanId, 'hex')); - return `/blog/${encoded}`; +const getBlogDetailUrl = (slug: string): string => { + return `/blog/${slug}`; }; - export const metadata = { title: '日志 - 特恩的日志', description: '我随便发点,你也随便看看~', @@ -56,7 +52,7 @@ export default async function Blog() {

{blog.title} diff --git a/apps/frontend/lib/api/endpoints/blog.server.ts b/apps/frontend/lib/api/endpoints/blog.server.ts index 7b3d8a6..60dab46 100644 --- a/apps/frontend/lib/api/endpoints/blog.server.ts +++ b/apps/frontend/lib/api/endpoints/blog.server.ts @@ -3,16 +3,17 @@ import { serverFetch } from "../server"; export async function list() { return serverFetch[]>('/api/blog') } -export async function getBlog(id: string, password?: string) { +export async function getBlogBySlug(slug: string, password?: string) { return serverFetch<{ id: string; title: string; description: string; createdAt: string; content: string; - }>(`/api/blog/${id}` + (password ? `?p=${password}` : '')); -} \ No newline at end of file + }>(`/api/blog/${slug}/slug` + (password ? `?p=${password}` : '')); +} +