推进活动系统最小成品闭环与游客体验
This commit is contained in:
222
backend/internal/service/preview_contract.go
Normal file
222
backend/internal/service/preview_contract.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package service
|
||||
|
||||
import "strings"
|
||||
|
||||
type MapPreviewView struct {
|
||||
Mode string `json:"mode"`
|
||||
BaseTiles *PreviewBaseTiles `json:"baseTiles,omitempty"`
|
||||
Viewport *PreviewViewport `json:"viewport,omitempty"`
|
||||
Variants []PreviewVariantView `json:"variants,omitempty"`
|
||||
SelectedVariantID *string `json:"selectedVariantId,omitempty"`
|
||||
}
|
||||
|
||||
type PreviewBaseTiles struct {
|
||||
TileBaseURL string `json:"tileBaseUrl"`
|
||||
Zoom *int `json:"zoom,omitempty"`
|
||||
TileSize *int `json:"tileSize,omitempty"`
|
||||
}
|
||||
|
||||
type PreviewViewport struct {
|
||||
Width *int `json:"width,omitempty"`
|
||||
Height *int `json:"height,omitempty"`
|
||||
MinLon *float64 `json:"minLon,omitempty"`
|
||||
MinLat *float64 `json:"minLat,omitempty"`
|
||||
MaxLon *float64 `json:"maxLon,omitempty"`
|
||||
MaxLat *float64 `json:"maxLat,omitempty"`
|
||||
}
|
||||
|
||||
type PreviewVariantView struct {
|
||||
VariantID string `json:"variantId"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
RouteCode *string `json:"routeCode,omitempty"`
|
||||
Controls []PreviewControlView `json:"controls,omitempty"`
|
||||
Legs []PreviewLegView `json:"legs,omitempty"`
|
||||
}
|
||||
|
||||
type PreviewControlView struct {
|
||||
ID string `json:"id"`
|
||||
Kind *string `json:"kind,omitempty"`
|
||||
Lon *float64 `json:"lon,omitempty"`
|
||||
Lat *float64 `json:"lat,omitempty"`
|
||||
Label *string `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
type PreviewLegView struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
func buildPreviewFromPayload(payloadJSON *string) (*MapPreviewView, error) {
|
||||
if payloadJSON == nil || strings.TrimSpace(*payloadJSON) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
payload, err := decodeJSONObject(*payloadJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawPreview, _ := payload["preview"].(map[string]any)
|
||||
if len(rawPreview) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
view := &MapPreviewView{}
|
||||
if mode := readStringField(rawPreview, "mode"); mode != nil {
|
||||
view.Mode = *mode
|
||||
}
|
||||
if view.Mode == "" {
|
||||
view.Mode = "readonly"
|
||||
}
|
||||
|
||||
if rawBaseTiles, ok := rawPreview["baseTiles"].(map[string]any); ok && len(rawBaseTiles) > 0 {
|
||||
baseTiles := &PreviewBaseTiles{}
|
||||
if tileBaseURL := readStringField(rawBaseTiles, "tileBaseUrl"); tileBaseURL != nil {
|
||||
baseTiles.TileBaseURL = *tileBaseURL
|
||||
}
|
||||
baseTiles.Zoom = readIntField(rawBaseTiles, "zoom")
|
||||
baseTiles.TileSize = readIntField(rawBaseTiles, "tileSize")
|
||||
if strings.TrimSpace(baseTiles.TileBaseURL) != "" {
|
||||
view.BaseTiles = baseTiles
|
||||
}
|
||||
}
|
||||
|
||||
if rawViewport, ok := rawPreview["viewport"].(map[string]any); ok && len(rawViewport) > 0 {
|
||||
viewport := &PreviewViewport{
|
||||
Width: readIntField(rawViewport, "width"),
|
||||
Height: readIntField(rawViewport, "height"),
|
||||
MinLon: readFloatField(rawViewport, "minLon"),
|
||||
MinLat: readFloatField(rawViewport, "minLat"),
|
||||
MaxLon: readFloatField(rawViewport, "maxLon"),
|
||||
MaxLat: readFloatField(rawViewport, "maxLat"),
|
||||
}
|
||||
view.Viewport = viewport
|
||||
}
|
||||
|
||||
if selectedVariantID := readStringField(rawPreview, "selectedVariantId"); selectedVariantID != nil {
|
||||
view.SelectedVariantID = selectedVariantID
|
||||
}
|
||||
|
||||
rawVariants, _ := rawPreview["variants"].([]any)
|
||||
if len(rawVariants) > 0 {
|
||||
view.Variants = make([]PreviewVariantView, 0, len(rawVariants))
|
||||
for _, raw := range rawVariants {
|
||||
item, ok := raw.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
variantID := readStringField(item, "variantId")
|
||||
if variantID == nil || strings.TrimSpace(*variantID) == "" {
|
||||
variantID = readStringField(item, "id")
|
||||
}
|
||||
if variantID == nil || strings.TrimSpace(*variantID) == "" {
|
||||
continue
|
||||
}
|
||||
variant := PreviewVariantView{
|
||||
VariantID: *variantID,
|
||||
Name: readStringField(item, "name"),
|
||||
RouteCode: readStringField(item, "routeCode"),
|
||||
}
|
||||
rawControls, _ := item["controls"].([]any)
|
||||
if len(rawControls) > 0 {
|
||||
variant.Controls = make([]PreviewControlView, 0, len(rawControls))
|
||||
for _, rawControl := range rawControls {
|
||||
controlMap, ok := rawControl.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
controlID := readStringField(controlMap, "id")
|
||||
if controlID == nil || strings.TrimSpace(*controlID) == "" {
|
||||
continue
|
||||
}
|
||||
variant.Controls = append(variant.Controls, PreviewControlView{
|
||||
ID: *controlID,
|
||||
Kind: readStringField(controlMap, "kind"),
|
||||
Lon: readFloatField(controlMap, "lon"),
|
||||
Lat: readFloatField(controlMap, "lat"),
|
||||
Label: readStringField(controlMap, "label"),
|
||||
})
|
||||
}
|
||||
}
|
||||
rawLegs, _ := item["legs"].([]any)
|
||||
if len(rawLegs) > 0 {
|
||||
variant.Legs = make([]PreviewLegView, 0, len(rawLegs))
|
||||
for _, rawLeg := range rawLegs {
|
||||
legMap, ok := rawLeg.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
from := readStringField(legMap, "from")
|
||||
to := readStringField(legMap, "to")
|
||||
if from == nil || to == nil || strings.TrimSpace(*from) == "" || strings.TrimSpace(*to) == "" {
|
||||
continue
|
||||
}
|
||||
variant.Legs = append(variant.Legs, PreviewLegView{
|
||||
From: *from,
|
||||
To: *to,
|
||||
})
|
||||
}
|
||||
}
|
||||
view.Variants = append(view.Variants, variant)
|
||||
}
|
||||
}
|
||||
|
||||
if view.BaseTiles == nil && view.Viewport == nil && len(view.Variants) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return view, nil
|
||||
}
|
||||
|
||||
func readIntField(object map[string]any, key string) *int {
|
||||
if object == nil {
|
||||
return nil
|
||||
}
|
||||
value, ok := object[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
result := v
|
||||
return &result
|
||||
case int32:
|
||||
result := int(v)
|
||||
return &result
|
||||
case int64:
|
||||
result := int(v)
|
||||
return &result
|
||||
case float64:
|
||||
result := int(v)
|
||||
return &result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func readFloatField(object map[string]any, key string) *float64 {
|
||||
if object == nil {
|
||||
return nil
|
||||
}
|
||||
value, ok := object[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
result := v
|
||||
return &result
|
||||
case float32:
|
||||
result := float64(v)
|
||||
return &result
|
||||
case int:
|
||||
result := float64(v)
|
||||
return &result
|
||||
case int32:
|
||||
result := float64(v)
|
||||
return &result
|
||||
case int64:
|
||||
result := float64(v)
|
||||
return &result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user