Files
updata_hfb/internal/hfb/parser.go
T
2026-07-23 17:05:23 +08:00

266 lines
7.5 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 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 = parseOptionalFloat(values, "绝密KD", "绝密kd")
if err != nil {
return ParsedUpload{}, err
}
account.Deposit, err = parseRequiredFloat(values, "押金")
if err != nil {
return ParsedUpload{}, err
}
banRaw := firstValue(values, "封禁记录")
account.BanRecord = normalizeBanFlag(banRaw)
account.CommonRegion = firstValue(values, "常用地区", "常用区域")
account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话")
account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间")
account.Remark = composeRemark(firstValue(values, "备注", "说明"), banRaw)
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)
}
// normalizeBanFlag 只输出“有/无”,具体封禁历史放到备注。
func normalizeBanFlag(value string) string {
value = strings.TrimSpace(value)
if value == "" || isNoBanRecord(value) {
return "无"
}
return "有"
}
func isNoBanRecord(value string) bool {
value = strings.TrimSpace(value)
switch value {
case "无", "无封禁", "无封禁记录", "否", "没有", "none", "None", "NONE":
return true
default:
return false
}
}
// composeRemark 合并原文备注与封禁历史详情。
func composeRemark(remark, banRaw string) string {
remark = strings.TrimSpace(remark)
banDetail := strings.TrimSpace(banRaw)
if banDetail == "" || isNoBanRecord(banDetail) {
return remark
}
// 如果原文已经是“有/无”,不当作详情追加
if banDetail == "有" || banDetail == "是" || banDetail == "有封禁" || banDetail == "有封禁记录" {
return remark
}
banLine := "封禁历史:" + banDetail
if remark == "" {
return banLine
}
return remark + "\n" + banLine
}
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 parseOptionalFloat(values map[string]string, labels ...string) (float64, error) {
value := firstValue(values, labels...)
if value == "" {
return 0, nil
}
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
}