update
This commit is contained in:
@ -89,11 +89,10 @@ func NewBackstage(opt BackstageOptions) *gin.Engine {
|
||||
admin.POST("/brands", opt.Backstage.BrandCreate)
|
||||
admin.GET("/brands/:id", opt.Backstage.BrandEdit)
|
||||
admin.POST("/brands/:id", opt.Backstage.BrandUpdate)
|
||||
// 文章管理:走秀 + 街拍混合列表,替代原「审核页 + 分类列表」,是编辑/通过/驳回/下架的统一入口。
|
||||
// 文章管理:走秀 + 街拍混合列表,替代原「审核页 + 分类列表」,是编辑/通过/删除(下架)的统一入口。
|
||||
admin.GET("/articles", opt.Backstage.Articles)
|
||||
admin.POST("/articles/batch", opt.Backstage.ArticleBatch)
|
||||
admin.POST("/articles/:kind/:id/approve", opt.Backstage.ArticleApprove)
|
||||
admin.POST("/articles/:kind/:id/reject", opt.Backstage.ArticleReject)
|
||||
// 街拍主副图分组(零 JS:同步表单 POST,成功 302 回编辑页;AJAX 版见路由上方)。
|
||||
admin.POST("/articles/:kind/:id/images/attach", opt.Backstage.ArticleAttachImages)
|
||||
admin.POST("/articles/:kind/:id/images/:img/detach", opt.Backstage.ArticleDetachImage)
|
||||
|
||||
@ -147,6 +147,9 @@ func (f *fakeArticle) SetDeleted(ctx context.Context, id uint32, deleted uint8)
|
||||
func (f *fakeArticle) PurgeGallery(ctx context.Context, id uint32) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeArticle) GetRunwayDupSources(ctx context.Context, imageIDs []uint32) (map[uint32]dto.DupSource, error) {
|
||||
return map[uint32]dto.DupSource{}, nil
|
||||
}
|
||||
|
||||
type fakeStreet struct{}
|
||||
|
||||
@ -204,6 +207,9 @@ func (f *fakeStreet) SetDeleted(ctx context.Context, id uint32, deleted uint8) e
|
||||
func (f *fakeStreet) PurgeGallery(ctx context.Context, id uint32) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeStreet) GetStreetSnapDupSources(ctx context.Context, imageIDs []uint32) (map[uint32]dto.DupSource, error) {
|
||||
return map[uint32]dto.DupSource{}, nil
|
||||
}
|
||||
|
||||
// fakeAdminRepo 实现文章管理仓储接口;返回固定的混合列表示例,供路由/模板渲染测试。
|
||||
type fakeAdminRepo struct{}
|
||||
@ -254,6 +260,9 @@ func (f *fakeAdminRepo) CountMixed(_ context.Context, status, kind string) (int6
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
func (f *fakeAdminRepo) DupRateByRecords(_ context.Context, kind string, ids []uint32) (map[uint32]repository.DupStat, error) {
|
||||
return map[uint32]repository.DupStat{}, nil
|
||||
}
|
||||
|
||||
// ── 冒烟测试:验证路由 / 鉴权 / 模板渲染(无需真实数据库)─────────────────
|
||||
|
||||
@ -393,7 +402,6 @@ type fakeReview struct {
|
||||
service.ReviewService
|
||||
attachOK bool
|
||||
approveErr error
|
||||
rejectErr error
|
||||
last struct {
|
||||
kind string
|
||||
recordID uint32
|
||||
@ -407,7 +415,6 @@ type fakeReview struct {
|
||||
Action string
|
||||
Kind string
|
||||
ID uint32
|
||||
Reason string
|
||||
}
|
||||
view *service.RecordDetailView
|
||||
}
|
||||
@ -456,8 +463,7 @@ func (f *fakeReview) Approve(_ context.Context, kind string, id uint32, reviewer
|
||||
Action string
|
||||
Kind string
|
||||
ID uint32
|
||||
Reason string
|
||||
}{"approve", kind, id, ""})
|
||||
}{"approve", kind, id})
|
||||
// last 供「表单式通过」用例断言(批量接口用 calls)。
|
||||
f.last.kind, f.last.recordID, f.last.op = kind, id, "approve"
|
||||
if f.approveErr != nil {
|
||||
@ -466,16 +472,6 @@ func (f *fakeReview) Approve(_ context.Context, kind string, id uint32, reviewer
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (f *fakeReview) Reject(_ context.Context, kind string, id uint32, reviewer, reason string) error {
|
||||
f.calls = append(f.calls, struct {
|
||||
Action string
|
||||
Kind string
|
||||
ID uint32
|
||||
Reason string
|
||||
}{"reject", kind, id, reason})
|
||||
return f.rejectErr
|
||||
}
|
||||
|
||||
func newReviewRouter(t *testing.T) (*gin.Engine, *fakeReview) {
|
||||
t.Helper()
|
||||
m := jwt.NewManager("test-secret", 168)
|
||||
@ -697,26 +693,6 @@ func TestBackstageReviewBatchApprove(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackstageReviewBatchRejectNeedsReason 驳回必须带理由(留痕要求),否则 400 且不调用 service。
|
||||
func TestBackstageReviewBatchRejectNeedsReason(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
tok := reviewToken(t, r)
|
||||
|
||||
body := `{"action":"reject","items":[{"kind":"street","id":11}]}`
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/admin/api/articles/batch", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.AddCookie(tok)
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != 400 {
|
||||
t.Fatalf("缺理由应 400: %d %s", w.Code, w.Body.String())
|
||||
}
|
||||
if len(rev.calls) != 0 {
|
||||
t.Fatalf("缺理由不应调用 service: %+v", rev.calls)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBackstageReviewBatchPartialFailure 未注册类型只计失败,其余照常处理(不整体回滚)。
|
||||
func TestBackstageReviewBatchPartialFailure(t *testing.T) {
|
||||
r, rev := newReviewRouter(t)
|
||||
|
||||
Reference in New Issue
Block a user