package exporter import ( "encoding/json" "errors" "strings" ) type BuildRequest struct { MainTable string Fields []string // table.field Filters map[string]interface{} } func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{}, error) { if req.MainTable != "order" { return "", nil, errors.New("unsupported main table") } cols := []string{} need := map[string]bool{} for _, tf := range req.Fields { if !whitelist[tf] { return "", nil, errors.New("field not allowed") } parts := strings.Split(tf, ".") if len(parts) != 2 { return "", nil, errors.New("invalid field format") } t, f := parts[0], parts[1] need[t] = true if t == "order" { cols = append(cols, "`order`."+escape(f)) } else { cols = append(cols, "`"+t+"`."+escape(f)) } } if len(cols) == 0 { return "", nil, errors.New("no fields") } sb := strings.Builder{} sb.WriteString("SELECT ") sb.WriteString(strings.Join(cols, ",")) sb.WriteString(" FROM `order`") // JOINs based on need // order_detail if need["order_detail"] { sb.WriteString(" LEFT JOIN `order_detail` ON `order_detail`.order_number = `order`.order_number") } // order_cash if need["order_cash"] { sb.WriteString(" LEFT JOIN `order_cash` ON `order_cash`.order_number = `order`.order_number") } // order_voucher if need["order_voucher"] { sb.WriteString(" LEFT JOIN `order_voucher` ON `order_voucher`.order_number = `order`.order_number") } // plan if need["plan"] || need["key_batch"] { sb.WriteString(" LEFT JOIN `plan` ON `plan`.id = `order`.plan_id") } // key_batch depends on plan if need["key_batch"] { sb.WriteString(" LEFT JOIN `key_batch` ON `key_batch`.plan_id = `plan`.id") } // code_batch depends on key_batch if need["code_batch"] { sb.WriteString(" LEFT JOIN `code_batch` ON `code_batch`.key_batch_id = `key_batch`.id") } // voucher depends on order_voucher if need["voucher"] { sb.WriteString(" LEFT JOIN `voucher` ON `voucher`.channel_activity_id = `order_voucher`.channel_activity_id") } // voucher_batch depends on voucher if need["voucher_batch"] { sb.WriteString(" LEFT JOIN `voucher_batch` ON `voucher_batch`.voucher_id = `voucher`.id") } // merchant_key_send depends on order.key if need["merchant_key_send"] { sb.WriteString(" LEFT JOIN `merchant_key_send` ON `order`." + escape("key") + " = `merchant_key_send`.key") } args := []interface{}{} where := []string{} if v, ok := req.Filters["creator_in"]; ok { ids := []interface{}{} switch t := v.(type) { case []interface{}: ids = t case []int: for _, x := range t { ids = append(ids, x) } case []string: for _, x := range t { ids = append(ids, x) } } if len(ids) == 0 { return "", nil, errors.New("creator_in required") } ph := strings.Repeat("?,", len(ids)) ph = strings.TrimSuffix(ph, ",") where = append(where, "`order`.creator IN ("+ph+")") args = append(args, ids...) } 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") } where = append(where, "`order`.create_time BETWEEN ? AND ?") args = append(args, arr[0], arr[1]) } if v, ok := req.Filters["type_eq"]; ok { var tv int switch t := v.(type) { case float64: tv = int(t) case int: tv = t case string: // simple digits parsing for i := 0; i < len(t); i++ { c := t[i]; if c<'0'||c>'9' { continue }; tv = tv*10 + int(c-'0') } } if tv == 1 || tv == 2 || tv == 3 { where = append(where, "`order`.type = ?") args = append(args, tv) } } if len(where) > 0 { sb.WriteString(" WHERE ") sb.WriteString(strings.Join(where, " AND ")) } return sb.String(), args, nil } func escape(s string) string { if s == "key" { return "`key`" } return s }