feat: 增加商品目录导入记录 (#134)
This commit is contained in:
@@ -2,6 +2,7 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"cmautobuy/admin/config"
|
||||
"cmautobuy/admin/model"
|
||||
"cmautobuy/admin/repository"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestRequireCatalogToken(t *testing.T) {
|
||||
@@ -47,6 +51,35 @@ func TestRequireCatalogToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogBatchAPI_按批次查询摘要(t *testing.T) {
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
_, err = db.Exec(`CREATE TABLE catalog_import_runs(source TEXT,batch_id TEXT,request_hash TEXT,status TEXT,request_count INTEGER,conflict_count INTEGER,observed_at TEXT,last_request_at TEXT,last_conflict_at TEXT,shopee_created INTEGER DEFAULT 0,shopee_updated INTEGER DEFAULT 0,sku_created INTEGER DEFAULT 0,sku_updated INTEGER DEFAULT 0,pdd_created INTEGER DEFAULT 0,pdd_updated INTEGER DEFAULT 0,association_created INTEGER DEFAULT 0,association_unchanged INTEGER DEFAULT 0,failure_count INTEGER DEFAULT 0,error_summary TEXT,response_body TEXT,created_at TEXT,finished_at TEXT,PRIMARY KEY(source,batch_id))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
run := model.CatalogImportRun{Source: "script", BatchID: "B-1", RequestHash: "abc", Status: model.CatalogImportSucceeded, LastRequestAt: model.NowISO(), CreatedAt: model.NowISO()}
|
||||
if err := repository.InsertCatalogImportRun(db, run); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const token = "0123456789abcdef0123456789abcdef"
|
||||
r := gin.New()
|
||||
Register(r, db, config.CatalogIntegrationConfig{Source: "script", Token: token})
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/integrations/catalog/batches/B-1", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp := httptest.NewRecorder()
|
||||
r.ServeHTTP(resp, req)
|
||||
if resp.Code != 200 || !strings.Contains(resp.Body.String(), `"batch_id":"B-1"`) {
|
||||
t.Fatalf("status=%d body=%s", resp.Code, resp.Body.String())
|
||||
}
|
||||
if strings.Contains(resp.Body.String(), "request_hash") {
|
||||
t.Fatal("查询响应不应暴露请求哈希")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogBatchAPI_鉴权JSON与体积限制(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
const token = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"cmautobuy/admin/config"
|
||||
"cmautobuy/admin/repository"
|
||||
"cmautobuy/admin/service"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,20 @@ func Register(r *gin.Engine, db *sql.DB, cfg config.CatalogIntegrationConfig) {
|
||||
h := &Handler{db: db}
|
||||
g := r.Group("/api/v1/integrations/catalog", RequireCatalogToken(cfg))
|
||||
g.POST("/batches", h.CreateBatch)
|
||||
g.GET("/batches/:batch_id", h.GetBatch)
|
||||
}
|
||||
|
||||
func (h *Handler) GetBatch(c *gin.Context) {
|
||||
run, err := repository.GetCatalogImportRun(h.db, integrationSource(c), c.Param("batch_id"))
|
||||
if errors.Is(err, repository.ErrCatalogImportRunNotFound) {
|
||||
integrationError(c, 404, "BATCH_NOT_FOUND", "批次不存在", false, nil)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
integrationError(c, 500, "INTERNAL_ERROR", "查询批次失败", true, nil)
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"source": run.Source, "batch_id": run.BatchID, "status": run.Status, "request_count": run.RequestCount, "conflict_count": run.ConflictCount, "observed_at": run.ObservedAt, "created_at": run.CreatedAt, "finished_at": run.FinishedAt, "counts": gin.H{"shopee_created": run.ShopeeCreated, "shopee_updated": run.ShopeeUpdated, "sku_created": run.SKUCreated, "sku_updated": run.SKUUpdated, "pdd_created": run.PddCreated, "pdd_updated": run.PddUpdated, "association_created": run.AssociationCreated, "association_unchanged": run.AssociationUnchanged}, "failure_count": run.FailureCount, "error_summary": run.ErrorSummary})
|
||||
}
|
||||
|
||||
func (h *Handler) CreateBatch(c *gin.Context) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"cmautobuy/admin/service"
|
||||
)
|
||||
|
||||
func (h *Handler) CatalogHistory(c *gin.Context) {
|
||||
result, err := service.ListCatalogHistory(h.db, c.Query("source"), c.Query("status"), service.ParsePage(c.Query("page")))
|
||||
if err != nil {
|
||||
fail(c, 500, "读取商品目录导入记录失败,数据没有被改动。请刷新后重试。")
|
||||
return
|
||||
}
|
||||
values := url.Values{}
|
||||
if result.Source != "" {
|
||||
values.Set("source", result.Source)
|
||||
}
|
||||
if result.Status != "" {
|
||||
values.Set("status", result.Status)
|
||||
}
|
||||
c.HTML(http.StatusOK, "catalog/list", page(c, "catalog", "商品目录导入记录", gin.H{"Rows": result.Rows, "Sources": result.Sources, "Source": result.Source, "StatusFilter": result.Status, "StatusOptions": service.CatalogStatusOptions(), "Status": fmt.Sprintf("共 %d 条 · 第 %d/%d 页", result.Total, result.Page, result.TotalPages), "Pagination": service.NewPaginationView(result.Page, result.TotalPages, values.Encode())}))
|
||||
}
|
||||
|
||||
func (h *Handler) CatalogHistoryDetail(c *gin.Context) {
|
||||
view, err := service.GetCatalogRunView(h.db, c.Query("source"), c.Query("batch_id"))
|
||||
if err != nil {
|
||||
fail(c, 500, "读取导入批次详情失败。")
|
||||
return
|
||||
}
|
||||
if view == nil {
|
||||
fail(c, 404, "导入批次不存在,请刷新页面。")
|
||||
return
|
||||
}
|
||||
c.HTML(200, "catalog/detail_modal", gin.H{"Run": view})
|
||||
}
|
||||
@@ -102,6 +102,11 @@ func Register(r *gin.Engine, db *sql.DB, onlineThreshold time.Duration) {
|
||||
users.POST("/create", h.UserCreate)
|
||||
users.POST("/status", h.UserSetStatus)
|
||||
users.POST("/reset-password", h.UserResetPassword)
|
||||
|
||||
// 7. 第三方商品目录导入记录:系统级审计仅管理员可见。
|
||||
integrations := pages.Group("/integrations", AdminRequired())
|
||||
integrations.GET("/catalog", h.CatalogHistory)
|
||||
integrations.GET("/catalog/detail", h.CatalogHistoryDetail)
|
||||
}
|
||||
|
||||
// page 组装每个页面都要的公共数据(导航高亮、标题、CSRF token)。
|
||||
|
||||
Reference in New Issue
Block a user