refactor(street): 审核页改为统一网格+勾选成组;移除并入上一张
This commit is contained in:
@ -96,7 +96,6 @@ func NewBackstage(opt BackstageOptions) *gin.Engine {
|
||||
admin.POST("/reviews/:kind/:id/images/:img/delete", opt.Backstage.ReviewDraftImageDelete)
|
||||
// 街拍主副图分组操作(零 JS:同步表单 POST,成功 302 回详情页)。
|
||||
admin.POST("/reviews/:kind/:id/images/attach", opt.Backstage.ReviewAttachDraftImages)
|
||||
admin.POST("/reviews/:kind/:id/images/:img/attach-prev", opt.Backstage.ReviewAttachPrevDraftImage)
|
||||
admin.POST("/reviews/:kind/:id/images/:img/detach", opt.Backstage.ReviewDetachDraftImage)
|
||||
// 爬虫入库任务监控:列出处理进度 + 失败重试
|
||||
admin.GET("/ingest-jobs", opt.Backstage.IngestJobs)
|
||||
|
||||
@ -324,13 +324,6 @@ func (f *fakeReview) AttachDraftImages(_ context.Context, kind string, draftID,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReview) AttachPrevDraftImage(_ context.Context, kind string, draftID, imgID uint32) error {
|
||||
f.last.kind, f.last.draftID, f.last.imgID, f.last.op = kind, draftID, imgID, "attach-prev"
|
||||
if !f.attachOK {
|
||||
return errors.New("主图无效")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReview) DetachDraftImage(_ context.Context, kind string, draftID, imgID uint32) error {
|
||||
f.last.kind, f.last.draftID, f.last.imgID, f.last.op = kind, draftID, imgID, "detach"
|
||||
if !f.attachOK {
|
||||
@ -363,7 +356,7 @@ func reviewToken(t *testing.T, r *gin.Engine) *http.Cookie {
|
||||
return cookies[0]
|
||||
}
|
||||
|
||||
// TestBackstageStreetAttachRedirect 批量并入应 302 跳回带 ?main= 的详情页,且参数透传到 service。
|
||||
// TestBackstageStreetAttachRedirect 合并选中应 302 跳回详情页,且参数透传到 service。
|
||||
func TestBackstageStreetAttachRedirect(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
tok := reviewToken(t, r)
|
||||
@ -374,34 +367,35 @@ func TestBackstageStreetAttachRedirect(t *testing.T) {
|
||||
req.AddCookie(tok)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusFound {
|
||||
t.Fatalf("并入应 302,实际 %d %s", w.Code, w.Body.String())
|
||||
t.Fatalf("合并应 302,实际 %d %s", w.Code, w.Body.String())
|
||||
}
|
||||
if !strings.Contains(w.Header().Get("Location"), "/admin/reviews/street/7?main=1") {
|
||||
t.Fatalf("重定向应带 ?main=1,实际 %s", w.Header().Get("Location"))
|
||||
if !strings.Contains(w.Header().Get("Location"), "/admin/reviews/street/7") {
|
||||
t.Fatalf("应重定向回详情页,实际 %s", w.Header().Get("Location"))
|
||||
}
|
||||
if rev.last.op != "attach" || rev.last.mainID != 1 || len(rev.last.imgIDs) != 2 {
|
||||
t.Fatalf("参数透传错误: %+v", rev.last)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackstageStreetAttachPrev 并入上一张应 302。
|
||||
func TestBackstageStreetAttachPrev(t *testing.T) {
|
||||
// TestBackstageStreetAttachNeedsMain 未选主图时应 400,而不是静默失败。
|
||||
func TestBackstageStreetAttachNeedsMain(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
tok := reviewToken(t, r)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/admin/reviews/street/7/images/2/attach-prev", nil)
|
||||
req := httptest.NewRequest(http.MethodPost, "/admin/reviews/street/7/images/attach", strings.NewReader("img=2&img=3"))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.AddCookie(tok)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusFound {
|
||||
t.Fatalf("并入上一张应 302,实际 %d", w.Code)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("未选主图应 400,实际 %d", w.Code)
|
||||
}
|
||||
if rev.last.op != "attach-prev" || rev.last.imgID != 2 {
|
||||
t.Fatalf("参数透传错误: %+v", rev.last)
|
||||
if rev.last.op == "attach" {
|
||||
t.Fatalf("未选主图不应调用 service")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackstageStreetDetach 拆出应 302 跳回「以该图为主图」的详情页。
|
||||
// TestBackstageStreetDetach 拆出应 302 跳回详情页。
|
||||
func TestBackstageStreetDetach(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
tok := reviewToken(t, r)
|
||||
@ -413,22 +407,22 @@ func TestBackstageStreetDetach(t *testing.T) {
|
||||
if w.Code != http.StatusFound {
|
||||
t.Fatalf("拆出应 302,实际 %d", w.Code)
|
||||
}
|
||||
if !strings.Contains(w.Header().Get("Location"), "/admin/reviews/street/7?main=2") {
|
||||
t.Fatalf("拆出后应以该图为主图跳转,实际 %s", w.Header().Get("Location"))
|
||||
if !strings.Contains(w.Header().Get("Location"), "/admin/reviews/street/7") {
|
||||
t.Fatalf("应跳回详情页,实际 %s", w.Header().Get("Location"))
|
||||
}
|
||||
if rev.last.op != "detach" || rev.last.imgID != 2 {
|
||||
t.Fatalf("参数透传错误: %+v", rev.last)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackstageStreetRenderGroups 详情页应按组折叠渲染(strip-group + 副图名)。
|
||||
// TestBackstageStreetRenderGroups 详情页渲染统一网格:复选框 + 主图单选 + 底部合并按钮,且不再有「并入上一张」。
|
||||
func TestBackstageStreetRenderGroups(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
tok := reviewToken(t, r)
|
||||
rev.view = &service.DraftDetailView{
|
||||
Kind: "street", ID: 7, Title: "T", Year: 2026, City: "Beijing", ImageCount: 3, Status: "pending",
|
||||
Images: []service.DraftImageRef{
|
||||
{ID: 1, Image: "main.jpg", Name: "main", IsDetail: 0},
|
||||
{ID: 1, Image: "main.jpg", Name: "main", IsDetail: 0, DetailCount: 1},
|
||||
{ID: 2, Image: "det.jpg", Name: "detail-a", IsDetail: 1, ParentImageID: 1},
|
||||
{ID: 3, Image: "solo.jpg", Name: "solo", IsDetail: 0},
|
||||
},
|
||||
@ -439,36 +433,32 @@ func TestBackstageStreetRenderGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin/reviews/street/7?main=1", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin/reviews/street/7", nil)
|
||||
req.AddCookie(tok)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("详情应 200,实际 %d %s", w.Code, w.Body.String())
|
||||
}
|
||||
body := w.Body.String()
|
||||
if !strings.Contains(body, "strip-group") {
|
||||
t.Fatalf("应按组折叠渲染 strip-group")
|
||||
}
|
||||
if !strings.Contains(body, "detail-a") {
|
||||
t.Fatalf("应渲染副图 detail-a")
|
||||
}
|
||||
// 「设为主图」必须是带 ?main= 的 GET 链接(且带 #i<id> 锚点,选完不跳回顶部)。
|
||||
// (曾被误写成 POST 到 /admin/reviews/street/7,而该路由只有 GET,导致 404、主图根本选不了。)
|
||||
if !strings.Contains(body, `href="/admin/reviews/street/7?main=1#i1"`) {
|
||||
t.Fatalf("「设为主图」应是带 ?main= 与 #i<id> 锚点的 GET 链接")
|
||||
// 副图徽标显示所属主图(沿链归并后的实际主图)。
|
||||
if !strings.Contains(body, "副图 → #1") {
|
||||
t.Fatalf("副图应显示所属主图")
|
||||
}
|
||||
if strings.Contains(body, `action="/admin/reviews/street/7"`) {
|
||||
t.Fatalf("不应存在 POST 到详情页的表单(该路由只有 GET)")
|
||||
}
|
||||
// 批量并入:勾选框 + 独立提交按钮,用 form="batch" 关联(零 JS、不嵌套表单)。
|
||||
// 统一网格 + 勾选 + 主图单选 + 底部提交按钮,全部用 form="batch" 关联(零 JS、不嵌套表单)。
|
||||
if !strings.Contains(body, `id="batch"`) || !strings.Contains(body, `form="batch"`) {
|
||||
t.Fatalf("应有勾选批量并入当前主图的表单")
|
||||
t.Fatalf("应有勾选合并的表单")
|
||||
}
|
||||
// 「并入上一张」必须出现在每一张图(除第一张)上,否则「连拍同一个人」无法快捷归并。
|
||||
if !strings.Contains(body, `/admin/reviews/street/7/images/3/attach-prev`) {
|
||||
t.Fatalf("非首张图应有「并入上一张」")
|
||||
if !strings.Contains(body, `name="main"`) {
|
||||
t.Fatalf("应有主图单选(name=main)")
|
||||
}
|
||||
if strings.Contains(body, `/admin/reviews/street/7/images/1/attach-prev`) {
|
||||
t.Fatalf("首张图不应有「并入上一张」(没有上一张)")
|
||||
if !strings.Contains(body, "合并选中为一组") {
|
||||
t.Fatalf("底部应有「合并选中为一组」按钮")
|
||||
}
|
||||
// 「并入上一张」已废弃:不应再出现在页面或表单里。
|
||||
if strings.Contains(body, "并入上一张") || strings.Contains(body, "attach-prev") {
|
||||
t.Fatalf("不应再出现「并入上一张」")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user