MarketingSystemDataExportTool/server/internal/exporter/sqlbuilder.go

777 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package exporter
import (
"encoding/json"
"errors"
"fmt"
"server/internal/constants"
"server/internal/schema"
"server/internal/utils"
"strings"
)
// isZeroID returns true when the value is effectively empty/zero for ID filters.
func isZeroID(v interface{}) bool {
switch t := v.(type) {
case nil:
return true
case string:
s := strings.TrimSpace(t)
return s == "" || s == "0"
case int, int32, int64:
return fmt.Sprint(t) == "0"
case float32, float64:
return fmt.Sprint(t) == "0" || fmt.Sprint(t) == "0.0"
}
return false
}
type BuildRequest struct {
MainTable string
Datasource string
Fields []string // table.field
Filters map[string]interface{}
}
func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{}, error) {
if req.MainTable != "order" && req.MainTable != "order_info" {
return "", nil, errors.New("unsupported main table")
}
sch := schema.Get(req.Datasource, req.MainTable)
if req.Datasource == "marketing" && req.MainTable == "order" {
if v, ok := req.Filters["create_time_between"]; ok {
switch t := v.(type) {
case []interface{}:
if len(t) != 2 {
return "", nil, errors.New("create_time_between 需要两个时间值")
}
case []string:
if len(t) != 2 {
return "", nil, errors.New("create_time_between 需要两个时间值")
}
default:
return "", nil, errors.New("create_time_between 格式错误")
}
} else {
return "", nil, errors.New("缺少时间过滤:必须提供 create_time_between")
}
}
cols := []string{}
need := map[string]bool{}
for _, tf := range req.Fields {
// normalize YMT physical names saved previously to logical names
if req.Datasource == "ymt" && strings.HasPrefix(tf, "order_info.") {
tf = strings.Replace(tf, "order_info.", "order.", 1)
}
if whitelist != nil && !whitelist[tf] {
continue
}
parts := strings.Split(tf, ".")
if len(parts) != 2 {
return "", nil, errors.New("invalid field format")
}
t, f := parts[0], parts[1]
if req.Datasource == "marketing" && t == "order_voucher" && f == "channel_batch_no" {
f = "channel_activity_id"
}
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 req.Datasource == "marketing" && t == "order" && req.MainTable == "order" {
if f == "status" {
cols = append(cols, constants.BuildCaseWhen("order", "status", constants.MarketingOrderStatus, "order.status"))
continue
}
if f == "type" {
cols = append(cols, constants.BuildCaseWhen("order", "type", constants.MarketingOrderType, "order.type"))
continue
}
if f == "pay_type" {
cols = append(cols, constants.BuildCaseWhen("order", "pay_type", constants.MarketingPayType, "order.pay_type"))
continue
}
if f == "pay_status" {
cols = append(cols, constants.BuildCaseWhen("order", "pay_status", constants.MarketingPayStatus, "order.pay_status"))
continue
}
if req.Datasource == "marketing" && f == "card_code" {
cols = append(cols, "CASE WHEN LENGTH(`order`.card_code) > 10 THEN CONCAT(SUBSTRING(`order`.card_code,1,6),'****',SUBSTRING(`order`.card_code, LENGTH(`order`.card_code)-3, 4)) ELSE `order`.card_code END AS `order.card_code`")
continue
}
}
if req.Datasource == "ymt" && t == "order" {
if f == "type" {
cols = append(cols, constants.BuildCaseWhen(mt, "type", constants.YMTOrderType, "order.type"))
continue
}
if f == "recharge_suc_time" {
// 仅在充值成功状态下展示充值成功时间,其余状态展示为空
cols = append(cols, "CASE WHEN `"+mt+"`.status = 3 THEN `"+mt+"`.recharge_suc_time ELSE NULL END AS `order.recharge_suc_time`")
continue
}
if f == "status" {
cols = append(cols, constants.BuildCaseWhen(mt, "status", constants.YMTOrderStatus, "order.status"))
continue
}
if f == "pay_status" {
cols = append(cols, constants.BuildCaseWhen(mt, "pay_status", constants.YMTPayStatus, "order.pay_status"))
continue
}
if f == "is_retry" {
cols = append(cols, constants.BuildCaseWhen(mt, "is_retry", constants.YMTIsRetry, "order.is_retry"))
continue
}
if f == "supplier_name" {
need["supplier"] = true
cols = append(cols, "`supplier`.name AS `order.supplier_name`")
continue
}
if f == "is_inner" {
cols = append(cols, constants.BuildCaseWhen(mt, "is_inner", constants.YMTIsInner, "order.is_inner"))
continue
}
}
if req.Datasource == "ymt" && t == "activity" {
if f == "settlement_type" {
cols = append(cols, constants.BuildCaseWhen(mt, "settlement_type", constants.YMTSettlementType, "activity.settlement_type"))
continue
}
}
if t == "merchant" {
if f == "third_party" {
cols = append(cols, constants.BuildCaseWhen(mt, "third_party", constants.ThirdPartyType, "merchant.third_party"))
continue
}
}
// Generic mapping for order.is_retry across datasources
if t == "order" && f == "is_retry" {
cols = append(cols, constants.BuildCaseWhen(mt, "is_retry", constants.YMTIsRetry, "order.is_retry"))
continue
}
// Generic mapping for order.is_inner across datasources
if t == "order" && f == "is_inner" {
cols = append(cols, constants.BuildCaseWhen(mt, "is_inner", constants.YMTIsInner, "order.is_inner"))
continue
}
if req.Datasource == "ymt" && t == "order_digit" {
if f == "order_type" {
cols = append(cols, constants.BuildCaseWhen(mt, "order_type", constants.OrderDigitOrderType, "order_digit.order_type"))
continue
}
if f == "sms_channel" {
// 短信渠道枚举1=官方2=专票
cols = append(cols, constants.BuildCaseWhen(mt, "sms_channel", constants.OrderDigitSmsChannel, "order_digit.sms_channel"))
continue
}
}
if t == "order_cash" && f == "receive_status" {
// 营销与易码通枚举不同,按数据源分别映射
if req.Datasource == "ymt" {
cols = append(cols, "CASE `order_cash`.receive_status WHEN 1 THEN '待领取' WHEN 2 THEN '领取中' WHEN 3 THEN '领取成功' WHEN 4 THEN '领取失败' ELSE '' END AS `order_cash.receive_status`")
} else {
cols = append(cols, "CASE `order_cash`.receive_status WHEN 0 THEN '待领取' WHEN 1 THEN '领取中' WHEN 2 THEN '领取成功' WHEN 3 THEN '领取失败' ELSE '' END AS `order_cash.receive_status`")
}
continue
}
// YMT 的 order_cash 表无 is_confirm 字段,输出占位常量
if req.Datasource == "ymt" && t == "order_cash" && f == "is_confirm" {
cols = append(cols, "0 AS `order_cash.is_confirm`")
continue
}
if t == "order_cash" && f == "channel" {
cols = append(cols, "CASE `order_cash`.channel WHEN 1 THEN '支付宝' WHEN 2 THEN '微信' WHEN 3 THEN '云闪付' ELSE '' END AS `order_cash.channel`")
continue
}
if t == "order_voucher" && f == "channel" {
cols = append(cols, "CASE `order_voucher`.channel WHEN 1 THEN '支付宝' WHEN 2 THEN '微信' WHEN 3 THEN '云闪付' ELSE '' END AS `order_voucher.channel`")
continue
}
if req.Datasource == "ymt" && t == "order_voucher" && f == "status" {
cols = append(cols, "CASE `order_voucher`.status WHEN 1 THEN '待发放' WHEN 2 THEN '发放中' WHEN 3 THEN '发放失败' WHEN 4 THEN '待核销' WHEN 5 THEN '已核销' WHEN 6 THEN '已过期' WHEN 7 THEN '已退款' ELSE '' END AS `order_voucher.status`")
continue
}
if t == "order_voucher" && f == "status" {
cols = append(cols, "CASE `order_voucher`.status WHEN 1 THEN '可用' WHEN 2 THEN '已实扣' WHEN 3 THEN '已过期' WHEN 4 THEN '已退款' WHEN 5 THEN '领取失败' WHEN 6 THEN '发放中' WHEN 7 THEN '部分退款' WHEN 8 THEN '已退回' WHEN 9 THEN '发放失败' ELSE '' END AS `order_voucher.status`")
continue
}
if t == "order_voucher" && f == "receive_mode" {
cols = append(cols, "CASE `order_voucher`.receive_mode WHEN 1 THEN '渠道授权用户id' WHEN 2 THEN '手机号或邮箱' ELSE '' END AS `order_voucher.receive_mode`")
continue
}
if t == "order_voucher" && f == "out_biz_no" {
cols = append(cols, "'' AS `order_voucher.out_biz_no`")
continue
}
// Fallback for YMT tables that are not joined in schema: voucher, voucher_batch, merchant_key_send
if req.Datasource == "ymt" && (t == "voucher" || t == "voucher_batch" || t == "merchant_key_send") {
cols = append(cols, "'' AS `"+t+"."+f+"`")
continue
}
cols = append(cols, "`"+mt+"`."+escape(mf)+" AS `"+t+"."+f+"`")
}
if len(cols) == 0 {
return "", nil, errors.New("no fields")
}
sb := strings.Builder{}
baseCols := strings.Join(cols, ",")
sb.WriteString("SELECT ")
sb.WriteString(baseCols)
sb.WriteString(" FROM `" + sch.TableName(req.MainTable) + "`")
args := []interface{}{}
where := []string{}
// collect need from filters referencing related tables
if _, ok := req.Filters["order_cash_cash_activity_id_eq"]; ok {
need["order_cash"] = true
}
if _, ok := req.Filters["order_voucher_channel_activity_id_eq"]; ok {
need["order_voucher"] = true
}
if _, ok := req.Filters["voucher_batch_channel_activity_id_eq"]; ok {
need["voucher_batch"] = true
need["voucher"] = true
need["order_voucher"] = true
}
if _, ok := req.Filters["merchant_out_biz_no_eq"]; ok {
need["merchant_key_send"] = true
}
// normalize merchant_id_eq: skip 0/空,非零转为 merchant_id_in 以便和 creator_in 做 OR
if v, ok := req.Filters["merchant_id_eq"]; ok {
if isZeroID(v) {
delete(req.Filters, "merchant_id_eq")
} else {
if _, has := req.Filters["merchant_id_in"]; !has {
req.Filters["merchant_id_in"] = []interface{}{v}
}
delete(req.Filters, "merchant_id_eq")
}
}
// normalize reseller_id_eq (YMT 直连 merchant_id)
if req.Datasource == "ymt" && (req.MainTable == "order" || req.MainTable == "order_info") {
if v, ok := req.Filters["reseller_id_eq"]; ok {
if isZeroID(v) {
delete(req.Filters, "reseller_id_eq")
} else {
if _, has := req.Filters["merchant_id_in"]; !has {
req.Filters["merchant_id_in"] = []interface{}{v}
}
delete(req.Filters, "reseller_id_eq")
}
}
}
// helper: treat "0"/0/空 as未提供的 ID
isZeroID := func(v interface{}) bool {
switch t := v.(type) {
case nil:
return true
case string:
s := strings.TrimSpace(t)
return s == "" || s == "0"
case int, int32, int64:
return fmt.Sprint(t) == "0"
case float32, float64:
return fmt.Sprint(t) == "0" || fmt.Sprint(t) == "0.0"
}
return false
}
// Handle creator_in and merchant_id_in with OR logic if both exist
var creatorArgs []interface{}
hasCreator := false
if v, ok := req.Filters["creator_in"]; ok {
switch t := v.(type) {
case []interface{}:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
case []int:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
case []string:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
}
if len(creatorArgs) > 0 {
hasCreator = true
}
}
var merchantArgs []interface{}
hasMerchant := false
if v, ok := req.Filters["merchant_id_in"]; ok {
switch t := v.(type) {
case []interface{}:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
case []int:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
case []string:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
}
if len(merchantArgs) > 0 {
hasMerchant = true
}
}
// Apply the logic: if both present, use OR. Else use individual.
if hasCreator && hasMerchant {
cTbl, cCol, cOk := sch.FilterColumn("creator_in")
mTbl, mCol, mOk := sch.FilterColumn("merchant_id_in")
if cOk && mOk {
cPh := strings.Repeat("?,", len(creatorArgs))
cPh = strings.TrimSuffix(cPh, ",")
mPh := strings.Repeat("?,", len(merchantArgs))
mPh = strings.TrimSuffix(mPh, ",")
where = append(where, fmt.Sprintf("(`%s`.%s IN (%s) OR `%s`.%s IN (%s))",
sch.TableName(cTbl), escape(cCol), cPh,
sch.TableName(mTbl), escape(mCol), mPh))
args = append(args, creatorArgs...)
args = append(args, merchantArgs...)
} else if cOk {
// Fallback: only creator valid (e.g. marketing system)
ph := strings.Repeat("?,", len(creatorArgs))
ph = strings.TrimSuffix(ph, ",")
where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(cTbl), escape(cCol), ph))
args = append(args, creatorArgs...)
}
} else if hasCreator {
if tbl, col, ok := sch.FilterColumn("creator_in"); ok {
ph := strings.Repeat("?,", len(creatorArgs))
ph = strings.TrimSuffix(ph, ",")
where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph))
args = append(args, creatorArgs...)
}
}
if v, ok := req.Filters["create_time_between"]; ok {
var arr []interface{}
b, _ := json.Marshal(v)
json.Unmarshal(b, &arr)
if len(arr) != 2 {
return "", nil, errors.New("create_time_between requires 2 values")
}
if tbl, col, ok := sch.FilterColumn("create_time_between"); ok {
where = append(where, fmt.Sprintf("`%s`.%s BETWEEN ? AND ?", sch.TableName(tbl), escape(col)))
}
args = append(args, arr[0], arr[1])
}
var tv int
if v, ok := req.Filters["type_eq"]; ok {
switch t := v.(type) {
case float64:
tv = int(t)
case int:
tv = t
case string:
// simple digits parsing and label mapping
_s := strings.TrimSpace(t)
for i := 0; i < len(_s); i++ {
c := _s[i]
if c < '0' || c > '9' {
continue
}
tv = tv*10 + int(c-'0')
}
if tv == 0 {
if req.Datasource == "ymt" {
if _s == "红包订单" {
tv = 1
}
if _s == "直充卡密订单" {
tv = 2
}
if _s == "立减金订单" {
tv = 3
}
} else {
if _s == "直充卡密" {
tv = 1
}
if _s == "立减金" {
tv = 2
}
if _s == "红包" {
tv = 3
}
}
}
}
if tv == 1 || tv == 2 || tv == 3 {
if tbl, col, ok := sch.FilterColumn("type_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, tv)
}
}
if v, ok := req.Filters["out_trade_no_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("out_trade_no_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["account_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("account_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["plan_id_eq"]; ok {
s := utils.ToString(v)
if s != "" && s != "0" {
if tbl, col, ok := sch.FilterColumn("plan_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["key_batch_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("key_batch_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["product_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("product_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["reseller_id_eq"]; ok {
// If merchant_id_in is present, it handles the merchant_id logic (via OR condition),
if _, hasIn := req.Filters["merchant_id_in"]; !hasIn {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("reseller_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
}
if v, ok := req.Filters["code_batch_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("code_batch_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["order_cash_cash_activity_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("order_cash_cash_activity_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["order_voucher_channel_activity_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("order_voucher_channel_activity_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
if v, ok := req.Filters["voucher_batch_channel_activity_id_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("voucher_batch_channel_activity_id_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
args = append(args, s)
}
}
}
if v, ok := req.Filters["merchant_out_biz_no_eq"]; ok {
s := utils.ToString(v)
if s != "" {
if tbl, col, ok := sch.FilterColumn("merchant_out_biz_no_eq"); ok {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
}
args = append(args, s)
}
}
// append necessary joins after collecting filter-driven needs
joins := sch.BuildJoins(need, req.MainTable)
for _, j := range joins {
sb.WriteString(j)
}
if len(where) > 0 {
sb.WriteString(" WHERE ")
sb.WriteString(strings.Join(where, " AND "))
}
needDedupe := false
if req.Datasource == "marketing" && req.MainTable == "order" && (need["order_voucher"] || need["voucher"] || need["voucher_batch"] || need["key_batch"] || need["code_batch"]) {
needDedupe = true
}
if req.Datasource == "ymt" && (req.MainTable == "order" || req.MainTable == "order_info") && (need["order_digit"] || need["order_voucher"] || need["voucher"] || need["voucher_batch"]) {
needDedupe = true
}
if needDedupe {
// Extract alias names in order
aliases := make([]string, 0, len(cols))
sources := make([]string, 0, len(cols))
for _, c := range cols {
pos := strings.LastIndex(c, " AS `")
if pos < 0 {
continue
}
alias := c[pos+5:]
if len(alias) == 0 {
continue
}
if alias[len(alias)-1] == '`' {
alias = alias[:len(alias)-1]
}
aliases = append(aliases, alias)
parts := strings.Split(alias, ".")
src := ""
if len(parts) >= 1 {
src = parts[0]
}
sources = append(sources, src)
}
// 聚合时按别名分组:主表字段别名始终是 req.MainTable.order_number
pkAlias := req.MainTable + ".order_number"
var out strings.Builder
out.WriteString("SELECT ")
for i := range aliases {
if i > 0 {
out.WriteString(",")
}
alias := aliases[i]
src := sources[i]
if src == "order" {
out.WriteString("sub.`" + alias + "`")
} else {
out.WriteString("MIN(sub.`" + alias + "`) AS `" + alias + "`")
}
}
out.WriteString(" FROM (")
out.WriteString(sb.String())
out.WriteString(") AS sub GROUP BY sub.`" + pkAlias + "`")
return out.String(), args, nil
}
return sb.String(), args, nil
}
func escape(s string) string {
if s == "key" {
return "`key`"
}
if s == "index" {
return "`index`"
}
return s
}
// BuildCountSQL: minimal COUNT for filters-only joins, counting distinct main PK to avoid 1:N duplication
func BuildCountSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{}, error) {
if req.MainTable != "order" && req.MainTable != "order_info" {
return "", nil, errors.New("unsupported main table")
}
sch := schema.Get(req.Datasource, req.MainTable)
mt := sch.TableName(req.MainTable)
pkCol, _ := sch.MapField(req.MainTable, "order_number")
args := []interface{}{}
where := []string{}
need := map[string]bool{}
// mark joins only needed by filters
for k, _ := range req.Filters {
if tbl, _, ok := sch.FilterColumn(k); ok {
need[tbl] = true
}
}
// normalize merchant_id_eq / reseller_id_eq for YMT: skip 0/空, 非零转为 merchant_id_in 以便与 creator_in 做 OR
if v, ok := req.Filters["merchant_id_eq"]; ok {
if isZeroID(v) {
delete(req.Filters, "merchant_id_eq")
} else {
if _, has := req.Filters["merchant_id_in"]; !has {
req.Filters["merchant_id_in"] = []interface{}{v}
}
delete(req.Filters, "merchant_id_eq")
}
}
if req.Datasource == "ymt" && (req.MainTable == "order" || req.MainTable == "order_info") {
if v, ok := req.Filters["reseller_id_eq"]; ok {
if isZeroID(v) {
delete(req.Filters, "reseller_id_eq")
} else {
if _, has := req.Filters["merchant_id_in"]; !has {
req.Filters["merchant_id_in"] = []interface{}{v}
}
delete(req.Filters, "reseller_id_eq")
}
}
}
// Handle creator_in and merchant_id_in with OR logic if both exist
var creatorArgs []interface{}
hasCreator := false
if v, ok := req.Filters["creator_in"]; ok {
switch t := v.(type) {
case []interface{}:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
case []int:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
case []string:
for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
}
if len(creatorArgs) > 0 {
hasCreator = true
}
}
var merchantArgs []interface{}
hasMerchant := false
if v, ok := req.Filters["merchant_id_in"]; ok {
switch t := v.(type) {
case []interface{}:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
case []int:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
case []string:
for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
}
if len(merchantArgs) > 0 {
hasMerchant = true
}
}
if hasCreator && hasMerchant {
cTbl, cCol, cOk := sch.FilterColumn("creator_in")
mTbl, mCol, mOk := sch.FilterColumn("merchant_id_in")
if cOk && mOk {
cPh := strings.Repeat("?,", len(creatorArgs))
cPh = strings.TrimSuffix(cPh, ",")
mPh := strings.Repeat("?,", len(merchantArgs))
mPh = strings.TrimSuffix(mPh, ",")
where = append(where, fmt.Sprintf("(`%s`.%s IN (%s) OR `%s`.%s IN (%s))",
sch.TableName(cTbl), escape(cCol), cPh,
sch.TableName(mTbl), escape(mCol), mPh))
args = append(args, creatorArgs...)
args = append(args, merchantArgs...)
} else if cOk {
ph := strings.Repeat("?,", len(creatorArgs))
ph = strings.TrimSuffix(ph, ",")
where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(cTbl), escape(cCol), ph))
args = append(args, creatorArgs...)
}
} else if hasCreator {
if tbl, col, ok := sch.FilterColumn("creator_in"); ok {
ph := strings.Repeat("?,", len(creatorArgs))
ph = strings.TrimSuffix(ph, ",")
where = append(where, fmt.Sprintf("`%s`.%s IN (%s)", sch.TableName(tbl), escape(col), ph))
args = append(args, creatorArgs...)
}
}
// build WHERE from other filters
for k, v := range req.Filters {
if k == "creator_in" || k == "merchant_id_in" {
continue
}
if k == "reseller_id_eq" {
if _, has := req.Filters["merchant_id_in"]; has {
continue
}
}
if k == "plan_id_eq" && utils.ToString(v) == "0" {
continue
}
if k == "merchant_id_eq" && isZeroID(v) {
continue
}
if tbl, col, ok := sch.FilterColumn(k); ok {
switch k {
case "create_time_between":
var arr []interface{}
b, _ := json.Marshal(v)
_ = json.Unmarshal(b, &arr)
if len(arr) == 2 {
where = append(where, fmt.Sprintf("`%s`.%s BETWEEN ? AND ?", sch.TableName(tbl), escape(col)))
args = append(args, arr[0], arr[1])
}
default:
s := utils.ToString(v)
if s != "" {
where = append(where, fmt.Sprintf("`%s`.%s = ?", sch.TableName(tbl), escape(col)))
args = append(args, s)
}
}
}
}
sb := strings.Builder{}
sb.WriteString("SELECT COUNT(DISTINCT `" + mt + "`." + pkCol + ") FROM `" + mt + "`")
for _, j := range sch.BuildJoins(need, req.MainTable) {
sb.WriteString(j)
}
if len(where) > 0 {
sb.WriteString(" WHERE ")
sb.WriteString(strings.Join(where, " AND "))
}
return sb.String(), args, nil
}