From 0a416d40e93b8e9cfb015d2e7514f742c80b1028 Mon Sep 17 00:00:00 2001 From: AzenKain Date: Tue, 26 May 2026 14:32:29 +0700 Subject: [PATCH] feat: define commit snapshot DTOs and implement submission service submission creation logic --- internal/dtos/request/snapshot.go | 119 ++++++++++++++----------- internal/services/submissionService.go | 15 +--- 2 files changed, 73 insertions(+), 61 deletions(-) diff --git a/internal/dtos/request/snapshot.go b/internal/dtos/request/snapshot.go index 8b99250..5bbd514 100644 --- a/internal/dtos/request/snapshot.go +++ b/internal/dtos/request/snapshot.go @@ -3,20 +3,26 @@ package request import "encoding/json" type CommitSnapshot struct { - EditorFeatureCollection *FeatureCollection `json:"editor_feature_collection,omitempty" validate:"omitempty"` - Entities []*EntitySnapshot `json:"entities,omitempty" validate:"omitempty,dive"` - Geometries []*GeometrySnapshot `json:"geometries,omitempty" validate:"omitempty,dive"` - Wikis []*WikiSnapshot `json:"wikis,omitempty" validate:"omitempty,dive"` - GeometryEntity []*GeometryEntitySnapshot `json:"geometry_entity,omitempty" validate:"omitempty,dive"` - EntityWiki []*EntityWikiLinkSnapshot `json:"entity_wiki,omitempty" validate:"omitempty,dive"` - Replays []*BattleReplaySnapshot `json:"replays,omitempty" validate:"omitempty,dive"` + Project *ProjectSnapshot `json:"project" validate:"omitempty"` + EditorFeatureCollection *FeatureCollection `json:"editor_feature_collection" validate:"omitempty"` + Entities []*EntitySnapshot `json:"entities" validate:"omitempty,dive"` + Geometries []*GeometrySnapshot `json:"geometries" validate:"omitempty,dive"` + Wikis []*WikiSnapshot `json:"wikis" validate:"omitempty,dive"` + GeometryEntity []*GeometryEntitySnapshot `json:"geometry_entity" validate:"omitempty,dive"` + EntityWiki []*EntityWikiLinkSnapshot `json:"entity_wiki" validate:"omitempty,dive"` + Replays []*BattleReplaySnapshot `json:"replays" validate:"omitempty,dive"` +} + +type ProjectSnapshot struct { + ID string `json:"id" validate:"required,uuidv7"` + Title string `json:"title" validate:"required"` } type BattleReplaySnapshot struct { - ID string `json:"id" validate:"required,uuidv7"` - GeometryID string `json:"geometry_id" validate:"required,uuidv7"` - TargetGeometryIDs []string `json:"target_geometry_ids,omitempty" validate:"omitempty,dive,uuidv7"` - Detail json.RawMessage `json:"detail,omitempty"` + ID string `json:"id" validate:"required,uuidv7"` + GeometryID string `json:"geometry_id" validate:"required,uuidv7"` + TargetGeometryIDs []string `json:"target_geometry_ids" validate:"omitempty,dive,uuidv7"` + Detail json.RawMessage `json:"detail" validate:"omitempty"` } type FeatureCollection struct { @@ -31,45 +37,58 @@ type Feature struct { } type FeatureProperties struct { - ID any `json:"id" validate:"required"` - Type string `json:"type,omitempty"` - GeometryPreset string `json:"geometry_preset,omitempty"` - TimeStart *float64 `json:"time_start,omitempty"` - TimeEnd *float64 `json:"time_end,omitempty"` - BoundWith *string `json:"bound_with,omitempty"` - EntityID string `json:"entity_id,omitempty" validate:"omitempty,uuidv7"` - EntityIDs []string `json:"entity_ids,omitempty" validate:"omitempty,dive,uuidv7"` - EntityName string `json:"entity_name,omitempty"` - EntityNames []string `json:"entity_names,omitempty"` - EntityTypeID string `json:"entity_type_id,omitempty" validate:"omitempty,uuidv7"` + ID any `json:"id" validate:"required"` + Source string `json:"source" validate:"omitempty,oneof=inline ref"` + Type string `json:"type" validate:"omitempty"` + GeometryPreset string `json:"geometry_preset" validate:"omitempty"` + TimeStart *float64 `json:"time_start" validate:"omitempty"` + TimeEnd *float64 `json:"time_end" validate:"omitempty"` + BoundWith *string `json:"bound_with" validate:"omitempty"` + EntityID string `json:"entity_id" validate:"omitempty,uuidv7"` + EntityIDs []string `json:"entity_ids" validate:"omitempty,dive,uuidv7"` + EntityName string `json:"entity_name"` + EntityNames []string `json:"entity_names" validate:"omitempty"` + EntityTypeID string `json:"entity_type_id" validate:"omitempty,uuidv7"` + PointLabel *string `json:"point_label" validate:"omitempty"` + LineLabel *string `json:"line_label" validate:"omitempty"` + PolygonLabel *string `json:"polygon_label" validate:"omitempty"` + EntityLabelCandidates []*EntityLabelCandidate `json:"entity_label_candidates" validate:"omitempty,dive"` +} + +type EntityLabelCandidate struct { + ID string `json:"id" validate:"required,uuidv7"` + Name string `json:"name" validate:"required"` + TimeStart *float64 `json:"time_start" validate:"omitempty"` + TimeEnd *float64 `json:"time_end" validate:"omitempty"` } type EntitySnapshot struct { ID string `json:"id" validate:"required,uuidv7"` - Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` - Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` - Name string `json:"name,omitempty" validate:"omitempty"` - Slug *string `json:"slug,omitempty" validate:"omitempty,slug"` - Description string `json:"description,omitempty"` - Status *int `json:"status,omitempty" validate:"omitempty,oneof=0 1"` - TimeStart *float64 `json:"time_start,omitempty"` - TimeEnd *float64 `json:"time_end,omitempty"` - BaseUpdatedAt string `json:"base_updated_at,omitempty"` - BaseHash string `json:"base_hash,omitempty"` + Source string `json:"source" validate:"omitempty,oneof=inline ref"` + Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"` + Name string `json:"name" validate:"omitempty"` + Slug *string `json:"slug" validate:"omitempty,slug"` + Description string `json:"description" validate:"omitempty"` + Status *int `json:"status" validate:"omitempty,oneof=0 1"` + TimeStart *float64 `json:"time_start" validate:"omitempty"` + TimeEnd *float64 `json:"time_end" validate:"omitempty"` + BaseUpdatedAt string `json:"base_updated_at" validate:"omitempty"` + BaseHash string `json:"base_hash" validate:"omitempty"` } type GeometrySnapshot struct { ID string `json:"id" validate:"required,uuidv7"` - Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` - Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` - Type string `json:"type,omitempty" validate:"omitempty"` - DrawGeometry json.RawMessage `json:"draw_geometry,omitempty"` - BoundWith *string `json:"bound_with,omitempty"` - TimeStart *float64 `json:"time_start,omitempty"` - TimeEnd *float64 `json:"time_end,omitempty"` - BBox *BBox `json:"bbox,omitempty" validate:"omitempty"` - BaseUpdatedAt string `json:"base_updated_at,omitempty"` - BaseHash string `json:"base_hash,omitempty"` + Source string `json:"source" validate:"omitempty,oneof=inline ref"` + Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"` + Type string `json:"type" validate:"omitempty"` + DrawGeometry json.RawMessage `json:"draw_geometry" validate:"omitempty"` + Geometry json.RawMessage `json:"geometry" validate:"omitempty"` + BoundWith *string `json:"bound_with" validate:"omitempty"` + TimeStart *float64 `json:"time_start" validate:"omitempty"` + TimeEnd *float64 `json:"time_end" validate:"omitempty"` + BBox *BBox `json:"bbox" validate:"omitempty"` + BaseUpdatedAt string `json:"base_updated_at" validate:"omitempty"` + BaseHash string `json:"base_hash" validate:"omitempty"` } type BBox struct { @@ -82,23 +101,23 @@ type BBox struct { type GeometryEntitySnapshot struct { GeometryID string `json:"geometry_id" validate:"required,uuidv7"` EntityID string `json:"entity_id" validate:"required,uuidv7"` - Operation string `json:"operation,omitempty" validate:"omitempty,oneof=reference delete binding"` - BaseLinksHash string `json:"base_links_hash,omitempty"` + Operation string `json:"operation" validate:"omitempty,oneof=reference delete binding"` + BaseLinksHash string `json:"base_links_hash" validate:"omitempty"` } type WikiSnapshot struct { ID string `json:"id" validate:"required,uuidv7"` - Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` - Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` + Source string `json:"source" validate:"omitempty,oneof=inline ref"` + Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"` Title string `json:"title" validate:"required"` Slug *string `json:"slug" validate:"omitempty,slug"` - Doc string `json:"doc,omitempty" validate:"omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` + Doc string `json:"doc" validate:"omitempty"` + UpdatedAt string `json:"updated_at" validate:"omitempty"` } type EntityWikiLinkSnapshot struct { EntityID string `json:"entity_id" validate:"required,uuidv7"` WikiID string `json:"wiki_id" validate:"required,uuidv7"` - Operation string `json:"operation,omitempty" validate:"omitempty,oneof=reference delete binding"` - IsDeleted *int `json:"is_deleted,omitempty" validate:"omitempty,oneof=0 1"` + Operation string `json:"operation" validate:"omitempty,oneof=reference delete binding"` + IsDeleted *int `json:"is_deleted" validate:"omitempty,oneof=0 1"` } diff --git a/internal/services/submissionService.go b/internal/services/submissionService.go index ac93142..68a37ab 100644 --- a/internal/services/submissionService.go +++ b/internal/services/submissionService.go @@ -2,8 +2,8 @@ package services import ( "context" - "errors" "encoding/json" + "errors" "fmt" "history-api/internal/dtos/request" "history-api/internal/dtos/response" @@ -995,6 +995,9 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec if len(snapshotData.GeometryEntity) > 0 { geomLinks := make(map[string][]pgtype.UUID) for _, link := range snapshotData.GeometryEntity { + if link.Operation == "delete" { + continue + } if !validEntities[link.EntityID] || !validGeometries[link.GeometryID] { continue } @@ -1041,16 +1044,6 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec } } - newSnapshot, err := json.Marshal(snapshotData) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to marshal snapshot") - } - commitRepo := s.commitRepo.WithTx(tx) - _, err = commitRepo.UpdateSnapshot(ctx, commitUUID, newSnapshot) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update snapshot: "+err.Error()) - } - wikiDeleteIDs := make([]string, 0) entityDeleteIDs := make([]string, 0)