package hfb import ( "fmt" "regexp" "strconv" "strings" ) var numberPattern = regexp.MustCompile(`[-+]?\d+(?:\.\d+)?`) func ParseUploadText(text string) (ParsedUpload, error) { values := make(map[string]string) for lineNo, line := range strings.Split(text, "\n") { line = strings.TrimSpace(line) if line == "" { continue } key, value, ok := splitKeyValue(line) if !ok { return ParsedUpload{}, fmt.Errorf("第 %d 行格式不正确,应为“字段:值”", lineNo+1) } key = normalizeKey(key) if key == "" { continue } values[key] = strings.TrimSpace(value) } account := ExternalAccountData{} var err error account.LoginMethod = firstValue(values, "上号方式", "登录方式") account.Rank = firstValue(values, "段位") account.Level, err = parseRequiredInt(values, "等级") if err != nil { return ParsedUpload{}, err } account.SafeSlots, err = parseRequiredInt(values, "保险格数", "保险箱格数") if err != nil { return ParsedUpload{}, err } account.DailyLossM, err = parseRequiredFloat(values, "每日消耗") if err != nil { return ParsedUpload{}, err } account.SecretKD, err = parseRequiredFloat(values, "绝密KD", "绝密kd") if err != nil { return ParsedUpload{}, err } account.Deposit, err = parseRequiredFloat(values, "押金") if err != nil { return ParsedUpload{}, err } account.BanRecord = optionalDefault(firstValue(values, "封禁记录"), "无") account.CommonRegion = firstValue(values, "常用地区", "常用区域") account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话") account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间") account.Currency.HafuCoin, err = parseRequiredFloat(values, "哈夫币纯币", "哈夫币") if err != nil { return ParsedUpload{}, err } account.Currency.RecycleRatio, err = parseRequiredRatio(values, "回收比例") if err != nil { return ParsedUpload{}, err } account.Currency.RecycleRent, err = parseRequiredFloat(values, "回收租金") if err != nil { return ParsedUpload{}, err } account.DailyConsumption.Stamina, err = parseRequiredInt(values, "体力") if err != nil { return ParsedUpload{}, err } account.DailyConsumption.Weight, err = parseRequiredInt(values, "负重") if err != nil { return ParsedUpload{}, err } account.Inventory.AWMBullets, err = parseOptionalInt(values, "AWM子弹数量", "AWM子弹") if err != nil { return ParsedUpload{}, err } account.Inventory.Level6Helmets, err = parseOptionalInt(values, "6头数量", "六头数量", "6头") if err != nil { return ParsedUpload{}, err } account.Inventory.Level6Armor, err = parseOptionalInt(values, "6甲数量", "六甲数量", "6甲") if err != nil { return ParsedUpload{}, err } account.Inventory.Skins = parseSkins(firstValue(values, "皮肤")) if strings.TrimSpace(account.LoginMethod) == "" { return ParsedUpload{}, fmt.Errorf("缺少必填字段:上号方式") } if strings.TrimSpace(account.Rank) == "" { return ParsedUpload{}, fmt.Errorf("缺少必填字段:段位") } return ParsedUpload{ UploaderName: firstValue(values, "上传人", "上传者", "上传账号", "上传管理员"), Account: account, }, nil } func splitKeyValue(line string) (string, string, bool) { if idx := strings.Index(line, ":"); idx >= 0 { return line[:idx], line[idx+len(":"):], true } if idx := strings.Index(line, ":"); idx >= 0 { return line[:idx], line[idx+1:], true } return "", "", false } func normalizeKey(key string) string { return strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(key), " ", "")) } func firstValue(values map[string]string, labels ...string) string { for _, label := range labels { if value, ok := values[normalizeKey(label)]; ok { return strings.TrimSpace(value) } } return "" } func optionalDefault(value, fallback string) string { if strings.TrimSpace(value) == "" { return fallback } return strings.TrimSpace(value) } func parseRequiredInt(values map[string]string, labels ...string) (int, error) { value := firstValue(values, labels...) if value == "" { return 0, fmt.Errorf("缺少必填字段:%s", labels[0]) } return parseIntLabel(labels[0], value) } func parseOptionalInt(values map[string]string, labels ...string) (int, error) { value := firstValue(values, labels...) if value == "" { return 0, nil } return parseIntLabel(labels[0], value) } func parseIntLabel(label, value string) (int, error) { number, err := parseFloatText(value, false) if err != nil { return 0, fmt.Errorf("%s 数字格式不正确:%s", label, value) } return int(number), nil } func parseRequiredFloat(values map[string]string, labels ...string) (float64, error) { value := firstValue(values, labels...) if value == "" { return 0, fmt.Errorf("缺少必填字段:%s", labels[0]) } number, err := parseFloatText(value, false) if err != nil { return 0, fmt.Errorf("%s 数字格式不正确:%s", labels[0], value) } return number, nil } func parseRequiredRatio(values map[string]string, labels ...string) (float64, error) { value := firstValue(values, labels...) if value == "" { return 0, fmt.Errorf("缺少必填字段:%s", labels[0]) } number, err := parseFloatText(value, true) if err != nil { return 0, fmt.Errorf("%s 数字格式不正确:%s", labels[0], value) } return number, nil } func parseFloatText(value string, preferLast bool) (float64, error) { value = strings.TrimSpace(strings.ReplaceAll(value, ",", "")) matches := numberPattern.FindAllString(value, -1) if len(matches) == 0 { return 0, strconv.ErrSyntax } pick := matches[0] if preferLast { pick = matches[len(matches)-1] } return strconv.ParseFloat(pick, 64) } func parseSkins(value string) []string { value = strings.TrimSpace(value) if value == "" || value == "无" || strings.EqualFold(value, "none") { return nil } parts := strings.FieldsFunc(value, func(r rune) bool { return r == ',' || r == ',' || r == '、' || r == ';' || r == ';' }) skins := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(part) if part != "" && part != "无" { skins = append(skins, part) } } return skins }