From ced4c1635c4fbf47ddb1ec0d5f1bbaf2b9aed316 Mon Sep 17 00:00:00 2001 From: tamakyi Date: Thu, 29 Oct 2026 15:53:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(question):=20=E5=9B=BE=E7=89=87=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=A4=B1=E8=B4=A5=E6=97=B6=E5=9B=9E=E6=BB=9A=E5=B7=B2?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=9A=84=E6=8F=90=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Questions.Create 在图片处理之前就提交了,失败分支只渲染错误页、不回滚。于是: 箱主收到一条无图提问,提问者只看到「图片过大 / 上传失败」以为没发出去;再想重发 又会被 DuplicateGuard 按「同 IP + 同内容 5 分钟内重复」拦住 —— 既没发成功也重发不了。 现在失败分支先 Questions.DeleteByID 回滚(连带清理追问与图片关联,并回退前面 那次提问配额 +1),再做提示。顺带把不需要的 else 去掉(两个分支都以 return 结束)。 验证:6MB 图提交后提问数增量 0、该行 deleted_at 非空(回滚走的是软删); 无图对照增量 1。两条路径均返回 200,正常流程未受影响。 --- route/question/page.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/route/question/page.go b/route/question/page.go index fda8d40..728e951 100644 --- a/route/question/page.go +++ b/route/question/page.go @@ -685,16 +685,25 @@ func New(ctx context.Context, f form.NewQuestion, pageUser *db.User, recaptcha r QuestionID: question.ID, UploaderUserID: askerUserID, }); err != nil { + // 问题在上面已经落库(db.Questions.Create),图片这一步失败必须回滚。 + // 不回滚的后果不是「少一张图」:箱主会收到一条无图提问,而提问者只看到 + // 「图片过大 / 上传失败」,以为没发出去;再想重发又会被 DuplicateGuard + // 按「同 IP + 同内容 5 分钟内重复」拦住 —— 既没发成功也重发不了。 + // DeleteByID 会连带清理追问与图片关联,并回退前面那次提问配额(+1)。 + if delErr := db.Questions.DeleteByID(ctx.Request().Context(), question.ID); delErr != nil { + logrus.WithContext(ctx.Request().Context()).WithError(delErr).Errorf( + "图片上传失败后回滚提问 %d 失败,已留下一条无图提问", question.ID) + } + if errors.Is(err, ErrUploadImageSizeTooLarge) || errors.Is(err, imagecheck.ErrImageTooLarge) { ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errImageTooLarge")), f) ctx.Success("question/list") return - } else { - logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to upload image") - ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errUploadImageFailed")), f) - ctx.Success("question/list") - return } + logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to upload image") + ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errUploadImageFailed")), f) + ctx.Success("question/list") + return } }