diff --git a/config/examples/ymt_direct_charge_template.json b/config/examples/ymt_direct_charge_template.json new file mode 100644 index 0000000..c41e8ef --- /dev/null +++ b/config/examples/ymt_direct_charge_template.json @@ -0,0 +1,19 @@ +{ + "name": "易码通-直充卡密订单-示例", + "datasource": "ymt", + "main_table": "order_info", + "fields": [ + "order.order_number", + "order.key", + "order.type", + "order.status", + "order.pay_amount", + "order.create_time", + "merchant.name" + ], + "filters": { + "type_eq": 2 + }, + "file_format": "xlsx", + "visibility": "public" +} diff --git a/docs/test_report_ymt_direct_charge_dedupe.md b/docs/test_report_ymt_direct_charge_dedupe.md new file mode 100644 index 0000000..f714cd5 --- /dev/null +++ b/docs/test_report_ymt_direct_charge_dedupe.md @@ -0,0 +1,21 @@ +# 易码通直充卡密订单导出客户名称去重测试报告 + +## 场景与用例 +- 单客户订单导出:选择单一 `merchant_id`,包含多笔订单 +- 批量客户订单导出:选择多个 `merchant_id` +- 相同客户多笔订单:同一 `merchant_id` 在时间窗口内多笔订单 + +## 验证点 +- 导出文件格式:列头正确、编码 `UTF-8`、首行表头 +- 客户名称列:每行仅出现一次且来源为 `merchant.name` +- 其他订单信息:金额、时间、订单编号等字段完整无误 + +## 结果摘要 +- 前端:仅在立减金场景显示“立减金批次号”,直充卡密不显示 +- 后端:YMT 直充卡密过滤 `order_voucher/*` 与去重 `order.merchant_name` +- SQL 构建:包含 `LEFT JOIN merchant`,输出列使用 `merchant.name` + +## 兼容性与影响 +- YMT 立减金与营销立减金不受影响,仍支持“立减金批次号”筛选与列输出 +- 直充卡密模板示例更新为使用 `merchant.name` + diff --git a/server/internal/api/exports.go b/server/internal/api/exports.go index 9ef178a..c2e5d06 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -189,8 +189,7 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { return } } - // 过滤非法字段,保持列头与查询列一致 - // 计算订单类型(若提供)以便做场景化列过滤 + filtered := make([]string, 0, len(fs)) tv := 0 if v, ok := p.Filters["type_eq"]; ok { switch t := v.(type) { @@ -208,7 +207,6 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { } } } - filtered := make([]string, 0, len(fs)) for _, tf := range fs { if ds == "ymt" && strings.HasPrefix(tf, "order_info.") { tf = strings.Replace(tf, "order_info.", "order.", 1) @@ -216,7 +214,6 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { if ds == "marketing" && tf == "order_voucher.channel_batch_no" { tf = "order_voucher.channel_activity_id" } - // 易码通直充卡密订单不输出立减金相关列 if ds == "ymt" && tv == 2 { if strings.HasPrefix(tf, "order_voucher.") || strings.HasPrefix(tf, "goods_voucher_batch.") || strings.HasPrefix(tf, "goods_voucher_subject_config.") { continue @@ -225,6 +222,22 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { if wl[tf] { filtered = append(filtered, tf) } + } + if ds == "ymt" { + present := map[string]bool{} + for _, f := range filtered { + present[f] = true + } + if present["merchant.name"] && present["order.merchant_name"] { + out := make([]string, 0, len(filtered)) + for _, f := range filtered { + if f == "order.merchant_name" { + continue + } + out = append(out, f) + } + filtered = out + } } // relax: creator_in 非必填,若权限中提供其他边界将被合并为等值过滤 req := exporter.BuildRequest{MainTable: main, Datasource: ds, Fields: filtered, Filters: p.Filters} diff --git a/server/internal/exporter/sqlbuilder.go b/server/internal/exporter/sqlbuilder.go index dfa982f..ae77a0a 100644 --- a/server/internal/exporter/sqlbuilder.go +++ b/server/internal/exporter/sqlbuilder.go @@ -357,8 +357,7 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{ } if v, ok := req.Filters["reseller_id_eq"]; ok { // If merchant_id_in is present, it handles the merchant_id logic (via OR condition), - // so we should skip this strict equality filter to avoid generating "AND merchant_id = '0'". - if _, hasIn := req.Filters["merchant_id_in"]; !hasIn { + if _, hasIn := req.Filters["merchant_id_in"]; !hasIn { s := toString(v) if s != "" { if tbl, col, ok := sch.FilterColumn("reseller_id_eq"); ok { @@ -387,9 +386,7 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{ } } if v, ok := req.Filters["order_voucher_channel_activity_id_eq"]; ok { - // 易码通直充卡密订单不允许使用立减金批次号过滤 if req.Datasource == "ymt" && tv == 2 { - // skip } else { s := toString(v) if s != "" {