package recovery import ( _ "embed" "encoding/json" "fmt" "strconv" "strings" "sync" ) //go:embed recovery_orders.json var recoveryOrdersJSON []byte type Config struct { Orders []Order `json:"orders"` } type Order struct { OutOrderNo string `json:"out_order_no,omitempty"` OrderNo string `json:"order_no"` OrderStatus string `json:"order_status,omitempty"` SubscribeId int64 `json:"subscribe_id"` LegacyID string `json:"订阅id,omitempty"` ExpireAt int64 `json:"expire_at"` Quantity int64 `json:"quantity,omitempty"` PaidAt int64 `json:"paid_at,omitempty"` PaymentOrderNo string `json:"payment_order_no,omitempty"` OrderMoneyCents int64 `json:"order_money_cents,omitempty"` Note string `json:"note"` } var ( loadOnce sync.Once orders map[string]Order loadErr error ) func FindOrder(orderNo string) (Order, bool, error) { loadOnce.Do(func() { orders, loadErr = loadOrders() }) if loadErr != nil { return Order{}, false, loadErr } item, ok := orders[strings.TrimSpace(orderNo)] return item, ok, nil } func loadOrders() (map[string]Order, error) { var cfg Config if err := json.Unmarshal(recoveryOrdersJSON, &cfg); err != nil { return nil, fmt.Errorf("parse recovery orders json: %w", err) } result := make(map[string]Order, len(cfg.Orders)) for i, item := range cfg.Orders { item.OutOrderNo = strings.TrimSpace(item.OutOrderNo) item.OrderNo = strings.TrimSpace(item.OrderNo) item.OrderStatus = strings.ToLower(strings.TrimSpace(item.OrderStatus)) if item.OutOrderNo == "" { item.OutOrderNo = item.OrderNo } if item.OrderNo == "" { item.OrderNo = item.OutOrderNo } if item.OrderStatus == "" { item.OrderStatus = "success" } if item.SubscribeId == 0 && strings.TrimSpace(item.LegacyID) != "" { id, err := strconv.ParseInt(strings.TrimSpace(item.LegacyID), 10, 64) if err != nil { return nil, fmt.Errorf("orders[%d] legacy subscribe id is invalid", i) } item.SubscribeId = id } if item.OutOrderNo == "" { return nil, fmt.Errorf("orders[%d] out_order_no is required", i) } if item.SubscribeId <= 0 { return nil, fmt.Errorf("orders[%d] subscribe_id is required", i) } if item.ExpireAt <= 0 { return nil, fmt.Errorf("orders[%d] expire_at is required", i) } if item.Quantity <= 0 { item.Quantity = quantityFromAmount(item.OrderMoneyCents) } if item.Quantity <= 0 { return nil, fmt.Errorf("orders[%d] quantity is required", i) } if item.PaidAt < 0 { return nil, fmt.Errorf("orders[%d] paid_at is invalid", i) } if item.OrderMoneyCents < 0 { return nil, fmt.Errorf("orders[%d] order_money_cents is invalid", i) } if _, exists := result[item.OutOrderNo]; exists { return nil, fmt.Errorf("duplicate recovery out_order_no %s", item.OutOrderNo) } result[item.OutOrderNo] = item if item.OrderNo != "" && item.OrderNo != item.OutOrderNo { if _, exists := result[item.OrderNo]; exists { return nil, fmt.Errorf("duplicate recovery order_no %s", item.OrderNo) } result[item.OrderNo] = item } } return result, nil } func quantityFromAmount(amount int64) int64 { switch amount { case 1953: return 7 case 4193: return 30 case 9093: return 90 case 31500: return 365 default: return 0 } }