fix(question): 图片上传失败时回滚已创建的提问

Questions.Create 在图片处理之前就提交了,失败分支只渲染错误页、不回滚。于是:
箱主收到一条无图提问,提问者只看到「图片过大 / 上传失败」以为没发出去;再想重发
又会被 DuplicateGuard 按「同 IP + 同内容 5 分钟内重复」拦住 —— 既没发成功也重发不了。

现在失败分支先 Questions.DeleteByID 回滚(连带清理追问与图片关联,并回退前面
那次提问配额 +1),再做提示。顺带把不需要的 else 去掉(两个分支都以 return 结束)。

验证:6MB 图提交后提问数增量 0、该行 deleted_at 非空(回滚走的是软删);
无图对照增量 1。两条路径均返回 200,正常流程未受影响。
This commit is contained in:
tamakyi
2026-10-29 15:53:39 +08:00
parent 11d6e701ee
commit ced4c1635c
+14 -5
View File
@@ -685,16 +685,25 @@ func New(ctx context.Context, f form.NewQuestion, pageUser *db.User, recaptcha r
QuestionID: question.ID, QuestionID: question.ID,
UploaderUserID: askerUserID, UploaderUserID: askerUserID,
}); err != nil { }); 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) { if errors.Is(err, ErrUploadImageSizeTooLarge) || errors.Is(err, imagecheck.ErrImageTooLarge) {
ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errImageTooLarge")), f) ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errImageTooLarge")), f)
ctx.Success("question/list") ctx.Success("question/list")
return 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
} }
} }