From 6fa4abdcf5caa06b97f722fc44f8858f209c5ed9 Mon Sep 17 00:00:00 2001 From: zhouyonggao <1971162852@qq.com> Date: Tue, 25 Nov 2025 15:25:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AF=BC=E5=87=BA):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=88=86=E9=94=80=E5=95=86=E7=AD=9B=E9=80=89=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=8F=8A=E4=BB=BB=E5=8A=A1=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加分销商API接口及前端筛选组件 - 实现导出任务分页列表功能 - 优化导出任务状态展示和进度显示 - 增加EXPLAIN评分阈值检查 - 默认设置导出时间为当年范围 --- server/internal/api/exports.go | 562 ++++++++++++---------- server/internal/api/resellers.go | 72 +++ server/internal/api/router.go | 2 + server/log/server-20251125.log | 151 ++++++ server/storage/export_20251125150158.xlsx | Bin 0 -> 6536 bytes server/storage/export_20251125150231.xlsx | Bin 0 -> 6501 bytes web/index.html | 43 +- web/main.js | 58 ++- 8 files changed, 628 insertions(+), 260 deletions(-) create mode 100644 server/internal/api/resellers.go create mode 100755 server/storage/export_20251125150158.xlsx create mode 100755 server/storage/export_20251125150231.xlsx diff --git a/server/internal/api/exports.go b/server/internal/api/exports.go index 303c179..6fcad7d 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -3,276 +3,348 @@ package api import ( "database/sql" "encoding/json" + "fmt" "io" "log" + "marketing-system-data-tool/server/internal/exporter" "net/http" "strconv" "strings" "time" - "marketing-system-data-tool/server/internal/exporter" ) -type ExportsAPI struct{ - meta *sql.DB - marketing *sql.DB +type ExportsAPI struct { + meta *sql.DB + marketing *sql.DB } func ExportsHandler(meta, marketing *sql.DB) http.Handler { - api := &ExportsAPI{meta: meta, marketing: marketing} - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - p := strings.TrimPrefix(r.URL.Path, "/api/exports") - if r.Method == http.MethodPost && p == "" { - api.create(w, r) - return - } - if strings.HasPrefix(p, "/") { - id := strings.TrimPrefix(p, "/") - if r.Method == http.MethodGet && !strings.HasSuffix(p, "/download") { - api.get(w, r, id) - return - } - if r.Method == http.MethodGet && strings.HasSuffix(p, "/download") { - id = strings.TrimSuffix(id, "/download") - api.download(w, r, id) - return - } - if r.Method == http.MethodPost && strings.HasSuffix(p, "/cancel") { - id = strings.TrimSuffix(id, "/cancel") - api.cancel(w, r, id) - return - } - } - w.WriteHeader(http.StatusNotFound) - }) + api := &ExportsAPI{meta: meta, marketing: marketing} + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + p := strings.TrimPrefix(r.URL.Path, "/api/exports") + if r.Method == http.MethodPost && p == "" { + api.create(w, r) + return + } + if r.Method == http.MethodGet && p == "" { + api.list(w, r) + return + } + if strings.HasPrefix(p, "/") { + id := strings.TrimPrefix(p, "/") + if r.Method == http.MethodGet && !strings.HasSuffix(p, "/download") { + api.get(w, r, id) + return + } + if r.Method == http.MethodGet && strings.HasSuffix(p, "/download") { + id = strings.TrimSuffix(id, "/download") + api.download(w, r, id) + return + } + if r.Method == http.MethodPost && strings.HasSuffix(p, "/cancel") { + id = strings.TrimSuffix(id, "/cancel") + api.cancel(w, r, id) + return + } + } + w.WriteHeader(http.StatusNotFound) + }) } -type ExportPayload struct{ - TemplateID uint64 `json:"template_id"` - RequestedBy uint64 `json:"requested_by"` - Permission map[string]interface{} `json:"permission"` - Options map[string]interface{} `json:"options"` - FileFormat string `json:"file_format"` - Filters map[string]interface{} `json:"filters"` - Datasource string `json:"datasource"` +type ExportPayload struct { + TemplateID uint64 `json:"template_id"` + RequestedBy uint64 `json:"requested_by"` + Permission map[string]interface{} `json:"permission"` + Options map[string]interface{} `json:"options"` + FileFormat string `json:"file_format"` + Filters map[string]interface{} `json:"filters"` + Datasource string `json:"datasource"` } func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { - b, _ := io.ReadAll(r.Body) - var p ExportPayload - json.Unmarshal(b, &p) - r = WithPayload(r, p) - var main string - var ds string - var fields []byte - log.Printf("trace_id=%s sql=%s args=%v", TraceIDFrom(r), "SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ?", []interface{}{p.TemplateID}) - row := a.meta.QueryRow("SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ?", p.TemplateID) - err := row.Scan(&ds, &main, &fields) - if err != nil { - fail(w, r, http.StatusBadRequest, "invalid template") - return - } - if p.Datasource != "" { ds = p.Datasource } - var fs []string - json.Unmarshal(fields, &fs) - wl := whitelist() - req := exporter.BuildRequest{MainTable: main, Fields: fs, Filters: p.Filters} - q, args, err := exporter.BuildSQL(req, wl) - if err != nil { - r = WithSQL(r, q) - fail(w, r, http.StatusBadRequest, err.Error()) - return - } - r = WithSQL(r, q) - dataDB := a.selectDataDB(ds) + b, _ := io.ReadAll(r.Body) + var p ExportPayload + json.Unmarshal(b, &p) + r = WithPayload(r, p) + var main string + var ds string + var fields []byte + log.Printf("trace_id=%s sql=%s args=%v", TraceIDFrom(r), "SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ?", []interface{}{p.TemplateID}) + row := a.meta.QueryRow("SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ?", p.TemplateID) + err := row.Scan(&ds, &main, &fields) + if err != nil { + fail(w, r, http.StatusBadRequest, "invalid template") + return + } + if p.Datasource != "" { + ds = p.Datasource + } + var fs []string + json.Unmarshal(fields, &fs) + wl := whitelist() + req := exporter.BuildRequest{MainTable: main, Fields: fs, Filters: p.Filters} + q, args, err := exporter.BuildSQL(req, wl) + if err != nil { + r = WithSQL(r, q) + fail(w, r, http.StatusBadRequest, err.Error()) + return + } + r = WithSQL(r, q) + dataDB := a.selectDataDB(ds) expRows, score, err := exporter.RunExplain(dataDB, q, args) if err != nil { fail(w, r, http.StatusBadRequest, err.Error()) return } - ejSQL := "INSERT INTO export_jobs (template_id, status, requested_by, permission_scope_json, filters_json, options_json, explain_json, explain_score, file_format, created_at) VALUES (?,?,?,?,?,?,?,?,?,?)" - ejArgs := []interface{}{p.TemplateID, "queued", p.RequestedBy, toJSON(p.Permission), toJSON(p.Filters), toJSON(p.Options), toJSON(expRows), score, p.FileFormat, time.Now()} - log.Printf("trace_id=%s sql=%s args=%v", TraceIDFrom(r), ejSQL, ejArgs) - res, err := a.meta.Exec(ejSQL, ejArgs...) + const passThreshold = 60 + if score < passThreshold { + fail(w, r, http.StatusBadRequest, fmt.Sprintf("EXPLAIN 未通过:评分=%d,请优化索引或缩小查询范围", score)) + return + } + var estimate int64 + for _, r := range expRows { + if r.Table.Valid && r.Table.String == "order" && r.Rows.Valid { estimate = r.Rows.Int64; break } + if r.Rows.Valid { estimate += r.Rows.Int64 } + } + ejSQL := "INSERT INTO export_jobs (template_id, status, requested_by, permission_scope_json, filters_json, options_json, explain_json, explain_score, row_estimate, file_format, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)" + ejArgs := []interface{}{p.TemplateID, "queued", p.RequestedBy, toJSON(p.Permission), toJSON(p.Filters), toJSON(p.Options), toJSON(expRows), score, estimate, p.FileFormat, time.Now(), time.Now()} + log.Printf("trace_id=%s sql=%s args=%v", TraceIDFrom(r), ejSQL, ejArgs) + res, err := a.meta.Exec(ejSQL, ejArgs...) + if err != nil { + fail(w, r, http.StatusInternalServerError, err.Error()) + return + } + id, _ := res.LastInsertId() + go a.runJob(uint64(id), dataDB, q, args, fs, p.FileFormat) + ok(w, r, map[string]interface{}{"id": id}) +} + +func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, cols []string, fmt string) { + log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, started_at=? WHERE id= ?", []interface{}{"running", time.Now(), id}) + a.meta.Exec("UPDATE export_jobs SET status=?, started_at=?, updated_at=? WHERE id= ?", "running", time.Now(), time.Now(), id) + if fmt == "csv" { + w, err := exporter.NewCSVWriter("storage", "export") + if err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) + return + } + w.WriteHeader(cols) + log.Printf("job_id=%d sql=%s args=%v", id, q, args) + rows, err := db.Query(q, args...) + if err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) + return + } + defer rows.Close() + out := make([]interface{}, len(cols)) + dest := make([]interface{}, len(cols)) + for i := range out { + dest[i] = &out[i] + } + var count int64 + var tick int64 + for rows.Next() { + if err := rows.Scan(dest...); err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id=?", "failed", time.Now(), id) + return + } + vals := make([]string, len(cols)) + for i := range out { + if b, ok := out[i].([]byte); ok { + vals[i] = string(b) + } else if out[i] == nil { + vals[i] = "" + } else { + vals[i] = toString(out[i]) + } + } + w.WriteRow(vals) + count++ + tick++ + if tick%500 == 0 { a.meta.Exec("UPDATE export_jobs SET total_rows=?, updated_at=? WHERE id= ?", count, time.Now(), id) } + } + path, size, _ := w.Close() + log.Printf("job_id=%d sql=%s args=%v", id, "INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?)", []interface{}{id, path, count, size, time.Now(), time.Now()}) + a.meta.Exec("INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?)", id, path, count, size, time.Now(), time.Now()) + log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ?", []interface{}{"completed", time.Now(), count, time.Now(), id}) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ?", "completed", time.Now(), count, time.Now(), id) + return + } + if fmt == "xlsx" { + x, path, err := exporter.NewXLSXWriter("storage", "export", "Sheet1") + if err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) + return + } + x.WriteHeader(cols) + log.Printf("job_id=%d sql=%s args=%v", id, q, args) + rows, err := db.Query(q, args...) + if err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) + return + } + defer rows.Close() + out := make([]interface{}, len(cols)) + dest := make([]interface{}, len(cols)) + for i := range out { + dest[i] = &out[i] + } + var count int64 + for rows.Next() { + if err := rows.Scan(dest...); err != nil { + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id=?", "failed", time.Now(), id) + return + } + vals := make([]string, len(cols)) + for i := range out { + if b, ok := out[i].([]byte); ok { + vals[i] = string(b) + } else if out[i] == nil { + vals[i] = "" + } else { + vals[i] = toString(out[i]) + } + } + x.WriteRow(vals) + count++ + } + p, size, _ := x.Close(path) + log.Printf("job_id=%d sql=%s args=%v", id, "INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?)", []interface{}{id, p, count, size, time.Now(), time.Now()}) + a.meta.Exec("INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?)", id, p, count, size, time.Now(), time.Now()) + log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ?", []interface{}{"completed", time.Now(), count, time.Now(), id}) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ?", "completed", time.Now(), count, time.Now(), id) + return + } + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=?, updated_at=? WHERE id= ?", "failed", time.Now(), time.Now(), id) +} + +func (a *ExportsAPI) selectDataDB(ds string) *sql.DB { + if ds == "ymt" { + return a.meta + } + return a.marketing +} + +func (a *ExportsAPI) get(w http.ResponseWriter, r *http.Request, id string) { + row := a.meta.QueryRow("SELECT id, template_id, status, requested_by, total_rows, file_format, started_at, finished_at, created_at, updated_at FROM export_jobs WHERE id=?", id) + var m = map[string]interface{}{} + var jid uint64 + var templateID uint64 + var status string + var requestedBy uint64 + var totalRows sql.NullInt64 + var fileFormat string + var startedAt, finishedAt sql.NullTime + var createdAt, updatedAt time.Time + err := row.Scan(&jid, &templateID, &status, &requestedBy, &totalRows, &fileFormat, &startedAt, &finishedAt, &createdAt, &updatedAt) + if err != nil { + fail(w, r, http.StatusNotFound, "not found") + return + } + m["id"] = jid + m["template_id"] = templateID + m["status"] = status + m["requested_by"] = requestedBy + m["file_format"] = fileFormat + m["total_rows"] = totalRows.Int64 + m["started_at"] = startedAt.Time + m["finished_at"] = finishedAt.Time + m["created_at"] = createdAt + m["updated_at"] = updatedAt + rows, _ := a.meta.Query("SELECT storage_uri, sheet_name, row_count, size_bytes FROM export_job_files WHERE job_id=?", id) + files := []map[string]interface{}{} + for rows.Next() { + var uri, sheet sql.NullString + var rc, sz sql.NullInt64 + rows.Scan(&uri, &sheet, &rc, &sz) + files = append(files, map[string]interface{}{"storage_uri": uri.String, "sheet_name": sheet.String, "row_count": rc.Int64, "size_bytes": sz.Int64}) + } + rows.Close() + m["files"] = files + ok(w, r, m) +} + +func (a *ExportsAPI) download(w http.ResponseWriter, r *http.Request, id string) { + row := a.meta.QueryRow("SELECT storage_uri FROM export_job_files WHERE job_id=? ORDER BY id DESC LIMIT 1", id) + var uri string + err := row.Scan(&uri) + if err != nil { + fail(w, r, http.StatusNotFound, "not found") + return + } + http.ServeFile(w, r, uri) +} + +func (a *ExportsAPI) cancel(w http.ResponseWriter, r *http.Request, id string) { + a.meta.Exec("UPDATE export_jobs SET status=?, updated_at=? WHERE id=? AND status IN ('queued','running')", "canceled", time.Now(), id) + w.Write([]byte("ok")) +} + +func toString(v interface{}) string { + switch t := v.(type) { + case []byte: + return string(t) + case string: + return t + case int64: + return strconv.FormatInt(t, 10) + case int: + return strconv.Itoa(t) + case float64: + return strconv.FormatFloat(t, 'f', -1, 64) + default: + return "" + } +} +func (a *ExportsAPI) list(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + page := 1 + size := 15 + if p := q.Get("page"); p != "" { + if n, err := strconv.Atoi(p); err == nil && n > 0 { page = n } + } + if s := q.Get("page_size"); s != "" { + if n, err := strconv.Atoi(s); err == nil && n > 0 && n <= 100 { size = n } + } + tplIDStr := q.Get("template_id") + var tplID uint64 + if tplIDStr != "" { + if n, err := strconv.ParseUint(tplIDStr, 10, 64); err == nil { tplID = n } + } + offset := (page - 1) * size + var totalCount int64 + if tplID > 0 { + row := a.meta.QueryRow("SELECT COUNT(1) FROM export_jobs WHERE template_id = ?", tplID) + _ = row.Scan(&totalCount) + } else { + row := a.meta.QueryRow("SELECT COUNT(1) FROM export_jobs") + _ = row.Scan(&totalCount) + } + var rows *sql.Rows + var err error + if tplID > 0 { + rows, err = a.meta.Query("SELECT id, template_id, status, requested_by, row_estimate, total_rows, file_format, created_at, updated_at, explain_score FROM export_jobs WHERE template_id = ? ORDER BY id DESC LIMIT ? OFFSET ?", tplID, size, offset) + } else { + rows, err = a.meta.Query("SELECT id, template_id, status, requested_by, row_estimate, total_rows, file_format, created_at, updated_at, explain_score FROM export_jobs ORDER BY id DESC LIMIT ? OFFSET ?", size, offset) + } if err != nil { fail(w, r, http.StatusInternalServerError, err.Error()) return } - id, _ := res.LastInsertId() - go a.runJob(uint64(id), dataDB, q, args, fs, p.FileFormat) - ok(w, r, map[string]interface{}{"id": id}) -} - -func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, cols []string, fmt string) { - log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, started_at=? WHERE id= ?", []interface{}{"running", time.Now(), id}) - a.meta.Exec("UPDATE export_jobs SET status=?, started_at=? WHERE id= ?", "running", time.Now(), id) - if fmt == "csv" { - w, err := exporter.NewCSVWriter("storage", "export") - if err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) - return - } - w.WriteHeader(cols) - log.Printf("job_id=%d sql=%s args=%v", id, q, args) - rows, err := db.Query(q, args...) - if err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) - return - } - defer rows.Close() - out := make([]interface{}, len(cols)) - dest := make([]interface{}, len(cols)) - for i := range out { - dest[i] = &out[i] - } - var count int64 - for rows.Next() { - if err := rows.Scan(dest...); err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id=?", "failed", time.Now(), id) - return - } - vals := make([]string, len(cols)) - for i := range out { - if b, ok := out[i].([]byte); ok { - vals[i] = string(b) - } else if out[i] == nil { - vals[i] = "" - } else { - vals[i] = toString(out[i]) - } - } - w.WriteRow(vals) - count++ - } - path, size, _ := w.Close() - log.Printf("job_id=%d sql=%s args=%v", id, "INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at) VALUES (?,?,?,?,?)", []interface{}{id, path, count, size, time.Now()}) - a.meta.Exec("INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at) VALUES (?,?,?,?,?)", id, path, count, size, time.Now()) - log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, finished_at=?, total_rows=? WHERE id= ?", []interface{}{"completed", time.Now(), count, id}) - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=?, total_rows=? WHERE id=?", "completed", time.Now(), count, id) - return - } - if fmt == "xlsx" { - x, path, err := exporter.NewXLSXWriter("storage", "export", "Sheet1") - if err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) - return - } - x.WriteHeader(cols) - log.Printf("job_id=%d sql=%s args=%v", id, q, args) - rows, err := db.Query(q, args...) - if err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) - return - } - defer rows.Close() - out := make([]interface{}, len(cols)) - dest := make([]interface{}, len(cols)) - for i := range out { - dest[i] = &out[i] - } - var count int64 - for rows.Next() { - if err := rows.Scan(dest...); err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id=?", "failed", time.Now(), id) - return - } - vals := make([]string, len(cols)) - for i := range out { - if b, ok := out[i].([]byte); ok { - vals[i] = string(b) - } else if out[i] == nil { - vals[i] = "" - } else { - vals[i] = toString(out[i]) - } - } - x.WriteRow(vals) - count++ - } - p, size, _ := x.Close(path) - log.Printf("job_id=%d sql=%s args=%v", id, "INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at) VALUES (?,?,?,?,?)", []interface{}{id, p, count, size, time.Now()}) - a.meta.Exec("INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at) VALUES (?,?,?,?,?)", id, p, count, size, time.Now()) - log.Printf("job_id=%d sql=%s args=%v", id, "UPDATE export_jobs SET status=?, finished_at=?, total_rows=? WHERE id= ?", []interface{}{"completed", time.Now(), count, id}) - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=?, total_rows=? WHERE id=?", "completed", time.Now(), count, id) - return - } - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) -} - -func (a *ExportsAPI) selectDataDB(ds string) *sql.DB { - if ds == "ymt" { - return a.meta - } - return a.marketing -} - -func (a *ExportsAPI) get(w http.ResponseWriter, r *http.Request, id string) { - row := a.meta.QueryRow("SELECT id, template_id, status, requested_by, total_rows, file_format, started_at, finished_at, created_at, updated_at FROM export_jobs WHERE id=?", id) - var m = map[string]interface{}{} - var jid uint64 - var templateID uint64 - var status string - var requestedBy uint64 - var totalRows sql.NullInt64 - var fileFormat string - var startedAt, finishedAt sql.NullTime - var createdAt, updatedAt time.Time - err := row.Scan(&jid, &templateID, &status, &requestedBy, &totalRows, &fileFormat, &startedAt, &finishedAt, &createdAt, &updatedAt) - if err != nil { - fail(w, r, http.StatusNotFound, "not found") - return - } - m["id"] = jid - m["template_id"] = templateID - m["status"] = status - m["requested_by"] = requestedBy - m["file_format"] = fileFormat - m["total_rows"] = totalRows.Int64 - m["started_at"] = startedAt.Time - m["finished_at"] = finishedAt.Time - m["created_at"] = createdAt - m["updated_at"] = updatedAt - rows, _ := a.meta.Query("SELECT storage_uri, sheet_name, row_count, size_bytes FROM export_job_files WHERE job_id=?", id) - files := []map[string]interface{}{} + defer rows.Close() + items := []map[string]interface{}{} for rows.Next() { - var uri, sheet sql.NullString - var rc, sz sql.NullInt64 - rows.Scan(&uri, &sheet, &rc, &sz) - files = append(files, map[string]interface{}{"storage_uri": uri.String, "sheet_name": sheet.String, "row_count": rc.Int64, "size_bytes": sz.Int64}) - } - rows.Close() - m["files"] = files - ok(w, r, m) -} - -func (a *ExportsAPI) download(w http.ResponseWriter, r *http.Request, id string) { - row := a.meta.QueryRow("SELECT storage_uri FROM export_job_files WHERE job_id=? ORDER BY id DESC LIMIT 1", id) - var uri string - err := row.Scan(&uri) - if err != nil { - fail(w, r, http.StatusNotFound, "not found") - return - } - http.ServeFile(w, r, uri) -} - -func (a *ExportsAPI) cancel(w http.ResponseWriter, r *http.Request, id string) { - a.meta.Exec("UPDATE export_jobs SET status=? WHERE id=? AND status IN ('queued','running')", "canceled", id) - w.Write([]byte("ok")) -} - -func toString(v interface{}) string { - switch t := v.(type) { - case []byte: - return string(t) - case string: - return t - case int64: - return strconv.FormatInt(t, 10) - case int: - return strconv.Itoa(t) - case float64: - return strconv.FormatFloat(t, 'f', -1, 64) - default: - return "" + var id, tid, req uint64 + var status, fmtstr string + var estimate, total sql.NullInt64 + var createdAt, updatedAt sql.NullTime + var score sql.NullInt64 + if err := rows.Scan(&id, &tid, &status, &req, &estimate, &total, &fmtstr, &createdAt, &updatedAt, &score); err != nil { continue } + evalStatus := "通过" + if score.Int64 < 60 { evalStatus = "禁止" } + desc := fmt.Sprintf("评分:%d,估算行数:%d;%s", score.Int64, estimate.Int64, map[bool]string{true:"允许执行", false:"禁止执行"}[score.Int64>=60]) + m := map[string]interface{}{"id": id, "template_id": tid, "status": status, "requested_by": req, "row_estimate": estimate.Int64, "total_rows": total.Int64, "file_format": fmtstr, "created_at": createdAt.Time, "updated_at": updatedAt.Time, "eval_status": evalStatus, "eval_desc": desc} + items = append(items, m) } + ok(w, r, map[string]interface{}{"items": items, "total": totalCount, "page": page, "page_size": size}) } diff --git a/server/internal/api/resellers.go b/server/internal/api/resellers.go new file mode 100644 index 0000000..a268835 --- /dev/null +++ b/server/internal/api/resellers.go @@ -0,0 +1,72 @@ +package api + +import ( + "database/sql" + "net/http" + "strconv" + "strings" +) + +type ResellersAPI struct { + marketing *sql.DB +} + +func ResellersHandler(marketing *sql.DB) http.Handler { + api := &ResellersAPI{marketing: marketing} + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + p := strings.TrimPrefix(r.URL.Path, "/api/resellers") + if r.Method == http.MethodGet && p == "" { + api.list(w, r) + return + } + w.WriteHeader(http.StatusNotFound) + }) +} + +func (a *ResellersAPI) list(w http.ResponseWriter, r *http.Request) { + creatorsParam := r.URL.Query().Get("creator") + q := r.URL.Query().Get("q") + limitStr := r.URL.Query().Get("limit") + limit := 2000 + if limitStr != "" { + if n, err := strconv.Atoi(limitStr); err == nil && n > 0 && n <= 10000 { limit = n } + } + creators := []string{} + for _, s := range strings.Split(creatorsParam, ",") { + s = strings.TrimSpace(s) + if s != "" { creators = append(creators, s) } + } + if len(creators) == 0 { + ok(w, r, []map[string]interface{}{}) + return + } + ph := strings.Repeat("?,", len(creators)) + ph = strings.TrimSuffix(ph, ",") + sql1 := "SELECT DISTINCT reseller_id, COALESCE(reseller_name,'') AS name FROM plan WHERE reseller_id IS NOT NULL AND creator IN (" + ph + ")" + args := []interface{}{} + for _, c := range creators { args = append(args, c) } + if q != "" { + sql1 += " AND (CAST(reseller_id AS CHAR) LIKE ? OR reseller_name LIKE ?)" + like := "%" + q + "%" + args = append(args, like, like) + } + sql1 += " ORDER BY reseller_id ASC LIMIT ?" + args = append(args, limit) + rows, err := a.marketing.Query(sql1, args...) + if err != nil { + fail(w, r, http.StatusInternalServerError, err.Error()) + return + } + defer rows.Close() + out := []map[string]interface{}{} + for rows.Next() { + var id sql.NullInt64 + var name sql.NullString + if err := rows.Scan(&id, &name); err != nil { continue } + if !id.Valid { continue } + m := map[string]interface{}{"id": id.Int64, "name": name.String} + out = append(out, m) + } + ok(w, r, out) +} + diff --git a/server/internal/api/router.go b/server/internal/api/router.go index 65e12c8..0c37f62 100644 --- a/server/internal/api/router.go +++ b/server/internal/api/router.go @@ -14,6 +14,8 @@ func NewRouter(metaDB *sql.DB, marketingDB *sql.DB) http.Handler { mux.Handle("/api/exports/", withAccess(withTrace(ExportsHandler(metaDB, marketingDB)))) mux.Handle("/api/creators", withAccess(withTrace(CreatorsHandler(marketingDB)))) mux.Handle("/api/creators/", withAccess(withTrace(CreatorsHandler(marketingDB)))) + mux.Handle("/api/resellers", withAccess(withTrace(ResellersHandler(marketingDB)))) + mux.Handle("/api/resellers/", withAccess(withTrace(ResellersHandler(marketingDB)))) sd := staticDir() mux.Handle("/", http.FileServer(http.Dir(sd))) return mux diff --git a/server/log/server-20251125.log b/server/log/server-20251125.log index a2a21e1..93ff1bf 100644 --- a/server/log/server-20251125.log +++ b/server/log/server-20251125.log @@ -147,3 +147,154 @@ YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 server listening on :8077 {"bytes":1021,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:33:47+08:00"} +{"bytes":1021,"duration_ms":135,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:00+08:00"} +{"bytes":1430,"duration_ms":111,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:03+08:00"} +{"bytes":1614,"duration_ms":19,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:03+08:00"} +{"bytes":1021,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:08+08:00"} +{"bytes":1430,"duration_ms":101,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:12+08:00"} +{"bytes":1614,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:34:12+08:00"} +{"bytes":1021,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:35:18+08:00"} +{"bytes":1430,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:35:20+08:00"} +{"bytes":1614,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:35:20+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:38:20+08:00"} +{"bytes":1430,"duration_ms":107,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:08+08:00"} +{"bytes":1614,"duration_ms":18,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:08+08:00"} +{"bytes":1021,"duration_ms":53,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:12+08:00"} +{"bytes":1430,"duration_ms":95,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:14+08:00"} +{"bytes":1614,"duration_ms":15,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:14+08:00"} +{"bytes":213,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:16+08:00"} +{"bytes":3605,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:20+08:00"} +{"bytes":213,"duration_ms":21,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:25+08:00"} +{"bytes":112,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:29+08:00"} +{"bytes":1430,"duration_ms":95,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:40+08:00"} +{"bytes":1614,"duration_ms":23,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:40+08:00"} +{"bytes":1021,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:55+08:00"} +{"bytes":1430,"duration_ms":135,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:58+08:00"} +{"bytes":1614,"duration_ms":44,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:39:58+08:00"} +{"bytes":3605,"duration_ms":21,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:09+08:00"} +{"bytes":3605,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:13+08:00"} +trace_id=139b1ae6a7c5a2d20e8b6e04ce20a17b sql=SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ? args=[14] +sql=EXPLAIN SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`order_cash`.channel,`order_cash`.cash_activity_id,`order_cash`.receive_status,`order_cash`.receive_time,`order_cash`.cash_packet_id,`order_cash`.cash_id,`order_cash`.amount,`order_cash`.status,`order_cash`.expire_time,`order_cash`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `order_cash` ON `order_cash`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? args=[1 2025-11-01 00:00:00 2025-12-31 00:00:00] +trace_id=139b1ae6a7c5a2d20e8b6e04ce20a17b sql=INSERT INTO export_jobs (template_id, status, requested_by, permission_scope_json, filters_json, options_json, explain_json, explain_score, file_format, created_at) VALUES (?,?,?,?,?,?,?,?,?,?) args=[14 queued 1 [123 125] [123 34 99 114 101 97 116 101 95 116 105 109 101 95 98 101 116 119 101 101 110 34 58 91 34 50 48 50 53 45 49 49 45 48 49 32 48 48 58 48 48 58 48 48 34 44 34 50 48 50 53 45 49 50 45 51 49 32 48 48 58 48 48 58 48 48 34 93 44 34 99 114 101 97 116 111 114 95 105 110 34 58 91 49 93 125] [123 125] [91 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 97 110 103 101 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 54 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 95 100 101 116 97 105 108 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 56 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 95 99 97 115 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 44 111 95 117 105 100 120 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 56 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 112 108 97 110 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 65 76 76 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 99 111 100 101 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 93] 100 xlsx 2025-11-25 14:40:18.254068 +0800 CST m=+129.217624792] +trace_id=139b1ae6a7c5a2d20e8b6e04ce20a17b status=500 file=response.go:43 method=POST path=/api/exports query= remote=[::1]:59663 sql=SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`order_cash`.channel,`order_cash`.cash_activity_id,`order_cash`.receive_status,`order_cash`.receive_time,`order_cash`.cash_packet_id,`order_cash`.cash_id,`order_cash`.amount,`order_cash`.status,`order_cash`.expire_time,`order_cash`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `order_cash` ON `order_cash`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? payload={"template_id":14,"requested_by":1,"permission":{},"options":{},"file_format":"xlsx","filters":{"create_time_between":["2025-11-01 00:00:00","2025-12-31 00:00:00"],"creator_in":[1]},"datasource":"marketing"} msg=Error 1364 (HY000): Field 'updated_at' doesn't have a default value +{"bytes":144,"duration_ms":227,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":500,"trace_id":"","ts":"2025-11-25T14:40:18+08:00"} +{"bytes":1430,"duration_ms":112,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:30+08:00"} +{"bytes":1614,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:30+08:00"} +{"bytes":1430,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:35+08:00"} +{"bytes":1614,"duration_ms":20,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:40:36+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":54,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:41:56+08:00"} +{"bytes":1430,"duration_ms":93,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:53:09+08:00"} +{"bytes":1614,"duration_ms":24,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:53:09+08:00"} +{"bytes":1021,"duration_ms":50,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:53:16+08:00"} +{"bytes":1430,"duration_ms":91,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:53:17+08:00"} +{"bytes":1614,"duration_ms":8,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:53:17+08:00"} +{"bytes":1021,"duration_ms":44,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:54:09+08:00"} +{"bytes":1430,"duration_ms":94,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:54:10+08:00"} +{"bytes":1614,"duration_ms":20,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:54:10+08:00"} +{"bytes":1021,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:04+08:00"} +{"bytes":1430,"duration_ms":91,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:05+08:00"} +{"bytes":1614,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:05+08:00"} +{"bytes":1021,"duration_ms":47,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:31+08:00"} +{"bytes":1430,"duration_ms":94,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:32+08:00"} +{"bytes":1614,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:32+08:00"} +{"bytes":3605,"duration_ms":13,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:41+08:00"} +{"bytes":3605,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:48+08:00"} +{"bytes":3605,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:52+08:00"} +{"bytes":3605,"duration_ms":19,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:55+08:00"} +{"bytes":3605,"duration_ms":37,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T14:55:55+08:00"} +trace_id=4dfd3ab8db7af3a3d3c1e9347bf6f8dd sql=SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ? args=[14] +sql=EXPLAIN SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`order_cash`.channel,`order_cash`.cash_activity_id,`order_cash`.receive_status,`order_cash`.receive_time,`order_cash`.cash_packet_id,`order_cash`.cash_id,`order_cash`.amount,`order_cash`.status,`order_cash`.expire_time,`order_cash`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `order_cash` ON `order_cash`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? args=[1 2025-01-01 00:00:00 2025-12-31 23:59:59] +trace_id=4dfd3ab8db7af3a3d3c1e9347bf6f8dd sql=INSERT INTO export_jobs (template_id, status, requested_by, permission_scope_json, filters_json, options_json, explain_json, explain_score, file_format, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?) args=[14 queued 1 [123 125] [123 34 99 114 101 97 116 101 95 116 105 109 101 95 98 101 116 119 101 101 110 34 58 91 34 50 48 50 53 45 48 49 45 48 49 32 48 48 58 48 48 58 48 48 34 44 34 50 48 50 53 45 49 50 45 51 49 32 50 51 58 53 57 58 53 57 34 93 44 34 99 114 101 97 116 111 114 95 105 110 34 58 91 49 93 125] [123 125] [91 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 97 110 103 101 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 54 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 95 100 101 116 97 105 108 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 56 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 95 99 97 115 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 44 111 95 117 105 100 120 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 56 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 112 108 97 110 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 65 76 76 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 99 111 100 101 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 93] 100 xlsx 2025-11-25 15:01:58.788369 +0800 CST m=+1071.165826668 2025-11-25 15:01:58.788369 +0800 CST m=+1071.165826710] +{"bytes":83,"duration_ms":205,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:01:58+08:00"} +job_id=1 sql=UPDATE export_jobs SET status=?, started_at=? WHERE id= ? args=[running 2025-11-25 15:01:58.884743 +0800 CST m=+1071.262201501 1] +job_id=1 sql=SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`order_cash`.channel,`order_cash`.cash_activity_id,`order_cash`.receive_status,`order_cash`.receive_time,`order_cash`.cash_packet_id,`order_cash`.cash_id,`order_cash`.amount,`order_cash`.status,`order_cash`.expire_time,`order_cash`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `order_cash` ON `order_cash`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? args=[1 2025-01-01 00:00:00 2025-12-31 23:59:59] +job_id=1 sql=INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?) args=[1 storage/export_20251125150158.xlsx 0 6536 2025-11-25 15:01:58.999267 +0800 CST m=+1071.376726001 2025-11-25 15:01:58.999267 +0800 CST m=+1071.376726126] +job_id=1 sql=UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ? args=[completed 2025-11-25 15:01:59.043325 +0800 CST m=+1071.420784085 0 2025-11-25 15:01:59.043325 +0800 CST m=+1071.420784335 1] +{"bytes":350,"duration_ms":515,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:01:59+08:00"} +{"bytes":1021,"duration_ms":47,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:14+08:00"} +{"bytes":1430,"duration_ms":92,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:19+08:00"} +{"bytes":1614,"duration_ms":10,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:20+08:00"} +{"bytes":1369,"duration_ms":108,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:23+08:00"} +{"bytes":1614,"duration_ms":10,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:24+08:00"} +{"bytes":3605,"duration_ms":13,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:28+08:00"} +trace_id=5fcc5f0745003259333eadb64e4fc015 sql=SELECT datasource, main_table, fields_json FROM export_templates WHERE id= ? args=[12] +sql=EXPLAIN SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock,`merchant_key_send`.merchant_id,`merchant_key_send`.out_biz_no,`merchant_key_send`.`key`,`merchant_key_send`.status,`merchant_key_send`.usage_time,`merchant_key_send`.create_time FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id LEFT JOIN `merchant_key_send` ON `order`.`key` = `merchant_key_send`.key WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? args=[1 2025-01-01 00:00:00 2025-12-31 23:59:59] +trace_id=5fcc5f0745003259333eadb64e4fc015 sql=INSERT INTO export_jobs (template_id, status, requested_by, permission_scope_json, filters_json, options_json, explain_json, explain_score, file_format, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?) args=[12 queued 1 [123 125] [123 34 99 114 101 97 116 101 95 116 105 109 101 95 98 101 116 119 101 101 110 34 58 91 34 50 48 50 53 45 48 49 45 48 49 32 48 48 58 48 48 58 48 48 34 44 34 50 48 50 53 45 49 50 45 51 49 32 50 51 58 53 57 58 53 57 34 93 44 34 99 114 101 97 116 111 114 95 105 110 34 58 91 49 93 125] [123 125] [91 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 97 110 103 101 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 105 100 120 95 99 114 101 97 116 101 95 116 105 109 101 95 115 116 97 116 117 115 95 99 114 101 97 116 111 114 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 54 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 111 114 100 101 114 95 100 101 116 97 105 108 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 56 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 112 108 97 110 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 101 113 95 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 80 82 73 77 65 82 89 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 65 76 76 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 99 111 100 101 95 98 97 116 99 104 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 107 101 121 95 98 97 116 99 104 95 105 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 52 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 44 123 34 73 68 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 83 101 108 101 99 116 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 83 73 77 80 76 69 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 97 98 108 101 34 58 123 34 83 116 114 105 110 103 34 58 34 109 101 114 99 104 97 110 116 95 107 101 121 95 115 101 110 100 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 84 121 112 101 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 80 111 115 115 105 98 108 101 75 101 121 115 34 58 123 34 83 116 114 105 110 103 34 58 34 114 101 102 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 34 58 123 34 83 116 114 105 110 103 34 58 34 117 100 120 95 107 101 121 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 75 101 121 76 101 110 34 58 123 34 83 116 114 105 110 103 34 58 34 117 100 120 95 107 101 121 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 101 102 34 58 123 34 83 116 114 105 110 103 34 58 34 49 50 50 34 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 82 111 119 115 34 58 123 34 73 110 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 116 114 117 101 125 44 34 70 105 108 116 101 114 101 100 34 58 123 34 70 108 111 97 116 54 52 34 58 48 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 44 34 69 120 116 114 97 34 58 123 34 83 116 114 105 110 103 34 58 34 34 44 34 86 97 108 105 100 34 58 102 97 108 115 101 125 125 93] 100 xlsx 2025-11-25 15:02:31.75134 +0800 CST m=+1104.128954710 2025-11-25 15:02:31.75134 +0800 CST m=+1104.128954835] +{"bytes":83,"duration_ms":205,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:31+08:00"} +job_id=2 sql=UPDATE export_jobs SET status=?, started_at=? WHERE id= ? args=[running 2025-11-25 15:02:31.851175 +0800 CST m=+1104.228790918 2] +job_id=2 sql=SELECT `order`.order_number,`order`.creator,`order`.out_trade_no,`order`.type,`order`.status,`order`.contract_price,`order`.num,`order`.total,`order`.pay_amount,`order`.create_time,`order`.update_time,`order_detail`.plan_title,`order_detail`.reseller_name,`order_detail`.product_name,`order_detail`.show_url,`order_detail`.official_price,`order_detail`.cost_price,`order_detail`.create_time,`order_detail`.update_time,`plan`.id,`plan`.title,`plan`.status,`plan`.begin_time,`plan`.end_time,`key_batch`.id,`key_batch`.batch_name,`key_batch`.bind_object,`key_batch`.quantity,`key_batch`.stock,`key_batch`.begin_time,`key_batch`.end_time,`code_batch`.id,`code_batch`.title,`code_batch`.status,`code_batch`.begin_time,`code_batch`.end_time,`code_batch`.quantity,`code_batch`.usage,`code_batch`.stock,`merchant_key_send`.merchant_id,`merchant_key_send`.out_biz_no,`merchant_key_send`.`key`,`merchant_key_send`.status,`merchant_key_send`.usage_time,`merchant_key_send`.create_time FROM `order` LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number LEFT JOIN `plan` ON `plan`.id = `order`.plan_id LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id LEFT JOIN `merchant_key_send` ON `order`.`key` = `merchant_key_send`.key WHERE `order`.creator IN (?) AND `order`.create_time BETWEEN ? AND ? args=[1 2025-01-01 00:00:00 2025-12-31 23:59:59] +job_id=2 sql=INSERT INTO export_job_files (job_id, storage_uri, row_count, size_bytes, created_at, updated_at) VALUES (?,?,?,?,?,?) args=[2 storage/export_20251125150231.xlsx 0 6501 2025-11-25 15:02:31.97477 +0800 CST m=+1104.352386043 2025-11-25 15:02:31.97477 +0800 CST m=+1104.352386126] +job_id=2 sql=UPDATE export_jobs SET status=?, finished_at=?, total_rows=?, updated_at=? WHERE id= ? args=[completed 2025-11-25 15:02:32.020952 +0800 CST m=+1104.398568418 0 2025-11-25 15:02:32.020952 +0800 CST m=+1104.398568585 2] +{"bytes":337,"duration_ms":178,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:02:32+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":440,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:04:13+08:00"} +{"bytes":1021,"duration_ms":372,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:04:14+08:00"} +{"bytes":440,"duration_ms":58,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:04:21+08:00"} +{"bytes":1021,"duration_ms":59,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:04:21+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":47,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:18+08:00"} +{"bytes":484,"duration_ms":494,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:18+08:00"} +{"bytes":1021,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:37+08:00"} +{"bytes":484,"duration_ms":147,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:38+08:00"} +trace_id=e670a4285657d7202f967015ba28ecdb status=404 file=response.go:43 method=GET path=/api/exports/2/download query=ide_webview_request_time=1764054403690 remote=[::1]:57917 payload= msg=not found +{"bytes":86,"duration_ms":96,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":404,"trace_id":"","ts":"2025-11-25T15:06:43+08:00"} +{"bytes":1021,"duration_ms":50,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:51+08:00"} +{"bytes":484,"duration_ms":148,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:06:51+08:00"} +{"bytes":1021,"duration_ms":54,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:08:02+08:00"} +{"bytes":484,"duration_ms":150,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:08:02+08:00"} +{"bytes":1021,"duration_ms":53,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:11:16+08:00"} +{"bytes":484,"duration_ms":155,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:11:16+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:12:21+08:00"} +{"bytes":518,"duration_ms":500,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:12:21+08:00"} +{"bytes":1021,"duration_ms":59,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:12:55+08:00"} +{"bytes":518,"duration_ms":156,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:12:55+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":59,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:14:39+08:00"} +{"bytes":1021,"duration_ms":47,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:41+08:00"} +{"bytes":319,"duration_ms":202,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:43+08:00"} +{"bytes":1803,"duration_ms":101,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:49+08:00"} +{"bytes":1614,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:49+08:00"} +{"bytes":319,"duration_ms":202,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:52+08:00"} +{"bytes":319,"duration_ms":295,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:52+08:00"} +{"bytes":319,"duration_ms":232,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:55+08:00"} +{"bytes":350,"duration_ms":203,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:15:57+08:00"} +{"bytes":1021,"duration_ms":71,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:17:53+08:00"} +{"bytes":1430,"duration_ms":131,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:17:54+08:00"} +{"bytes":1614,"duration_ms":33,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:17:54+08:00"} +{"bytes":121,"duration_ms":243,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:17:57+08:00"} +{"bytes":319,"duration_ms":196,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:18:02+08:00"} +connecting YMT MySQL: 47.97.27.195:3306 db merketing user root +connecting Marketing MySQL: 192.168.6.92:3306 db market user root +YMT DSN: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local&charset=utf8mb4 +Marketing DSN: root:lansexiongdi@tcp(192.168.6.92:3306)/market?parseTime=True&loc=Local&charset=utf8mb4 +server listening on :8077 +{"bytes":1021,"duration_ms":46,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:21:33+08:00"} +{"bytes":1021,"duration_ms":57,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:22:52+08:00"} +{"bytes":399,"duration_ms":204,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-25T15:22:53+08:00"} diff --git a/server/storage/export_20251125150158.xlsx b/server/storage/export_20251125150158.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..17aea939d512d9743188b8fd68209ae05cf55c96 GIT binary patch literal 6536 zcmaJ`2Ut^C(+(gVA#{+M&_q~@QU&Qv2)#%#w9p9>MWJEk9avOSV82StbG9UZRq>g-8zdG?$pHuj5 zRA2e`DnXq}wP^!egbBXG4ZCyV;x-c^}%+lEBe4_+|Ql#m< z@Z}O5n&LYgm79R3!?z+(r4L6YDCLnU#IHVSSwIZ=SaxpGrn>8zPE8pMnMAI-%YJJH zGH{aWVqgy(Z=~*eEQN{sAFU^C26<|1`P?5TmT5iBlW1(Xbe5~vH^I0DvGN0HF7eej>;D$;!(f?ky~IF=mc*YMzJyiMEc<`IRwqg{%F_0wKFyjPNsvb{$+1!@ff4JysX`1gV#DhZD$eXa#0G6QG|1v+WqAGw-44ZrC;S(K$Zl$)5}74^rl;dO*2x%SNbwdG;L>>g&SwlVbYVPK!p>V^+|%W>Zvu z8fznzcTfSdNPrSjU2p477zTGrd>pPVC4uMJ>}EG-H=Hj`6j~xz_+ACFY##1E;8?Iv zto6a-5cw0IhQZ*BFSym);r|;xz>6n5V0vC22ybB<1mYry^M>WME{OsOx8`-LQSJE? zO3QsKz>{(F$^)(I!IEi%(Srdh@~v?n%`D{DPXdNHg5Gp9=caFAp7*}F+^0{o?dkVo zV0?3kt?Pccvq3fUW6D;$_tW`NUOJQuwUE13P98>Wv8zPby}U?s68bEA$%UJ>z&-#&eIv9e zw*6l0!1UJNF|j1YNm`Fv5ho6Xe~t<2pVtiY@Up*HGzzJ$MIZ`{!JZ}X9L~L|*Aqjn zDz4sEtSSm1e^YnaT~5P5@+^3@e$E7=-RC^-$Ue^JH=Q(EQbDflrD&efPGG63 zyQuNdPOS=iT=IzWYxEey{3CX&n2Ps3#e(@t;6@XOYUZ@MzTYUKk!cGy&<*f4PPd5_ zdb;cubY5jnQypHIGe5BbSLJ6J>Yd+rJr}IA%zb$jyUh(`Jv*uTM*3F>sV*p#0&!5X z{%Z(b!NC~-7yfgX`FnV|+Io1nUO@WCj;kDLcWF@oc5I&=(hcm%s|$mo3oU8!CwW)! z7}(}MAEaU+G}sLSq9XO@RaMjWIoOb_9jU;la{SfFkE6f?iuz74$8NpO;N6{b>XiNV zo%?wVECehvH90NhcO$8p9|4=hm&F}DQ6hnpy~{5V4C4_`+=qiWWtWc#N_+{{DoSqp z(xVLTM7a1|(YW?4VRbM@gXznQK3=SX_(NoUWlE@rMNrB{>{Cw3OW)9dHJZt+nYwNz zSd-LJ>n-^Sw=6UvSB&i9$!1tlQ?s^DdmO^6PmOPFo_ksZ6JF+fB8+ue{x0(uV4e%_ zTvl;#EdMKDbbkWo?PTL+Z)fP^SEky$-Gtj+3kYNJ4gR zH!$jHMBqIpDgDlOGAW#9(WUl_heFd)#f~C@{2kmKLr|j_)y@wL5JNp-g0^D>vi%$C*3E1Rll1tO}R-h+jb>%nk>IRfidqDwP{MW2$h?3Ek|duOxp=}S1+b#6O|CW3%|G)En0^#<5cpa4mO`W;Q7=c}|iVTs5W$^K(0Q?WMF zuNF>k4-09U;pP}P|NJ43=_riz6~J1WwpoL+M{GuH;+ulh*kKK*%gaRTHFEUOV8d|k zYSgujIw>tivw9urBJe7S#k*Guc2GlzoYt%t7@`h(57bAlJ=d=R_3IS{m*!o5{Y)a= zO0kH^ws>U02=n3@?eIu1F$w}OauHhc<|F5R^8~X?!xQ*B#hkv{+cHPvU^xO~{kl7G zhd_y5DaeweL8#JFToC?I579g(`ih6m zuII~@>Z%*F;xy_jz)E_8BW=nNL9!)|&?hqY-Z_&k>RSvWgWPk`ijRqTqqtUA@{ z=I>Y7wZiH(GzEF@yzhFMG=7-FEbF1ArCgM4zULbnefd`UBT!AQ!3`6;MZddo;h`3LXU(Eh&G zVilWgi#4Z zrVZART(vdunx~4Io+$d=0ojx2@-@l66P`W~17)E0v*auxeLcBPbs0p0pXix3s*H0p z$V*1Yf~i+$pPVkVRQBH;nEHG~YDblE*JLA5aLIPSa`07`G3#=%ne_vufF?(T=wuJD z1Yrjwzf*fV%$&=}ZmaK_1KH0p}X%LRw1AYv!KqgLO-ddwxlDq%m3_tssCX+(;JTYbCrZ zPG{7N*A{|j6&Z0);NuPXEi)EV!b)>Mg&d&bK}R`#mCNPIAb^QmgmG$zbR$~9-xKIX6hVOo`e2>SuH|3IZE2!qSyoEY18ZJXxbR!RRyCsDJ9oFTAv-TOtdQTHJY}TYd{v2lGowPxhH z#E&S@=MHa%39yJ*PZ~Isl8h9JxUXM-ff4at!2Qme#}(9n)|U%kNM-6S0RW@8>g%6tt&2Vh zR~+~_*}K{QIf`B!b(rj7j~NNNUhJ^|$^4x+@^-z7_rcYl{9MNvv_CxVb&P*7rH4`& zyB)&vVO)rS$fsL~z;DKe$;6t_1XCl=tC0BYn2YFC12RoRTPI{fp-qYGI}F4gv-7u^ zwuOBXv-z5(p-On@xt^0qy%ACzwR_g8ERYb0@?eQ2(Li-2ocM=FY@Lo4oVNfUBchrTM{Wqsv{Y4eP$aGd%i` zOvM@15o5cC_L-TDXW-)BF5xW}o6zq15y^0Q(BHr$4&=l=IV&A^QA#e&r}vo|2+JKA z!rypa_-Pwa%_`vJ9~mTWywMOl$NSEru$8_J>z>+t$}858%KTYfpm2TvW_J!4YN|_! zHrAqS*=$^}tiLXAE0?%L^xaZ{_7f;?K@DdL@V3UV8U0Tb}PstjnLZXi7nm z$8)=r!UgT$&(_<+!W=s5c_0u>8n4P@ndM7!jHCz)h3iNUb@mEuS(z8z!I>| zh+TdtF|;X|d|Sy46eC)O@=V2>1XAmW^{>351~Hsa7vJ}tei})q2@wxc8Ku;7C8b(b zne1+G!lzh~Sd0}la%nmTZjd;TOM-ii@dGXmGDKBr9(i;@lx^RV&^XWp7s4!Ph`pJO z7$DB1vS3KzT-sH87`8uWN+3p%R5_oq5G=5A?#E zGb~&@=EJAvG5HS3$!{qupA@@kxrB~9WSO=o*O{P-f&{9}no`_S*r_qcxPm zl2Id8dTbo8(r7puc1V!dpz37BN=H+#liO-ZCA1DyO_S=)k6dZ^h!uo;pjg|xhR+j6 zE!$arC=QRHC?^crgx=NPSZwGJ>??()LVVMmJNE* zMu4Q~>6`Q?M{CbRi*pmqw9(Lv`NH6rU@Mzq0dUfW3B3)xn@Oogwq@ldJH^_=W~jiE z_HDWMAIxsCFuj<6Ov`b2vmj9)P?q|XLQG9DJx0!Cx`|A1NsiLT>}!LxnL*heiP7Dx z&xWdJq!TJ&HChgjtbvYiN){D!{_=M{2@bm}@@iZ9>~8Wzx-*&0Zt_E=%JM_iKVj4c z#gN3F0a3hgUaaz0*o%dJGA4s!;c{jJXZWTn{lk=s9sSA{1E_3K#~4$8iY@y}&-xg- zv>JQU*yqZ;W*VKM?VHV;pGQpgN8nDJr#*(4Q}IA$w{|CYU}{>9w5tM&|NcZr7lfQ-i)$7_gtZ&GrjpIi;@^)Qca$wV$w4K3#%39>zc{~v zyxb7}9K+9$Wy~KgnK`O4g;%>6gS}Gi+U{qUe5rc6QrgJA+S>Sh;WM3?wggO8HOTn8 z5U7_jlDX2@ao6y7_cm@tjKtlz$bG-oYs8_vEya>+TW`KI?(oiXBdiZqLsQec#T5S__u6iOGp47XJ1m-x+&fGOq2|`q!S5>4zu1o$T#>yoLYx_~S56x^q1#wAFIY} zvvr#V|CiOFi*tih^!~0DiECA5*nsPg9__R_5&PvK+|M0j%GT@OksG=P-TL+kSb^?p za6o9I{RDL@>L9QxEy7UU-bIJ+XLPDFWih)o2PuvdN4x1XqS+lr3~l8f%XE)-^i?%%f!)r4scnx zw2;vt_)+z?(o9Ifj$6UbPIfD~SrR6+kH#)Bjns}qi5K~svR@Pi)~ADn#$YbjRHWsD zj_GO|`sBLj;Ac)eu#L%J5}{r7sMe)IP*%OwF;EfXY;W z_{WcMw-xSQz}@nIco3nYZXB3UUpJ2Ho(*3*-K$%m2XvP#f!btX;W$3R$BZhOQB&l$ znu=S5`o)~zqne|($k)imgi8hD+6$j^p5rF)HP*! z6U4X)a9{ZIM)7fUcu4)x9%%5K=&xLG%K6RX1Q$Uve>ARm_;i3DocGTx>qRa1pYlH# za6PU6H1IPGc|pehkW$<$f1+l8Rs5ODyI?7QNFOfJf8{KH)&7~fy5ReMNDHoE|6BVn z_U~8qpNX6ce(Q&j{19o}zY-S~ z{$>(>RsA`~|EKS+;w=4-s{B_2Kc|+9qu__E(EMTG=Y!!_<)5SKBK!Q1DjX8Hw_hm# YS1!`iBE$g$0FdFX2%MjH7%sm3AFysAS^xk5 literal 0 HcmV?d00001 diff --git a/server/storage/export_20251125150231.xlsx b/server/storage/export_20251125150231.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..c48b41a0cd33a51d0610e39e4b56500f1acd0ff3 GIT binary patch literal 6501 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 - -
- 任务 {{ job.id }} 状态:{{ job.status }} 行数:{{ job.total_rows || '' }} - 下载 + + + + + + + + + + + + + + + +
+
关闭
+
-
暂无任务
+
暂无任务
@@ -143,13 +160,13 @@ 保存 - + 筛选条件 - + - + @@ -164,7 +181,9 @@ - + + + diff --git a/web/main.js b/web/main.js index 4b6eb9d..da33c38 100644 --- a/web/main.js +++ b/web/main.js @@ -3,6 +3,12 @@ const { createApp, reactive } = Vue; setup(){ const state = reactive({ templates: [], + jobs: [], + jobsVisible: false, + jobsTplId: null, + jobsPage: 1, + jobsPageSize: 15, + jobsTotal: 0, job: {}, form: { name: '', @@ -345,6 +351,7 @@ const { createApp, reactive } = Vue; return v || '' } const creatorOptions = Vue.ref([]) + const resellerOptions = Vue.ref([]) const hasCreators = Vue.computed(()=> Array.isArray(state.exportForm.creatorIds) && state.exportForm.creatorIds.length>0 ) const hasReseller = Vue.computed(()=> !!state.exportForm.resellerId) const hasPlan = Vue.computed(()=> !!state.exportForm.planId) @@ -358,6 +365,16 @@ const { createApp, reactive } = Vue; creatorOptions.value = arr.map(it=>({label: it.name || String(it.id), value: Number(it.id)})) }catch(_e){ creatorOptions.value = [] } } + const loadResellers = async ()=>{ + const ids = Array.isArray(state.exportForm.creatorIds) ? state.exportForm.creatorIds : [] + if(!ids.length){ resellerOptions.value = []; return } + try{ + const res = await fetch(API_BASE + '/api/resellers?creator=' + ids.join(',')) + const data = await res.json() + const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []) + resellerOptions.value = arr.map(it=>({label: (it.name||'') + (it.name?'':'') , value: Number(it.id)})) + }catch(_e){ resellerOptions.value = [] } + } const exportType = Vue.computed(()=>{ const f = state.exportTpl && state.exportTpl.filters if(!f) return null @@ -404,6 +421,12 @@ const { createApp, reactive } = Vue; const pad=(n)=>String(n).padStart(2,'0'); return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; } + const yearRange = ()=>{ + const now = new Date() + const start = new Date(now.getFullYear(), 0, 1, 0, 0, 0) + const end = new Date(now.getFullYear(), 11, 31, 23, 59, 59) + return [ fmtDT(start), fmtDT(end) ] + } const loadTemplates = async ()=>{ try{ const res = await fetch(API_BASE + '/api/templates'); @@ -420,6 +443,33 @@ const { createApp, reactive } = Vue; state.templates = [] } } + const loadJobs = async (page)=>{ + if(!page) page = state.jobsPage + try{ + const qs = new URLSearchParams() + qs.set('page', String(page)) + qs.set('page_size', String(state.jobsPageSize)) + if(state.jobsTplId){ qs.set('template_id', String(state.jobsTplId)) } + const res = await fetch(API_BASE + '/api/exports?' + qs.toString()); + if(!res.ok){ state.jobs = []; return } + const data = await res.json(); + const payload = data?.data || data || {} + const arr = Array.isArray(payload.items) ? payload.items : (Array.isArray(payload) ? payload : []) + state.jobs = arr + state.jobsTotal = Number(payload.total || 0) + state.jobsPage = Number(payload.page || page) + }catch(_e){ state.jobs = [] } + } + const openJobs = (row)=>{ state.jobsTplId = row.id; state.jobsVisible = true; loadJobs(1) } + const closeJobs = ()=>{ state.jobsVisible = false } + const jobPercent = (row)=>{ + const est = Number(row.row_estimate || 0) + const done = Number(row.total_rows || 0) + if(row.status==='completed') return '100%' + if(row.status==='queued') return '0%' + if(est>0 && done>=0){ const p = Math.max(0, Math.min(100, Math.floor(done*100/est))); return p + '%' } + return row.status || '' + } const createTemplate = async ()=>{ const formRef = createFormRef.value const ok = formRef ? await formRef.validate().catch(()=>false) : true @@ -463,6 +513,7 @@ const { createApp, reactive } = Vue; state.exportForm.datasource = state.exportTpl.datasource || row.datasource || 'marketing' state.exportForm.file_format = state.exportTpl.file_format || row.file_format || 'xlsx' if(state.exportForm.datasource==='marketing'){ loadCreators() } + if(!Array.isArray(state.exportForm.dateRange) || state.exportForm.dateRange.length!==2){ state.exportForm.dateRange = yearRange() } state.exportVisible = true } const loadTemplateDetail = async (id)=>{ @@ -501,9 +552,9 @@ const { createApp, reactive } = Vue; const j=await r.json(); const jid = j?.data?.id ?? j?.id state.exportVisible=false - if(jid){ loadJob(jid) } else { msg('任务创建返回异常','error') } + if(jid){ loadJob(jid); loadJobs() } else { msg('任务创建返回异常','error') } } - Vue.watch(()=>state.exportForm.creatorIds, ()=>{ state.exportForm.resellerId=null; state.exportForm.planId=null; state.exportForm.keyBatchId=null; state.exportForm.codeBatchId=null; state.exportForm.productId=null }) + Vue.watch(()=>state.exportForm.creatorIds, ()=>{ state.exportForm.resellerId=null; state.exportForm.planId=null; state.exportForm.keyBatchId=null; state.exportForm.codeBatchId=null; state.exportForm.productId=null; loadResellers() }) Vue.watch(()=>state.exportForm.resellerId, ()=>{ state.exportForm.planId=null; state.exportForm.keyBatchId=null; state.exportForm.codeBatchId=null; state.exportForm.productId=null }) Vue.watch(()=>state.exportForm.planId, ()=>{ state.exportForm.keyBatchId=null; state.exportForm.codeBatchId=null; state.exportForm.productId=null }) Vue.watch(()=>state.exportForm.keyBatchId, ()=>{ state.exportForm.codeBatchId=null; state.exportForm.productId=null }) @@ -562,7 +613,8 @@ const { createApp, reactive } = Vue; } const download = (id)=>{ window.open(API_BASE + '/api/exports/'+id+'/download','_blank') } loadTemplates() - return { ...Vue.toRefs(state), visibilityOptions, formatOptions, datasourceOptions, fieldOptions, loadTemplates, createTemplate, openExport, submitExport, loadJob, download, openEdit, saveEdit, removeTemplate, resizeDialog, createRules, exportRules, editRules, createFormRef, exportFormRef, editFormRef, dsLabel, exportType, isOrder, exportTitle } + + return { ...Vue.toRefs(state), visibilityOptions, formatOptions, datasourceOptions, fieldOptions, loadTemplates, createTemplate, openExport, submitExport, loadJob, loadJobs, openJobs, closeJobs, download, openEdit, saveEdit, removeTemplate, resizeDialog, createRules, exportRules, editRules, createFormRef, exportFormRef, editFormRef, dsLabel, exportType, isOrder, exportTitle, creatorOptions, resellerOptions, hasCreators, hasReseller, hasPlan, hasKeyBatch, hasCodeBatch, jobPercent } } }) app.use(ElementPlus)