MarketingSystemDataExportTool/server/internal/api/metadata.go

213 lines
6.1 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 api
import (
"database/sql"
"net/http"
"sort"
"strings"
"server/internal/schema"
)
func MetadataHandler(meta, marketing, ymt *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ds := r.URL.Query().Get("datasource")
ot := r.URL.Query().Get("order_type")
db := marketing
if ds == "ymt" {
db = ymt
}
// 从代码中获取隐藏字段列表
hiddenFields := getHiddenFieldsFromCode(ds)
tables := []string{}
if ds == "ymt" {
tables = []string{"order_info", "order_cash", "order_voucher", "order_digit", "goods_voucher_batch", "goods_voucher_subject_config", "merchant", "activity"}
} else {
tables = []string{"order", "order_detail", "order_cash", "order_voucher", "plan", "key_batch", "code_batch", "voucher", "voucher_batch", "merchant_key_send"}
}
out := []map[string]interface{}{}
for _, tbl := range tables {
cols := getColumns(db, tbl)
fields := []map[string]string{}
for _, c := range cols {
tCanonical, fCanonical := canonicalField(ds, tbl, c.Name)
if tCanonical == "" || fCanonical == "" {
continue
}
// 检查字段是否在隐藏列表中
if isFieldHidden(hiddenFields, tCanonical, fCanonical) {
continue
}
lab := c.Comment
if lab == "" {
lab = fCanonical
}
fields = append(fields, map[string]string{"key": tCanonical + "." + fCanonical, "field": fCanonical, "label": lab})
}
tDisplay := displayTable(ds, tbl)
out = append(out, map[string]interface{}{"table": tDisplay, "label": tableLabel(tDisplay), "fields": fields})
}
sort.Slice(out, func(i, j int) bool { return out[i]["table"].(string) < out[j]["table"].(string) })
rec := recommendedDefaults(ds, ot)
ok(w, r, map[string]interface{}{"datasource": ds, "tables": out, "recommended": rec})
})
}
// getHiddenFieldsFromCode 从代码中获取指定数据源的隐藏字段映射
func getHiddenFieldsFromCode(ds string) map[string]map[string]bool {
// 从字段白名单构建隐藏字段映射,所有字段默认 true隐藏
return buildHiddenFieldsFromWhitelist()
}
// buildHiddenFieldsFromWhitelist 读取 schema.AllWhitelist 中的所有字段,按表分组,全部标记为 true
func buildHiddenFieldsFromWhitelist() map[string]map[string]bool {
out := make(map[string]map[string]bool)
for k := range schema.AllWhitelist() {
parts := strings.SplitN(k, ".", 2)
if len(parts) != 2 {
continue
}
tbl, field := parts[0], parts[1]
if _, ok := out[tbl]; !ok {
out[tbl] = make(map[string]bool)
}
out[tbl][field] = true
}
return out
}
// isFieldHidden 检查字段是否在隐藏列表中
func isFieldHidden(hiddenFields map[string]map[string]bool, table, field string) bool {
tableFields, ok := hiddenFields[table]
if !ok {
return false
}
return tableFields[field]
}
func tableLabel(t string) string {
switch t {
case "order":
return "订单主表"
case "order_detail":
return "订单详情"
case "order_cash":
return "红包订单"
case "order_voucher":
return "立减金订单"
case "order_digit":
return "直充卡密订单"
case "plan":
return "活动"
case "key_batch":
return "key批次"
case "code_batch":
return "兑换码批次"
case "voucher":
return "立减金"
case "voucher_batch":
return "立减金批次"
case "merchant_key_send":
return "key码API发放记录"
case "goods_voucher_batch":
return "立减金批次表"
case "goods_voucher_subject_config":
return "立减金主体"
case "merchant":
return "客户"
case "activity":
return "活动"
default:
return t
}
}
func displayTable(ds, tbl string) string {
if ds == "ymt" && tbl == "order_info" {
return "order"
}
return tbl
}
func canonicalField(ds, tbl, col string) (string, string) {
if ds == "ymt" && tbl == "order_info" {
switch col {
case "order_no":
return "order", "order_number"
case "key_code":
return "order", "key"
case "user_id":
return "order", "creator"
case "out_order_no":
return "order", "out_trade_no"
case "activity_id":
return "order", "plan_id"
case "merchant_id":
return "order", "reseller_id"
case "goods_id":
return "order", "product_id"
case "pay_price":
return "order", "pay_amount"
case "key_batch_name":
return "order", "key_batch_id"
default:
return "order", col
}
}
// other tables: canonical equals actual
return tbl, col
}
type columnMeta struct {
Name string
Comment string
}
func getColumns(db *sql.DB, tbl string) []columnMeta {
rows, err := db.Query("SELECT COLUMN_NAME, COLUMN_COMMENT FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? ORDER BY ORDINAL_POSITION", tbl)
if err != nil {
return []columnMeta{}
}
defer rows.Close()
cols := []columnMeta{}
for rows.Next() {
var name, comment string
if err := rows.Scan(&name, &comment); err == nil {
cols = append(cols, columnMeta{Name: name, Comment: comment})
}
}
return cols
}
func recommendedDefaults(ds, orderType string) []string {
base := []string{"order.order_number", "order.creator", "order.out_trade_no", "order.type", "order.status", "order.contract_price", "order.num", "order.pay_amount", "order.create_time"}
if ds != "ymt" {
base = []string{"order.order_number", "order.creator", "order.out_trade_no", "order.type", "order.status", "order.contract_price", "order.num", "order.total", "order.pay_amount", "order.create_time"}
}
t := orderType
if t == "2" { // 直充卡密
if ds == "ymt" {
base = append(base, "order_digit.order_no", "order_digit.account", "order_digit.success_time")
} else {
base = append(base, "plan.title")
}
} else if t == "3" { // 立减金
if ds == "ymt" {
base = append(base, "order_voucher.channel", "order_voucher.status", "goods_voucher_batch.channel_batch_no")
} else {
base = append(base, "order_voucher.channel", "voucher.denomination")
}
} else if t == "1" { // 红包
if ds == "ymt" {
base = append(base, "order_cash.channel", "order_cash.receive_status", "order_cash.denomination")
} else {
base = append(base, "order_cash.channel", "order_cash.receive_status", "order_cash.amount")
}
}
return base
}