package service import ( "encoding/json" "errors" "fmt" "strings" "time" "cmautobuy/admin/model" "cmautobuy/admin/repository" ) type CatalogHistoryResult struct { Rows []CatalogRunView Sources []string Source string Status string Page, Total, TotalPages int } type CatalogRunView struct { model.CatalogImportRun StatusText, CreatedText, FinishedText, DurationText string ErrorCode, ErrorDetails string Retryable bool } func ParseCatalogStatus(raw string) model.CatalogImportStatus { switch model.CatalogImportStatus(strings.TrimSpace(raw)) { case model.CatalogImportProcessing, model.CatalogImportSucceeded, model.CatalogImportFailed: return model.CatalogImportStatus(raw) } return "" } func ListCatalogHistory(q repository.Execer, source, statusRaw string, page int) (CatalogHistoryResult, error) { return ListCatalogHistoryWithPageSize(q, source, statusRaw, page, DefaultPageSize) } // ListCatalogHistoryWithPageSize 按白名单页容量返回商品目录导入记录。 func ListCatalogHistoryWithPageSize(q repository.Execer, source, statusRaw string, page, pageSize int) (CatalogHistoryResult, error) { pageSize = NormalizePageSize(pageSize) status := ParseCatalogStatus(statusRaw) totalPages := 1 _, total, err := repository.ListCatalogImportRuns(q, strings.TrimSpace(source), status, 1, 0) if err != nil { return CatalogHistoryResult{}, err } totalPages = TotalPages(total, pageSize) page = ClampPage(page, totalPages) runs, _, err := repository.ListCatalogImportRuns(q, strings.TrimSpace(source), status, pageSize, (page-1)*pageSize) if err != nil { return CatalogHistoryResult{}, err } sources, err := repository.ListCatalogImportSources(q) if err != nil { return CatalogHistoryResult{}, err } result := CatalogHistoryResult{Sources: sources, Source: strings.TrimSpace(source), Status: string(status), Page: page, Total: total, TotalPages: totalPages} for _, run := range runs { result.Rows = append(result.Rows, newCatalogRunView(run)) } return result, nil } func GetCatalogRunView(q repository.Execer, source, batchID string) (*CatalogRunView, error) { run, err := repository.GetCatalogImportRun(q, strings.TrimSpace(source), strings.TrimSpace(batchID)) if errors.Is(err, repository.ErrCatalogImportRunNotFound) { return nil, nil } if err != nil { return nil, err } view := newCatalogRunView(run) return &view, nil } func newCatalogRunView(run model.CatalogImportRun) CatalogRunView { v := CatalogRunView{CatalogImportRun: run, StatusText: map[model.CatalogImportStatus]string{model.CatalogImportProcessing: "处理中", model.CatalogImportSucceeded: "成功", model.CatalogImportFailed: "失败"}[run.Status], CreatedText: formatLocalTime(run.CreatedAt), FinishedText: formatLocalTime(run.FinishedAt), DurationText: "—"} start, e1 := time.Parse(model.TimeLayout, run.CreatedAt) end, e2 := time.Parse(model.TimeLayout, run.FinishedAt) if e1 == nil && e2 == nil { v.DurationText = fmt.Sprintf("%.1f 秒", end.Sub(start).Seconds()) } if run.Status == model.CatalogImportFailed && run.ResponseBody != "" { var stored CatalogError if json.Unmarshal([]byte(run.ResponseBody), &stored) == nil { v.ErrorCode, v.Retryable = stored.Code, stored.Retryable if detail, err := json.Marshal(stored.Details); err == nil && string(detail) != "null" { v.ErrorDetails = truncateCatalogError(string(detail)) } } } return v } type CatalogStatusOption struct{ Value, Text string } func CatalogStatusOptions() []CatalogStatusOption { return []CatalogStatusOption{{Value: "", Text: "全部状态"}, {Value: "processing", Text: "处理中"}, {Value: "succeeded", Text: "成功"}, {Value: "failed", Text: "失败"}} }