From bb55adb1b9ccf357e06ccad2b8d7ad9bcf6383c9 Mon Sep 17 00:00:00 2001 From: zhouyonggao <1971162852@qq.com> Date: Tue, 7 Apr 2026 17:01:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(api):=20=E4=BF=AE=E5=A4=8D=20merchant=5Fid?= =?UTF-8?q?=20=E8=BF=87=E6=BB=A4=E6=9D=A1=E4=BB=B6=E5=86=B2=E7=AA=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 当存在非零 reseller_id_eq 时,跳过 merchant_id_in 过滤以避免条件冲突 - 兼容 YMT 数据源 merchant_id 与 reseller_id 的映射关系 - 在 SQL 构建器中新增对 merchant_id_in 的支持,保证查询过滤生效 - 优化 URL 参数解析逻辑,避免无效或重复的过滤条件注入 --- server/internal/api/exports.go | 41 ++++++++++++++++---------- server/internal/exporter/sqlbuilder.go | 14 +++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/server/internal/api/exports.go b/server/internal/api/exports.go index 55fb03d..c8260f9 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -156,23 +156,34 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { // 注意:不再从 URL 参数 userId 或 current_user_id 自动转换为 creator_in 过滤 // current_user_id 仅用于记录导出任务的 owner,不用于数据过滤 // support multiple merchantId in query: e.g., merchantId=1,2,3 → filters.merchant_id_in + // 注意:对于 YMT 数据源,reseller_id_eq 和 merchant_id_in 映射到同一物理列 order_info.merchant_id, + // 当 reseller_id_eq 已存在且非零时,跳过 merchantId URL 参数注入以避免冲突导致过滤条件丢失 { - midStr := r.URL.Query().Get("merchantId") - if midStr != "" { - parts := strings.Split(midStr, ",") - ids := make([]interface{}, 0, len(parts)) - for _, s := range parts { - s = strings.TrimSpace(s) - if s == "" { - continue + skipMerchantIdIn := false + // 当 reseller_id_eq 已存在且非零时,跳过 merchantId URL 参数注入 + // YMT: reseller_id_eq 和 merchant_id_in 映射同一物理列 order_info.merchant_id,注入会导致冲突 + // Marketing: merchant_id_in 未在 schema 中定义,但其存在会阻止 reseller_id_eq 的应用 + if v, ok := p.Filters["reseller_id_eq"]; ok && v != nil && v != "" && v != 0 { + skipMerchantIdIn = true + } + if !skipMerchantIdIn { + midStr := r.URL.Query().Get("merchantId") + if midStr != "" { + parts := strings.Split(midStr, ",") + ids := make([]interface{}, 0, len(parts)) + for _, s := range parts { + s = strings.TrimSpace(s) + if s == "" { + continue + } + if n, err := strconv.ParseUint(s, 10, 64); err == nil { + ids = append(ids, n) + } } - if n, err := strconv.ParseUint(s, 10, 64); err == nil { - ids = append(ids, n) - } - } - if len(ids) > 0 { - if _, exists := p.Filters["merchant_id_in"]; !exists { - p.Filters["merchant_id_in"] = ids + if len(ids) > 0 { + if _, exists := p.Filters["merchant_id_in"]; !exists { + p.Filters["merchant_id_in"] = ids + } } } } diff --git a/server/internal/exporter/sqlbuilder.go b/server/internal/exporter/sqlbuilder.go index 041d0aa..908d74d 100644 --- a/server/internal/exporter/sqlbuilder.go +++ b/server/internal/exporter/sqlbuilder.go @@ -317,6 +317,13 @@ func BuildSQLWithFields(req BuildRequest, whitelist map[string]bool) (string, [] where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph)) args = append(args, creatorArgs...) } + } else if hasMerchant { + if tbl, col, ok := sch.FilterColumn("merchant_id_in"); ok { + ph := strings.Repeat("?,", len(merchantArgs)) + ph = strings.TrimSuffix(ph, ",") + where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph)) + args = append(args, merchantArgs...) + } } if v, ok := req.Filters["create_time_between"]; ok { @@ -647,6 +654,13 @@ func BuildCountSQL(req BuildRequest, whitelist map[string]bool) (string, []inter where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph)) args = append(args, creatorArgs...) } + } else if hasMerchant { + if tbl, col, ok := sch.FilterColumn("merchant_id_in"); ok { + ph := strings.Repeat("?,", len(merchantArgs)) + ph = strings.TrimSuffix(ph, ",") + where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph)) + args = append(args, merchantArgs...) + } } // build WHERE from other filters