diff --git a/server/bin/marketing-data-server b/server/bin/marketing-data-server index 8790ef3..4dd5e47 100755 Binary files a/server/bin/marketing-data-server and b/server/bin/marketing-data-server differ diff --git a/server/internal/api/creators.go b/server/internal/api/creators.go index b986a16..5d93312 100644 --- a/server/internal/api/creators.go +++ b/server/internal/api/creators.go @@ -30,15 +30,14 @@ func (a *CreatorsAPI) list(w http.ResponseWriter, r *http.Request) { if limitStr != "" { if n, err := strconv.Atoi(limitStr); err == nil && n > 0 && n <= 10000 { limit = n } } - // Try plan table first (creator, creator_name) - sql1 := "SELECT DISTINCT creator, COALESCE(creator_name, '') AS name FROM plan WHERE creator IS NOT NULL" + sql1 := "SELECT DISTINCT user_id, COALESCE(user_name, '') AS name FROM activity WHERE user_id IS NOT NULL" args := []interface{}{} if q != "" { - sql1 += " AND (CAST(creator AS CHAR) LIKE ? OR creator_name LIKE ?)" + sql1 += " AND (CAST(user_id AS CHAR) LIKE ? OR user_name LIKE ?)" like := "%" + q + "%" args = append(args, like, like) } - sql1 += " ORDER BY creator ASC LIMIT ?" + sql1 += " ORDER BY user_id ASC LIMIT ?" args = append(args, limit) rows, err := a.marketing.Query(sql1, args...) out := []map[string]interface{}{} @@ -53,9 +52,31 @@ func (a *CreatorsAPI) list(w http.ResponseWriter, r *http.Request) { out = append(out, m) } } - // Fallback to order table if empty or error if err != nil || len(out) == 0 { - sql2 := "SELECT DISTINCT creator, '' AS name FROM `order` WHERE creator IS NOT NULL" + sqlPlan := "SELECT DISTINCT creator, COALESCE(creator_name, '') AS name FROM plan WHERE creator IS NOT NULL" + argsPlan := []interface{}{} + if q != "" { + sqlPlan += " AND (CAST(creator AS CHAR) LIKE ? OR creator_name LIKE ?)" + like := "%" + q + "%" + argsPlan = append(argsPlan, like, like) + } + sqlPlan += " ORDER BY creator ASC LIMIT ?" + argsPlan = append(argsPlan, limit) + rowsPlan, errPlan := a.marketing.Query(sqlPlan, argsPlan...) + if errPlan == nil { + defer rowsPlan.Close() + tmp := []map[string]interface{}{} + for rowsPlan.Next() { + var id sql.NullInt64 + var name sql.NullString + if err := rowsPlan.Scan(&id, &name); err != nil { continue } + if !id.Valid { continue } + tmp = append(tmp, map[string]interface{}{"id": id.Int64, "name": name.String}) + } + if len(tmp) > 0 { out = tmp } + } + if len(out) == 0 { + sql2 := "SELECT DISTINCT creator, '' AS name FROM `order` WHERE creator IS NOT NULL" args2 := []interface{}{} if q != "" { sql2 += " AND CAST(creator AS CHAR) LIKE ?" @@ -64,19 +85,20 @@ func (a *CreatorsAPI) list(w http.ResponseWriter, r *http.Request) { sql2 += " ORDER BY creator ASC LIMIT ?" args2 = append(args2, limit) rows2, err2 := a.marketing.Query(sql2, args2...) - if err2 != nil { - fail(w, r, http.StatusInternalServerError, err2.Error()) - return - } - defer rows2.Close() - out = out[:0] - for rows2.Next() { - var id sql.NullInt64 - var name sql.NullString - if err := rows2.Scan(&id, &name); err != nil { continue } - if !id.Valid { continue } - m := map[string]interface{}{"id": id.Int64, "name": name.String} - out = append(out, m) + if err2 != nil { + fail(w, r, http.StatusInternalServerError, err2.Error()) + return + } + defer rows2.Close() + out = out[:0] + for rows2.Next() { + var id sql.NullInt64 + var name sql.NullString + if err := rows2.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/exports.go b/server/internal/api/exports.go index 7da54ca..11c27bd 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -106,31 +106,31 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { } r = WithSQL(r, q) dataDB := a.selectDataDB(ds) - score, sugg, err := exporter.EvaluateExplain(dataDB, q, args) - if err != nil { - fail(w, r, http.StatusBadRequest, err.Error()) - return - } - sugg = append(sugg, exporter.IndexSuggestions(req)...) + score, sugg, err := exporter.EvaluateExplain(dataDB, q, args) + if err != nil { + fail(w, r, http.StatusBadRequest, err.Error()) + return + } + sugg = append(sugg, exporter.IndexSuggestions(req)...) const passThreshold = 60 if score < passThreshold { fail(w, r, http.StatusBadRequest, fmt.Sprintf("EXPLAIN 未通过:评分=%d,请优化索引或缩小查询范围", score)) return } - var estimate int64 - func() { - idx := strings.Index(q, " FROM ") - if idx > 0 { - cq := "SELECT COUNT(1)" + q[idx:] - row := dataDB.QueryRow(cq, args...) - var cnt int64 - if err := row.Scan(&cnt); err == nil { - estimate = cnt - return - } - } - estimate = 0 - }() + var estimate int64 + func() { + idx := strings.Index(q, " FROM ") + if idx > 0 { + cq := "SELECT COUNT(1)" + q[idx:] + row := dataDB.QueryRow(cq, args...) + var cnt int64 + if err := row.Scan(&cnt); err == nil { + estimate = cnt + return + } + } + estimate = 0 + }() labels := FieldLabels() hdrs := make([]string, len(fs)) for i, tf := range fs { @@ -148,7 +148,7 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { } } ejSQL := "INSERT INTO export_jobs (template_id, status, requested_by, owner_id, 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, owner, toJSON(p.Permission), toJSON(p.Filters), toJSON(p.Options), toJSON(map[string]interface{}{"sql": q, "suggestions": sugg}), score, estimate, p.FileFormat, time.Now(), time.Now()} + ejArgs := []interface{}{p.TemplateID, "queued", p.RequestedBy, owner, toJSON(p.Permission), toJSON(p.Filters), toJSON(p.Options), toJSON(map[string]interface{}{"sql": q, "suggestions": sugg}), 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 { @@ -225,7 +225,7 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, fetched = true if err := rows2.Scan(dest...); err != nil { rows2.Close() - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id?", "failed", time.Now(), id) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) return } vals := make([]string, len(cols)) @@ -312,7 +312,7 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, fetched = true if err := rows3.Scan(dest...); err != nil { rows3.Close() - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id?", "failed", time.Now(), id) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) return } vals := make([]string, len(cols)) @@ -556,7 +556,7 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, fetched = true if err := rows2.Scan(dest...); err != nil { rows2.Close() - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id?", "failed", time.Now(), id) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) return } vals := make([]string, len(cols)) @@ -609,7 +609,7 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, var tick2 int64 for rows.Next() { if err := rows.Scan(dest2...); err != nil { - a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id?", "failed", time.Now(), id) + a.meta.Exec("UPDATE export_jobs SET status=?, finished_at=? WHERE id= ?", "failed", time.Now(), id) return } vals := make([]string, len(cols)) @@ -794,11 +794,11 @@ func (a *ExportsAPI) getSQL(w http.ResponseWriter, r *http.Request, id string) { json.Unmarshal(filters, &fl) wl := Whitelist() req := exporter.BuildRequest{MainTable: main, Datasource: ds, Fields: fs, Filters: fl} - q, args, err := exporter.BuildSQL(req, wl) - if err != nil { - failCat(w, r, http.StatusBadRequest, err.Error(), "sql_build_error") - return - } + q, args, err := exporter.BuildSQL(req, wl) + if err != nil { + failCat(w, r, http.StatusBadRequest, err.Error(), "sql_build_error") + return + } formatArg := func(a interface{}) string { switch t := a.(type) { case nil: @@ -975,10 +975,10 @@ func (a *ExportsAPI) list(w http.ResponseWriter, r *http.Request) { rows, err = a.meta.Query("SELECT id, template_id, status, requested_by, row_estimate, total_rows, file_format, created_at, updated_at, explain_score, explain_json FROM export_jobs ORDER BY id DESC LIMIT ? OFFSET ?", size, offset) } } - if err != nil { - failCat(w, r, http.StatusInternalServerError, err.Error(), "explain_error") - return - } + if err != nil { + failCat(w, r, http.StatusInternalServerError, err.Error(), "explain_error") + return + } defer rows.Close() items := []map[string]interface{}{} for rows.Next() { diff --git a/server/internal/api/router.go b/server/internal/api/router.go index 31d2e22..eb9a971 100644 --- a/server/internal/api/router.go +++ b/server/internal/api/router.go @@ -19,6 +19,12 @@ func NewRouter(metaDB *sql.DB, marketingDB *sql.DB) http.Handler { mux.Handle("/api/resellers/", withAccess(withTrace(ResellersHandler(marketingDB)))) mux.Handle("/api/plans", withAccess(withTrace(PlansHandler(marketingDB)))) mux.Handle("/api/plans/", withAccess(withTrace(PlansHandler(marketingDB)))) + mux.Handle("/api/ymt/users", withAccess(withTrace(YMTUsersHandler(metaDB)))) + mux.Handle("/api/ymt/users/", withAccess(withTrace(YMTUsersHandler(metaDB)))) + mux.Handle("/api/ymt/merchants", withAccess(withTrace(YMTMerchantsHandler(metaDB)))) + mux.Handle("/api/ymt/merchants/", withAccess(withTrace(YMTMerchantsHandler(metaDB)))) + mux.Handle("/api/ymt/activities", withAccess(withTrace(YMTActivitiesHandler(metaDB)))) + mux.Handle("/api/ymt/activities/", withAccess(withTrace(YMTActivitiesHandler(metaDB)))) mux.HandleFunc("/api/utils/decode_key", func(w http.ResponseWriter, r *http.Request) { v := r.URL.Query().Get("v") if v == "" { diff --git a/server/internal/api/ymt_activities.go b/server/internal/api/ymt_activities.go new file mode 100644 index 0000000..4fc8bd3 --- /dev/null +++ b/server/internal/api/ymt_activities.go @@ -0,0 +1,62 @@ +package api + +import ( + "database/sql" + "net/http" + "strconv" +) + +type YMTActivitiesAPI struct { + ymt *sql.DB +} + +func YMTActivitiesHandler(ymt *sql.DB) http.Handler { + api := &YMTActivitiesAPI{ymt: ymt} + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + api.list(w, r) + return + } + w.WriteHeader(http.StatusNotFound) + }) +} + +func (a *YMTActivitiesAPI) list(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + merchantIDStr := q.Get("merchant_id") + like := q.Get("q") + limitStr := q.Get("limit") + limit := 2000 + if limitStr != "" { + if n, err := strconv.Atoi(limitStr); err == nil && n > 0 && n <= 10000 { limit = n } + } + sql1 := "SELECT id, name FROM activity WHERE id IS NOT NULL" + args := []interface{}{} + if merchantIDStr != "" { + sql1 += " AND merchant_id = ?" + args = append(args, merchantIDStr) + } + if like != "" { + sql1 += " AND (CAST(id AS CHAR) LIKE ? OR name LIKE ?)" + s := "%" + like + "%" + args = append(args, s, s) + } + sql1 += " ORDER BY id ASC LIMIT ?" + args = append(args, limit) + rows, err := a.ymt.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 } + out = append(out, map[string]interface{}{"id": id.Int64, "name": name.String}) + } + ok(w, r, out) +} + diff --git a/server/internal/api/ymt_merchants.go b/server/internal/api/ymt_merchants.go new file mode 100644 index 0000000..5d15510 --- /dev/null +++ b/server/internal/api/ymt_merchants.go @@ -0,0 +1,62 @@ +package api + +import ( + "database/sql" + "net/http" + "strconv" +) + +type YMTMerchantsAPI struct { + ymt *sql.DB +} + +func YMTMerchantsHandler(ymt *sql.DB) http.Handler { + api := &YMTMerchantsAPI{ymt: ymt} + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + api.list(w, r) + return + } + w.WriteHeader(http.StatusNotFound) + }) +} + +func (a *YMTMerchantsAPI) list(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + userIDStr := q.Get("user_id") + like := q.Get("q") + limitStr := q.Get("limit") + limit := 2000 + if limitStr != "" { + if n, err := strconv.Atoi(limitStr); err == nil && n > 0 && n <= 10000 { limit = n } + } + sql1 := "SELECT id, name FROM merchant WHERE id IS NOT NULL" + args := []interface{}{} + if userIDStr != "" { + sql1 += " AND user_id = ?" + args = append(args, userIDStr) + } + if like != "" { + sql1 += " AND (CAST(id AS CHAR) LIKE ? OR name LIKE ?)" + s := "%" + like + "%" + args = append(args, s, s) + } + sql1 += " ORDER BY id ASC LIMIT ?" + args = append(args, limit) + rows, err := a.ymt.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 } + out = append(out, map[string]interface{}{"id": id.Int64, "name": name.String}) + } + ok(w, r, out) +} + diff --git a/server/internal/api/ymt_users.go b/server/internal/api/ymt_users.go new file mode 100644 index 0000000..ce694dc --- /dev/null +++ b/server/internal/api/ymt_users.go @@ -0,0 +1,57 @@ +package api + +import ( + "database/sql" + "net/http" + "strconv" + "strings" +) + +type YMTUsersAPI struct { + ymt *sql.DB +} + +func YMTUsersHandler(ymt *sql.DB) http.Handler { + api := &YMTUsersAPI{ymt: ymt} + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + p := strings.TrimPrefix(r.URL.Path, "/api/ymt/users") + if r.Method == http.MethodGet && p == "" { + api.list(w, r) + return + } + w.WriteHeader(http.StatusNotFound) + }) +} + +func (a *YMTUsersAPI) list(w http.ResponseWriter, r *http.Request) { + 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 } + } + sql1 := "SELECT DISTINCT user_id, COALESCE(user_name, '') AS name FROM activity WHERE user_id IS NOT NULL" + args := []interface{}{} + if q != "" { + sql1 += " AND (CAST(user_id AS CHAR) LIKE ? OR user_name LIKE ?)" + like := "%" + q + "%" + args = append(args, like, like) + } + sql1 += " ORDER BY user_id ASC LIMIT ?" + args = append(args, limit) + rows, err := a.ymt.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 } + out = append(out, map[string]interface{}{"id": id.Int64, "name": name.String}) + } + ok(w, r, out) +} diff --git a/server/internal/exporter/sqlbuilder.go b/server/internal/exporter/sqlbuilder.go index d4d3651..b908cd7 100644 --- a/server/internal/exporter/sqlbuilder.go +++ b/server/internal/exporter/sqlbuilder.go @@ -32,12 +32,14 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{ return "", nil, errors.New("invalid field format") } t, f := parts[0], parts[1] + if req.Datasource == "ymt" && t == "order_voucher" && f == "channel_activity_id" { + f = "channel_batch_no" + } need[t] = true mt := sch.TableName(t) mf, _ := sch.MapField(t, f) if t == "order" && req.MainTable == "order" { if f == "status" { - cols = append(cols, "CASE `order`.type WHEN 1 THEN '直充卡密' WHEN 2 THEN '立减金' WHEN 3 THEN '红包' ELSE '' END AS type") cols = append(cols, "CASE `order`.status WHEN 0 THEN '待充值' WHEN 1 THEN '充值中' WHEN 2 THEN '已完成' WHEN 3 THEN '充值失败' WHEN 4 THEN '已取消' WHEN 5 THEN '已过期' WHEN 6 THEN '待支付' END AS status") continue } diff --git a/server/internal/schema/ymt.go b/server/internal/schema/ymt.go index 1bb382e..789f417 100644 --- a/server/internal/schema/ymt.go +++ b/server/internal/schema/ymt.go @@ -25,6 +25,13 @@ func (s ymtSchema) MapField(t, f string) (string, bool) { return f, true } } + if t == "order_voucher" { + switch f { + case "channel_activity_id": return "channel_batch_no", true + default: + return f, true + } + } return f, true } diff --git a/server/log/server-20251127.log b/server/log/server-20251127.log index 4934ca5..ec115ed 100644 --- a/server/log/server-20251127.log +++ b/server/log/server-20251127.log @@ -140,3 +140,152 @@ server listening on :8077 {"bytes":2187,"duration_ms":92,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:38:10+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 +server listening on :8077 +{"bytes":2075,"duration_ms":55,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:03+08:00"} +{"bytes":19367,"duration_ms":81,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:03+08:00"} +{"bytes":884,"duration_ms":94,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:05+08:00"} +{"bytes":19423,"duration_ms":86,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:05+08:00"} +{"bytes":19423,"duration_ms":79,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:05+08:00"} +{"bytes":2075,"duration_ms":49,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:08+08:00"} +{"bytes":19367,"duration_ms":70,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:08+08:00"} +{"bytes":2075,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:13+08:00"} +{"bytes":19367,"duration_ms":103,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:13+08:00"} +{"bytes":884,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:15+08:00"} +{"bytes":19423,"duration_ms":79,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:15+08:00"} +{"bytes":19423,"duration_ms":87,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:15+08:00"} +{"bytes":2187,"duration_ms":98,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:21+08:00"} +{"bytes":14655,"duration_ms":785,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:22+08:00"} +{"bytes":14655,"duration_ms":1165,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:22+08:00"} +{"bytes":2187,"duration_ms":105,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:30+08:00"} +{"bytes":14655,"duration_ms":810,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:31+08:00"} +{"bytes":14655,"duration_ms":798,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:32+08:00"} +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate request body: {"name":"易码通-红包订单-导出","visibility":"private","file_format":"xlsx","fields":["order.order_number","order.creator","order.out_trade_no","order.type","order.status","order.contract_price","order.num","order.pay_amount","order.create_time"],"filters":{"type_eq":3},"main_table":"order_info"} +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate parsed payload: map[fields:[order.order_number order.creator order.out_trade_no order.type order.status order.contract_price order.num order.pay_amount order.create_time] file_format:xlsx filters:map[type_eq:3] main_table:order_info name:易码通-红包订单-导出 visibility:private] +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate template ID: 19 +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: main_table, value: order_info, type: string +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added string field: main_table, value: order_info +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: name, value: 易码通-红包订单-导出, type: string +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added string field: name, value: 易码通-红包订单-导出 +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: visibility, value: private, type: string +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added string field: visibility, value: private +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: file_format, value: xlsx, type: string +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added string field: file_format, value: xlsx +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: fields, value: [order.order_number order.creator order.out_trade_no order.type order.status order.contract_price order.num order.pay_amount order.create_time], type: []interface {} +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added fields_json: ["order.order_number","order.creator","order.out_trade_no","order.type","order.status","order.contract_price","order.num","order.pay_amount","order.create_time"] +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate processing field: filters, value: map[type_eq:3], type: map[string]interface {} +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate added filters_json: {"type_eq":3} +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate executing SQL: UPDATE export_templates SET main_table=?,name=?,visibility=?,file_format=?,fields_json=?,filters_json=?,updated_at=? WHERE id= ? +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate SQL args: [order_info 易码通-红包订单-导出 private xlsx [91 34 111 114 100 101 114 46 111 114 100 101 114 95 110 117 109 98 101 114 34 44 34 111 114 100 101 114 46 99 114 101 97 116 111 114 34 44 34 111 114 100 101 114 46 111 117 116 95 116 114 97 100 101 95 110 111 34 44 34 111 114 100 101 114 46 116 121 112 101 34 44 34 111 114 100 101 114 46 115 116 97 116 117 115 34 44 34 111 114 100 101 114 46 99 111 110 116 114 97 99 116 95 112 114 105 99 101 34 44 34 111 114 100 101 114 46 110 117 109 34 44 34 111 114 100 101 114 46 112 97 121 95 97 109 111 117 110 116 34 44 34 111 114 100 101 114 46 99 114 101 97 116 101 95 116 105 109 101 34 93] [123 34 116 121 112 101 95 101 113 34 58 51 125] 2025-11-27 18:41:40.910221 +0800 CST m=+46.878715918 19] +trace_id=6f0f7bcc51739fdf6827f84eb1bfa1ba patchTemplate update successful +{"bytes":79,"duration_ms":110,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:41+08:00"} +{"bytes":2074,"duration_ms":50,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:41+08:00"} +{"bytes":583,"duration_ms":101,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:45+08:00"} +{"bytes":14655,"duration_ms":797,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-27T18:41:46+08:00"} +{"bytes":19367,"duration_ms":136,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:08:37+08:00"} +{"bytes":2074,"duration_ms":411,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:08:38+08:00"} +{"bytes":884,"duration_ms":98,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:08:46+08:00"} +{"bytes":1614,"duration_ms":14,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:08:46+08:00"} +{"bytes":2074,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:09:02+08:00"} +{"bytes":19367,"duration_ms":105,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:09:02+08:00"} +{"bytes":884,"duration_ms":165,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:09:05+08:00"} +{"bytes":1614,"duration_ms":33,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:09:05+08:00"} +{"bytes":925,"duration_ms":64,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:09:05+08:00"} +{"bytes":2074,"duration_ms":239,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:47+08:00"} +{"bytes":19367,"duration_ms":472,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:48+08:00"} +{"bytes":884,"duration_ms":134,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:49+08:00"} +{"bytes":925,"duration_ms":44,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:49+08:00"} +{"bytes":1614,"duration_ms":44,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:49+08:00"} +{"bytes":1221,"duration_ms":97,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:55+08:00"} +{"bytes":1614,"duration_ms":13,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:55+08:00"} +{"bytes":925,"duration_ms":14,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:10:55+08:00"} +{"bytes":2074,"duration_ms":158,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:21+08:00"} +{"bytes":19367,"duration_ms":293,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:21+08:00"} +{"bytes":1221,"duration_ms":138,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:22+08:00"} +{"bytes":925,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:22+08:00"} +{"bytes":1614,"duration_ms":19,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:22+08:00"} +{"bytes":928,"duration_ms":96,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:37+08:00"} +{"bytes":1614,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:37+08:00"} +{"bytes":925,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:37+08:00"} +{"bytes":583,"duration_ms":103,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:44+08:00"} +{"bytes":925,"duration_ms":17,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:44+08:00"} +{"bytes":1086,"duration_ms":145,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:47+08:00"} +{"bytes":925,"duration_ms":48,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:47+08:00"} +{"bytes":1272,"duration_ms":106,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:49+08:00"} +{"bytes":925,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:12:49+08:00"} +{"bytes":1221,"duration_ms":104,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:04+08:00"} +{"bytes":1614,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:04+08:00"} +{"bytes":925,"duration_ms":15,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:04+08:00"} +{"bytes":2074,"duration_ms":98,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:08+08:00"} +{"bytes":19367,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:08+08:00"} +{"bytes":1221,"duration_ms":103,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:10+08:00"} +{"bytes":925,"duration_ms":18,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:10+08:00"} +{"bytes":1614,"duration_ms":18,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:10+08:00"} +{"bytes":121,"duration_ms":255,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:27+08:00"} +{"bytes":121,"duration_ms":248,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:28+08:00"} +{"bytes":583,"duration_ms":99,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:32+08:00"} +{"bytes":925,"duration_ms":10,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:32+08:00"} +{"bytes":1086,"duration_ms":96,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:39+08:00"} +{"bytes":925,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:13:39+08:00"} +{"bytes":19367,"duration_ms":78,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:18+08:00"} +{"bytes":2074,"duration_ms":95,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:18+08:00"} +{"bytes":1086,"duration_ms":102,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:21+08:00"} +{"bytes":925,"duration_ms":16,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:21+08:00"} +{"bytes":1272,"duration_ms":97,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:24+08:00"} +{"bytes":925,"duration_ms":8,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:24+08:00"} +{"bytes":583,"duration_ms":98,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:41+08:00"} +{"bytes":925,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:41+08:00"} +{"bytes":14631,"duration_ms":793,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:52+08:00"} +{"bytes":14631,"duration_ms":835,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:53+08:00"} +{"bytes":884,"duration_ms":99,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:54+08:00"} +{"bytes":1614,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:54+08:00"} +{"bytes":925,"duration_ms":7,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:54+08:00"} +{"bytes":2074,"duration_ms":110,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:59+08:00"} +{"bytes":19367,"duration_ms":122,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:18:59+08:00"} +{"bytes":2074,"duration_ms":105,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:29:57+08:00"} +{"bytes":19367,"duration_ms":133,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:29:57+08:00"} +{"bytes":583,"duration_ms":98,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:29:59+08:00"} +{"bytes":925,"duration_ms":11,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:29:59+08:00"} +{"bytes":884,"duration_ms":162,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:09+08:00"} +{"bytes":925,"duration_ms":18,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:09+08:00"} +{"bytes":1614,"duration_ms":19,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:09+08:00"} +{"bytes":583,"duration_ms":133,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:11+08:00"} +{"bytes":925,"duration_ms":44,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:11+08:00"} +{"bytes":1086,"duration_ms":110,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:13+08:00"} +{"bytes":925,"duration_ms":54,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:13+08:00"} +{"bytes":583,"duration_ms":104,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:46+08:00"} +{"bytes":925,"duration_ms":15,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:46+08:00"} +{"bytes":583,"duration_ms":124,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:56+08:00"} +{"bytes":925,"duration_ms":27,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:30:56+08:00"} +{"bytes":2074,"duration_ms":111,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:23+08:00"} +{"bytes":19367,"duration_ms":228,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:23+08:00"} +{"bytes":1086,"duration_ms":109,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:26+08:00"} +{"bytes":925,"duration_ms":22,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:26+08:00"} +{"bytes":1086,"duration_ms":119,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:35+08:00"} +{"bytes":925,"duration_ms":9,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:35+08:00"} +{"bytes":2074,"duration_ms":106,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:38+08:00"} +{"bytes":19367,"duration_ms":210,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:32:39+08:00"} +{"bytes":1221,"duration_ms":102,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:02+08:00"} +{"bytes":1614,"duration_ms":38,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:02+08:00"} +{"bytes":925,"duration_ms":39,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:02+08:00"} +{"bytes":583,"duration_ms":106,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:31+08:00"} +{"bytes":925,"duration_ms":8,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:31+08:00"} +{"bytes":19367,"duration_ms":80,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:34+08:00"} +{"bytes":2074,"duration_ms":101,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:34+08:00"} +{"bytes":1086,"duration_ms":102,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:35+08:00"} +{"bytes":925,"duration_ms":12,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:35+08:00"} +{"bytes":884,"duration_ms":106,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:47+08:00"} +{"bytes":925,"duration_ms":23,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:47+08:00"} +{"bytes":1614,"duration_ms":23,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:47+08:00"} +{"bytes":583,"duration_ms":105,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:50+08:00"} +{"bytes":925,"duration_ms":24,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:33:50+08:00"} +{"bytes":1086,"duration_ms":119,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:35:35+08:00"} +{"bytes":925,"duration_ms":18,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:35:35+08:00"} +{"bytes":2074,"duration_ms":114,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:35:39+08:00"} +{"bytes":19367,"duration_ms":208,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:35:39+08:00"} +{"bytes":1086,"duration_ms":95,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:35:41+08:00"} +{"bytes":2074,"duration_ms":109,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:04+08:00"} +{"bytes":19367,"duration_ms":166,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:04+08:00"} +{"bytes":1086,"duration_ms":243,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:06+08:00"} +{"bytes":2074,"duration_ms":99,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:13+08:00"} +{"bytes":19367,"duration_ms":102,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:13+08:00"} +{"bytes":583,"duration_ms":100,"kind":"access","level":"INFO","method":"","path":"","query":"","remote":"","status":200,"trace_id":"","ts":"2025-11-28T09:39:15+08:00"} diff --git a/server/storage/export/job_48_1764301730.zip b/server/storage/export/job_48_1764301730.zip new file mode 100644 index 0000000..42028f4 Binary files /dev/null and b/server/storage/export/job_48_1764301730.zip differ diff --git a/server/storage/export/job_51_1764302128.zip b/server/storage/export/job_51_1764302128.zip new file mode 100644 index 0000000..9587c88 Binary files /dev/null and b/server/storage/export/job_51_1764302128.zip differ diff --git a/server/storage/export/job_54_1764302344.zip b/server/storage/export/job_54_1764302344.zip new file mode 100644 index 0000000..543ebb1 Binary files /dev/null and b/server/storage/export/job_54_1764302344.zip differ diff --git a/server/storage/export/job_55_1764308184.zip b/server/storage/export/job_55_1764308184.zip new file mode 100644 index 0000000..448fea2 Binary files /dev/null and b/server/storage/export/job_55_1764308184.zip differ diff --git a/server/storage/export/job_56_1764308297.zip b/server/storage/export/job_56_1764308297.zip new file mode 100644 index 0000000..271da27 Binary files /dev/null and b/server/storage/export/job_56_1764308297.zip differ diff --git a/server/storage/export_20251128114848.xlsx b/server/storage/export_20251128114848.xlsx new file mode 100755 index 0000000..d72c148 Binary files /dev/null and b/server/storage/export_20251128114848.xlsx differ diff --git a/server/storage/export_20251128115528.csv b/server/storage/export_20251128115528.csv new file mode 100644 index 0000000..f5a55b6 --- /dev/null +++ b/server/storage/export_20251128115528.csv @@ -0,0 +1 @@ +订单编号,KEY,支付流水号,订单类型,账号,合同单价,数量,总金额,支付金额,支付方式,支付状态,充值时间,创建时间,更新时间,计划标题,分销商名称,商品名称,官方价,成本价,批次名称,标题,订单状态,商户业务号,是否使用优惠券 diff --git a/server/storage/export_20251128115902.xlsx b/server/storage/export_20251128115902.xlsx new file mode 100755 index 0000000..74cb7be Binary files /dev/null and b/server/storage/export_20251128115902.xlsx differ diff --git a/server/storage/export_20251128133623.xlsx b/server/storage/export_20251128133623.xlsx new file mode 100755 index 0000000..5e9f4f6 Binary files /dev/null and b/server/storage/export_20251128133623.xlsx differ diff --git a/server/storage/export_20251128133814.xlsx b/server/storage/export_20251128133814.xlsx new file mode 100755 index 0000000..4e6b56c Binary files /dev/null and b/server/storage/export_20251128133814.xlsx differ diff --git a/web/index.html b/web/index.html index 65b934d..5ad4ab8 100644 --- a/web/index.html +++ b/web/index.html @@ -232,8 +232,30 @@ - 层级筛选 - + + + + + + + + + + + + + + + + + + + + + + + + @@ -242,14 +264,14 @@ - + - + @@ -261,18 +283,11 @@ - - - - - - - - - + + diff --git a/web/main.js b/web/main.js index 124377d..15f508d 100644 --- a/web/main.js +++ b/web/main.js @@ -31,7 +31,7 @@ const { createApp, reactive } = Vue; createWidth: (localStorage.getItem('tplDialogWidth') || '900px'), editWidth: (localStorage.getItem('tplEditDialogWidth') || '900px'), edit: { id: null, name: '', datasource: 'marketing', main_table: 'order', orderType: 1, fieldsSel: [], visibility: 'private', file_format: 'xlsx' }, - exportForm: { tplId: null, datasource: 'marketing', file_format: 'xlsx', dateRange: [], creatorIds: [], creatorIdsRaw: '', resellerId: null, planId: null, keyBatchId: null, codeBatchId: null, productId: null, outTradeNo: '', account: '', cashActivityId: '', voucherChannelActivityId: '', voucherBatchChannelActivityId: '', outBizNo: '' }, + exportForm: { tplId: null, datasource: 'marketing', file_format: 'xlsx', dateRange: [], creatorIds: [], creatorIdsRaw: '', resellerId: null, planId: null, keyBatchId: null, codeBatchId: null, productId: null, outTradeNo: '', account: '', voucherChannelActivityId: '', outBizNo: '', ymtCreatorId: '', ymtMerchantId: '', ymtActivityId: '' }, exportTpl: { id: null, filters: {}, main_table: '', fields: [], datasource: '', file_format: '' } }) @@ -233,7 +233,7 @@ const { createApp, reactive } = Vue; ], order_voucher: [ { value: 'channel', label: '渠道' }, - { value: 'channel_activity_id', label: '渠道立减金批次' }, + { value: 'channel_batch_no', label: '渠道立减金批次' }, { value: 'channel_voucher_id', label: '渠道立减金ID' }, { value: 'status', label: '状态' }, { value: 'receive_mode', label: '领取方式' }, @@ -820,8 +820,11 @@ const { createApp, reactive } = Vue; return v || '' } const creatorOptions = Vue.ref([]) + const ymtCreatorOptions = Vue.ref([]) const resellerOptions = Vue.ref([]) const planOptions = Vue.ref([]) + const ymtMerchantOptions = Vue.ref([]) + const ymtActivityOptions = 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) @@ -835,6 +838,14 @@ const { createApp, reactive } = Vue; creatorOptions.value = arr.map(it=>({label: it.name || String(it.id), value: Number(it.id)})) }catch(_e){ creatorOptions.value = [] } } + const loadYmtCreators = async ()=>{ + try{ + const res = await fetch(API_BASE + '/api/ymt/users?limit=2000') + const data = await res.json() + const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []) + ymtCreatorOptions.value = arr.map(it=>({ label: it.name || String(it.id), value: Number(it.id) })) + }catch(_e){ ymtCreatorOptions.value = [] } + } const loadResellers = async ()=>{ const ids = Array.isArray(state.exportForm.creatorIds) ? state.exportForm.creatorIds : [] if(!ids.length){ resellerOptions.value = []; return } @@ -845,6 +856,32 @@ const { createApp, reactive } = Vue; resellerOptions.value = arr.map(it=>({label: (it.name||'') + (it.name?'':'') , value: Number(it.id)})) }catch(_e){ resellerOptions.value = [] } } + const loadYmtMerchants = async ()=>{ + const uid = state.exportForm.ymtCreatorId + if(!uid){ ymtMerchantOptions.value = []; return } + try{ + const qs = new URLSearchParams() + qs.set('user_id', String(uid)) + qs.set('limit', '2000') + const res = await fetch(API_BASE + '/api/ymt/merchants?' + qs.toString()) + const data = await res.json() + const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []) + ymtMerchantOptions.value = arr.map(it=>({ label: `${it.id} - ${it.name||''}`, value: Number(it.id) })) + }catch(_e){ ymtMerchantOptions.value = [] } + } + const loadYmtActivities = async ()=>{ + const mid = state.exportForm.ymtMerchantId + if(!mid){ ymtActivityOptions.value = []; return } + try{ + const qs = new URLSearchParams() + qs.set('merchant_id', String(mid)) + qs.set('limit', '2000') + const res = await fetch(API_BASE + '/api/ymt/activities?' + qs.toString()) + const data = await res.json() + const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []) + ymtActivityOptions.value = arr.map(it=>({ label: `${it.id} - ${it.name||''}`, value: Number(it.id) })) + }catch(_e){ ymtActivityOptions.value = [] } + } const loadPlans = async ()=>{ const rid = state.exportForm.resellerId if(!rid){ planOptions.value = []; return } @@ -872,7 +909,8 @@ const { createApp, reactive } = Vue; return [] }) const isOrder = Vue.computed(()=>{ - return (state.exportTpl && state.exportTpl.main_table) === 'order' + const mt = state.exportTpl && state.exportTpl.main_table + return mt === 'order' || mt === 'order_info' }) const orderTypeLabel = (n)=>{ if(n===1) return '直充卡密' @@ -881,7 +919,7 @@ const { createApp, reactive } = Vue; return '' } const sceneLabel = (s)=>{ - if(s==='order') return '订单' + if(s==='order' || s==='order_info') return '订单' return s || '' } const exportTitle = Vue.computed(()=>{ @@ -889,7 +927,7 @@ const { createApp, reactive } = Vue; const mt = state.exportTpl && state.exportTpl.main_table if(mt){ base += ' - ' + sceneLabel(mt) - if(mt==='order'){ + if(mt==='order' || mt==='order_info'){ const list = exportTypeList.value const labels = list.map(orderTypeLabel).filter(Boolean) if(labels.length){ @@ -1020,9 +1058,16 @@ const { createApp, reactive } = Vue; await loadTemplateDetail(row.id) 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() } - const uid = getUserId() - if(uid){ state.exportForm.creatorIds = [ Number(uid) ] } + if(state.exportForm.datasource==='marketing'){ + loadCreators() + const uid = getUserId() + if(uid){ state.exportForm.creatorIds = [ Number(uid) ] } + } + if(state.exportForm.datasource==='ymt'){ + await loadYmtCreators() + await loadYmtMerchants() + await loadYmtActivities() + } if(!Array.isArray(state.exportForm.dateRange) || state.exportForm.dateRange.length!==2){ state.exportForm.dateRange = yearRange() } state.exportVisible = true } @@ -1035,7 +1080,7 @@ const { createApp, reactive } = Vue; state.exportTpl = tpl }catch(e){ msg('加载模板详情异常','error'); state.exportTpl = { id: null, filters: {}, main_table: '', fields: [], datasource: '', file_format: '' } } } - const submitExport = async ()=>{ + const submitExport = async ()=>{ const formRef = exportFormRef.value const ok = formRef ? await formRef.validate().catch(()=>false) : true if(!ok){ msg('请完善必填项','error'); return } @@ -1049,12 +1094,17 @@ const { createApp, reactive } = Vue; if(state.exportForm.resellerId){ filters.reseller_id_eq = Number(state.exportForm.resellerId) } - if(state.exportForm.cashActivityId){ filters.order_cash_cash_activity_id_eq = state.exportForm.cashActivityId } if(state.exportForm.voucherChannelActivityId){ filters.order_voucher_channel_activity_id_eq = state.exportForm.voucherChannelActivityId } - if(state.exportForm.voucherBatchChannelActivityId){ filters.voucher_batch_channel_activity_id_eq = state.exportForm.voucherBatchChannelActivityId } + if(Array.isArray(state.exportForm.creatorIds) && state.exportForm.creatorIds.length){ filters.creator_in = state.exportForm.creatorIds.map(Number) } else if(state.exportForm.creatorIdsRaw){ const arr = String(state.exportForm.creatorIdsRaw).split(',').map(s=>s.trim()).filter(Boolean); if(arr.length){ filters.creator_in = arr } } + // 易码通专用筛选 + if(state.exportForm.datasource==='ymt'){ + if(String(state.exportForm.ymtCreatorId).trim()){ filters.creator_in = [ Number(state.exportForm.ymtCreatorId) ] } + if(String(state.exportForm.ymtMerchantId).trim()){ filters.reseller_id_eq = Number(state.exportForm.ymtMerchantId) } + if(String(state.exportForm.ymtActivityId).trim()){ filters.plan_id_eq = Number(state.exportForm.ymtActivityId) } + } const payload={template_id:Number(id),requested_by:1,permission:{},options:{},filters, file_format: state.exportForm.file_format, datasource: state.exportForm.datasource}; const r=await fetch(API_BASE + '/api/exports' + qsUser(),{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}); @@ -1069,6 +1119,8 @@ const { createApp, reactive } = Vue; } 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; loadResellers() }) + Vue.watch(()=>state.exportForm.ymtCreatorId, ()=>{ state.exportForm.ymtMerchantId=null; state.exportForm.ymtActivityId=null; loadYmtMerchants() }) + Vue.watch(()=>state.exportForm.ymtMerchantId, ()=>{ state.exportForm.ymtActivityId=null; loadYmtActivities() }) Vue.watch(()=>state.exportForm.resellerId, ()=>{ state.exportForm.planId=null; state.exportForm.keyBatchId=null; state.exportForm.codeBatchId=null; state.exportForm.productId=null; loadPlans() }) 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 }) @@ -1301,7 +1353,7 @@ const { createApp, reactive } = Vue; loadTemplates() loadFieldsMeta(state.form.datasource, state.form.orderType) - return { ...Vue.toRefs(state), visibilityOptions, formatOptions, datasourceOptions, fieldOptionsDynamic, editFieldOptionsDynamic, sceneOptions, editSceneOptions, loadTemplates, createTemplate, openExport, submitExport, loadJob, loadJobs, openJobs, closeJobs, download, openSQL, openEdit, saveEdit, removeTemplate, resizeDialog, createRules, exportRules, editRules, createFormRef, exportFormRef, editFormRef, dsLabel, exportType, isOrder, exportTitle, creatorOptions, resellerOptions, planOptions, hasCreators, hasReseller, hasPlan, hasKeyBatch, hasCodeBatch, jobPercent, fmtDT, fieldsCascader, editFieldsCascader, createCascaderRoot, editCascaderRoot, onCascaderVisible, onFieldsSelChange, hasUserId, currentUserId } + return { ...Vue.toRefs(state), visibilityOptions, formatOptions, datasourceOptions, fieldOptionsDynamic, editFieldOptionsDynamic, sceneOptions, editSceneOptions, loadTemplates, createTemplate, openExport, submitExport, loadJob, loadJobs, openJobs, closeJobs, download, openSQL, openEdit, saveEdit, removeTemplate, resizeDialog, createRules, exportRules, editRules, createFormRef, exportFormRef, editFormRef, dsLabel, exportType, isOrder, exportTitle, creatorOptions, ymtCreatorOptions, ymtMerchantOptions, ymtActivityOptions, resellerOptions, planOptions, hasCreators, hasReseller, hasPlan, hasKeyBatch, hasCodeBatch, jobPercent, fmtDT, fieldsCascader, editFieldsCascader, createCascaderRoot, editCascaderRoot, onCascaderVisible, onFieldsSelChange, hasUserId, currentUserId } } }) app.use(ElementPlus)