package iot_excelize import ( "reflect" "strings" ) type fieldSchema struct { Index []int Field reflect.StructField Name string Header string Required bool } func parseSchema(t reflect.Type) []fieldSchema { if t == nil { return nil } if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Struct { return nil } return parseStructSchema(t, nil) } func parseStructSchema(t reflect.Type, parent []int) []fieldSchema { var fields []fieldSchema for i := 0; i < t.NumField(); i++ { field := t.Field(i) if !field.IsExported() { continue } if field.Anonymous && derefType(field.Type).Kind() == reflect.Struct { fields = append(fields, parseStructSchema(derefType(field.Type), appendIndex(parent, i))...) continue } header := fieldHeader(field) if header == "-" || header == "" { continue } fields = append(fields, fieldSchema{ Index: appendIndex(parent, i), Field: field, Name: field.Name, Header: header, Required: isRequired(field), }) } return fields } func fieldHeader(field reflect.StructField) string { for _, tag := range []string{"excel", "dc", "description", "json"} { if value, ok := field.Tag.Lookup(tag); ok { value = strings.TrimSpace(strings.Split(value, ",")[0]) if value != "" { return value } } } return field.Name } func isRequired(field reflect.StructField) bool { rule := field.Tag.Get("v") if rule == "" { return false } for _, item := range strings.Split(rule, "|") { if strings.HasPrefix(strings.TrimSpace(item), "required") { return true } } return false } func requiredMessage(field reflect.StructField, header string) string { rule := field.Tag.Get("v") for _, item := range strings.Split(rule, "|") { item = strings.TrimSpace(item) if !strings.HasPrefix(item, "required") { continue } if parts := strings.SplitN(item, "#", 2); len(parts) == 2 && strings.TrimSpace(parts[1]) != "" { return strings.TrimSpace(parts[1]) } } return header + " is required" } func appendIndex(parent []int, i int) []int { index := make([]int, 0, len(parent)+1) index = append(index, parent...) index = append(index, i) return index } func derefType(t reflect.Type) reflect.Type { for t.Kind() == reflect.Ptr { t = t.Elem() } return t } func normalizeHeader(s string) string { return strings.TrimSpace(strings.ReplaceAll(s, "\uFEFF", "")) }