Files
sub2api-cn-relay-manager/internal/host/sub2api/flexible_id.go
phamnazage-jpg 85d495dd16 feat(control-plane): harden host-scoped reconcile and acceptance evidence
- add batch-scoped reconcile_runs persistence and queries
- route batch detail and reconcile writes through batch_id/host_id
- refresh production boards with host-scope acceptance artifacts
- include latest real-host acceptance evidence for self_service and subscription
2026-05-18 22:22:22 +08:00

207 lines
4.5 KiB
Go

package sub2api
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
)
func decodeFlexibleID(raw json.RawMessage) (string, error) {
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
return "", nil
}
var asString string
if err := json.Unmarshal(raw, &asString); err == nil {
return asString, nil
}
decoder := json.NewDecoder(bytes.NewReader(raw))
decoder.UseNumber()
var asNumber json.Number
if err := decoder.Decode(&asNumber); err == nil {
return asNumber.String(), nil
}
return "", fmt.Errorf("unsupported id payload: %s", string(raw))
}
func flexibleIDValue(raw string) any {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return ""
}
if id, err := strconv.ParseInt(trimmed, 10, 64); err == nil {
return id
}
return trimmed
}
func flexibleIDSliceValues(raw []string) []any {
values := make([]any, 0, len(raw))
for _, item := range raw {
values = append(values, flexibleIDValue(item))
}
return values
}
func (r CreateChannelRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Name string `json:"name"`
GroupIDs []any `json:"group_ids"`
}{
Name: r.Name,
GroupIDs: flexibleIDSliceValues(r.GroupIDs),
})
}
func (r CreatePlanRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
GroupID any `json:"group_id"`
Name string `json:"name"`
Price float64 `json:"price"`
ValidityDays int `json:"validity_days"`
ValidityUnit string `json:"validity_unit"`
}{
GroupID: flexibleIDValue(r.GroupID),
Name: r.Name,
Price: r.Price,
ValidityDays: r.ValidityDays,
ValidityUnit: r.ValidityUnit,
})
}
func (r CreateAccountRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Name string `json:"name"`
Platform string `json:"platform"`
Type string `json:"type"`
Credentials map[string]any `json:"credentials"`
GroupIDs []any `json:"group_ids"`
}{
Name: r.Name,
Platform: r.Platform,
Type: r.Type,
Credentials: r.Credentials,
GroupIDs: flexibleIDSliceValues(r.GroupIDs),
})
}
func (r AssignSubscriptionRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
UserID any `json:"user_id"`
GroupID any `json:"group_id"`
DurationDays int `json:"validity_days,omitempty"`
}{
UserID: flexibleIDValue(r.UserID),
GroupID: flexibleIDValue(r.GroupID),
DurationDays: r.DurationDays,
})
}
func (r *NamedResource) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
Name string `json:"name"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
r.Name = aux.Name
return nil
}
func (r *GroupRef) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
Name string `json:"name"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
r.Name = aux.Name
return nil
}
func (r *ChannelRef) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
Name string `json:"name"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
r.Name = aux.Name
return nil
}
func (r *PlanRef) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
Name string `json:"name"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
r.Name = aux.Name
return nil
}
func (r *AccountRef) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
Name string `json:"name,omitempty"`
Platform string `json:"platform,omitempty"`
Type string `json:"type,omitempty"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
r.Name = aux.Name
r.Platform = aux.Platform
r.Type = aux.Type
return nil
}
func (r *SubscriptionRef) UnmarshalJSON(data []byte) error {
var aux struct {
ID json.RawMessage `json:"id"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
id, err := decodeFlexibleID(aux.ID)
if err != nil {
return err
}
r.ID = id
return nil
}