From 5bd43eb835256fd5f77c6c7e1a1c59ced23271a7 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Mon, 20 Apr 2026 20:56:37 +0800 Subject: [PATCH] fix(comment): include context in notify failure logs Pass postId and commentId into reply notification flow and log structured failure context with receiverUserId and reason while keeping best-effort behavior unchanged. Made-with: Cursor --- .../[publicSlug]/posts/[postSlug]/comments.post.ts | 2 ++ .../[publicSlug]/[shareToken]/comments.post.ts | 2 ++ server/service/comment-notify/index.test.ts | 19 +++++++++++++++---- server/service/comment-notify/index.ts | 22 ++++++++++++++++++++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/server/api/public/profile/[publicSlug]/posts/[postSlug]/comments.post.ts b/server/api/public/profile/[publicSlug]/posts/[postSlug]/comments.post.ts index 4f69a2a..f330e12 100644 --- a/server/api/public/profile/[publicSlug]/posts/[postSlug]/comments.post.ts +++ b/server/api/public/profile/[publicSlug]/posts/[postSlug]/comments.post.ts @@ -47,6 +47,8 @@ export default defineEventHandler(async (event) => { }); void notifyReplyCommentCreated({ + postId: ctx.id, + commentId: newCommentId, parentId: parsed.parentId, actorUserId: viewer?.id ?? null, replyBody: parsed.body, diff --git a/server/api/public/unlisted/[publicSlug]/[shareToken]/comments.post.ts b/server/api/public/unlisted/[publicSlug]/[shareToken]/comments.post.ts index 193e566..cb05e3c 100644 --- a/server/api/public/unlisted/[publicSlug]/[shareToken]/comments.post.ts +++ b/server/api/public/unlisted/[publicSlug]/[shareToken]/comments.post.ts @@ -47,6 +47,8 @@ export default defineEventHandler(async (event) => { }); void notifyReplyCommentCreated({ + postId: ctx.id, + commentId: newCommentId, parentId: parsed.parentId, actorUserId: viewer?.id ?? null, replyBody: parsed.body, diff --git a/server/service/comment-notify/index.test.ts b/server/service/comment-notify/index.test.ts index 4c46c9b..6f8f28a 100644 --- a/server/service/comment-notify/index.test.ts +++ b/server/service/comment-notify/index.test.ts @@ -45,7 +45,7 @@ describe("notifyReplyCommentCreated", () => { smtpPass: "smtp-pass", }); - await notifyReplyCommentCreated({ parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); + await notifyReplyCommentCreated({ postId: 10, commentId: 200, parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); expect(state.sendMailCalled).toBe(false); }); @@ -54,7 +54,7 @@ describe("notifyReplyCommentCreated", () => { const { deps, state } = createDeps(); deps.getReceiverNotifyEnabled = async () => false; - await notifyReplyCommentCreated({ parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); + await notifyReplyCommentCreated({ postId: 10, commentId: 201, parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); expect(state.sendMailCalled).toBe(false); }); @@ -62,7 +62,7 @@ describe("notifyReplyCommentCreated", () => { test("无 parentId -> 不发送", async () => { const { deps, state } = createDeps(); - await notifyReplyCommentCreated({ parentId: null, actorUserId: 1, replyBody: "hello" }, deps); + await notifyReplyCommentCreated({ postId: 10, commentId: 202, parentId: null, actorUserId: 1, replyBody: "hello" }, deps); expect(state.sendMailCalled).toBe(false); }); @@ -71,8 +71,19 @@ describe("notifyReplyCommentCreated", () => { const { deps, state } = createDeps(); deps.getParentAuthorUserId = async () => 1; - await notifyReplyCommentCreated({ parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); + await notifyReplyCommentCreated({ postId: 10, commentId: 203, parentId: 100, actorUserId: 1, replyBody: "hello" }, deps); expect(state.sendMailCalled).toBe(false); }); + + test("发送异常 -> 不抛出(best-effort)", async () => { + const { deps } = createDeps(); + deps.sendMail = async () => { + throw new Error("smtp send failed"); + }; + + await expect( + notifyReplyCommentCreated({ postId: 10, commentId: 204, parentId: 100, actorUserId: 1, replyBody: "hello" }, deps), + ).resolves.toBeUndefined(); + }); }); diff --git a/server/service/comment-notify/index.ts b/server/service/comment-notify/index.ts index 44d8e44..63b98fb 100644 --- a/server/service/comment-notify/index.ts +++ b/server/service/comment-notify/index.ts @@ -31,6 +31,16 @@ function hasValue(value: string): boolean { return value.trim().length > 0; } +function getReason(error: unknown): string { + if (error instanceof Error && hasValue(error.message)) { + return error.message; + } + if (typeof error === "string" && hasValue(error)) { + return error; + } + return "unknown"; +} + function isSmtpConfigReady(config: NotifyGlobalConfig): boolean { return ( config.enabled && @@ -138,18 +148,21 @@ function getDefaultDeps(): NotifyDeps { export async function notifyReplyCommentCreated( input: { + postId: number; + commentId: number; parentId: number | null; actorUserId: number | null; replyBody: string; }, deps: NotifyDeps = getDefaultDeps(), ): Promise { + let receiverUserId: number | null = null; try { if (input.parentId == null) { return; } - const receiverUserId = await deps.getParentAuthorUserId(input.parentId); + receiverUserId = await deps.getParentAuthorUserId(input.parentId); if (receiverUserId == null) { return; } @@ -179,6 +192,11 @@ export async function notifyReplyCommentCreated( replyBody: input.replyBody, }); } catch (error) { - logger.warn("failed to send reply comment notify email", error); + logger.warn("failed to send reply comment notify email", { + postId: input.postId, + commentId: input.commentId, + receiverUserId, + reason: getReason(error), + }); } }