feat: define commit snapshot DTOs and implement submission service submission creation logic
Build and Release / release (push) Successful in 1m39s

This commit is contained in:
2026-05-26 14:32:29 +07:00
parent cc92e07d92
commit 0a416d40e9
2 changed files with 73 additions and 61 deletions
+66 -47
View File
@@ -3,20 +3,26 @@ package request
import "encoding/json" import "encoding/json"
type CommitSnapshot struct { type CommitSnapshot struct {
EditorFeatureCollection *FeatureCollection `json:"editor_feature_collection,omitempty" validate:"omitempty"` Project *ProjectSnapshot `json:"project" validate:"omitempty"`
Entities []*EntitySnapshot `json:"entities,omitempty" validate:"omitempty,dive"` EditorFeatureCollection *FeatureCollection `json:"editor_feature_collection" validate:"omitempty"`
Geometries []*GeometrySnapshot `json:"geometries,omitempty" validate:"omitempty,dive"` Entities []*EntitySnapshot `json:"entities" validate:"omitempty,dive"`
Wikis []*WikiSnapshot `json:"wikis,omitempty" validate:"omitempty,dive"` Geometries []*GeometrySnapshot `json:"geometries" validate:"omitempty,dive"`
GeometryEntity []*GeometryEntitySnapshot `json:"geometry_entity,omitempty" validate:"omitempty,dive"` Wikis []*WikiSnapshot `json:"wikis" validate:"omitempty,dive"`
EntityWiki []*EntityWikiLinkSnapshot `json:"entity_wiki,omitempty" validate:"omitempty,dive"` GeometryEntity []*GeometryEntitySnapshot `json:"geometry_entity" validate:"omitempty,dive"`
Replays []*BattleReplaySnapshot `json:"replays,omitempty" 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 { type BattleReplaySnapshot struct {
ID string `json:"id" validate:"required,uuidv7"` ID string `json:"id" validate:"required,uuidv7"`
GeometryID string `json:"geometry_id" validate:"required,uuidv7"` GeometryID string `json:"geometry_id" validate:"required,uuidv7"`
TargetGeometryIDs []string `json:"target_geometry_ids,omitempty" validate:"omitempty,dive,uuidv7"` TargetGeometryIDs []string `json:"target_geometry_ids" validate:"omitempty,dive,uuidv7"`
Detail json.RawMessage `json:"detail,omitempty"` Detail json.RawMessage `json:"detail" validate:"omitempty"`
} }
type FeatureCollection struct { type FeatureCollection struct {
@@ -32,44 +38,57 @@ type Feature struct {
type FeatureProperties struct { type FeatureProperties struct {
ID any `json:"id" validate:"required"` ID any `json:"id" validate:"required"`
Type string `json:"type,omitempty"` Source string `json:"source" validate:"omitempty,oneof=inline ref"`
GeometryPreset string `json:"geometry_preset,omitempty"` Type string `json:"type" validate:"omitempty"`
TimeStart *float64 `json:"time_start,omitempty"` GeometryPreset string `json:"geometry_preset" validate:"omitempty"`
TimeEnd *float64 `json:"time_end,omitempty"` TimeStart *float64 `json:"time_start" validate:"omitempty"`
BoundWith *string `json:"bound_with,omitempty"` TimeEnd *float64 `json:"time_end" validate:"omitempty"`
EntityID string `json:"entity_id,omitempty" validate:"omitempty,uuidv7"` BoundWith *string `json:"bound_with" validate:"omitempty"`
EntityIDs []string `json:"entity_ids,omitempty" validate:"omitempty,dive,uuidv7"` EntityID string `json:"entity_id" validate:"omitempty,uuidv7"`
EntityName string `json:"entity_name,omitempty"` EntityIDs []string `json:"entity_ids" validate:"omitempty,dive,uuidv7"`
EntityNames []string `json:"entity_names,omitempty"` EntityName string `json:"entity_name"`
EntityTypeID string `json:"entity_type_id,omitempty" validate:"omitempty,uuidv7"` 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 { type EntitySnapshot struct {
ID string `json:"id" validate:"required,uuidv7"` ID string `json:"id" validate:"required,uuidv7"`
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` Source string `json:"source" validate:"omitempty,oneof=inline ref"`
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"`
Name string `json:"name,omitempty" validate:"omitempty"` Name string `json:"name" validate:"omitempty"`
Slug *string `json:"slug,omitempty" validate:"omitempty,slug"` Slug *string `json:"slug" validate:"omitempty,slug"`
Description string `json:"description,omitempty"` Description string `json:"description" validate:"omitempty"`
Status *int `json:"status,omitempty" validate:"omitempty,oneof=0 1"` Status *int `json:"status" validate:"omitempty,oneof=0 1"`
TimeStart *float64 `json:"time_start,omitempty"` TimeStart *float64 `json:"time_start" validate:"omitempty"`
TimeEnd *float64 `json:"time_end,omitempty"` TimeEnd *float64 `json:"time_end" validate:"omitempty"`
BaseUpdatedAt string `json:"base_updated_at,omitempty"` BaseUpdatedAt string `json:"base_updated_at" validate:"omitempty"`
BaseHash string `json:"base_hash,omitempty"` BaseHash string `json:"base_hash" validate:"omitempty"`
} }
type GeometrySnapshot struct { type GeometrySnapshot struct {
ID string `json:"id" validate:"required,uuidv7"` ID string `json:"id" validate:"required,uuidv7"`
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` Source string `json:"source" validate:"omitempty,oneof=inline ref"`
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"`
Type string `json:"type,omitempty" validate:"omitempty"` Type string `json:"type" validate:"omitempty"`
DrawGeometry json.RawMessage `json:"draw_geometry,omitempty"` DrawGeometry json.RawMessage `json:"draw_geometry" validate:"omitempty"`
BoundWith *string `json:"bound_with,omitempty"` Geometry json.RawMessage `json:"geometry" validate:"omitempty"`
TimeStart *float64 `json:"time_start,omitempty"` BoundWith *string `json:"bound_with" validate:"omitempty"`
TimeEnd *float64 `json:"time_end,omitempty"` TimeStart *float64 `json:"time_start" validate:"omitempty"`
BBox *BBox `json:"bbox,omitempty" validate:"omitempty"` TimeEnd *float64 `json:"time_end" validate:"omitempty"`
BaseUpdatedAt string `json:"base_updated_at,omitempty"` BBox *BBox `json:"bbox" validate:"omitempty"`
BaseHash string `json:"base_hash,omitempty"` BaseUpdatedAt string `json:"base_updated_at" validate:"omitempty"`
BaseHash string `json:"base_hash" validate:"omitempty"`
} }
type BBox struct { type BBox struct {
@@ -82,23 +101,23 @@ type BBox struct {
type GeometryEntitySnapshot struct { type GeometryEntitySnapshot struct {
GeometryID string `json:"geometry_id" validate:"required,uuidv7"` GeometryID string `json:"geometry_id" validate:"required,uuidv7"`
EntityID string `json:"entity_id" validate:"required,uuidv7"` EntityID string `json:"entity_id" validate:"required,uuidv7"`
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=reference delete binding"` Operation string `json:"operation" validate:"omitempty,oneof=reference delete binding"`
BaseLinksHash string `json:"base_links_hash,omitempty"` BaseLinksHash string `json:"base_links_hash" validate:"omitempty"`
} }
type WikiSnapshot struct { type WikiSnapshot struct {
ID string `json:"id" validate:"required,uuidv7"` ID string `json:"id" validate:"required,uuidv7"`
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"` Source string `json:"source" validate:"omitempty,oneof=inline ref"`
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` Operation string `json:"operation" validate:"omitempty,oneof=create update delete reference"`
Title string `json:"title" validate:"required"` Title string `json:"title" validate:"required"`
Slug *string `json:"slug" validate:"omitempty,slug"` Slug *string `json:"slug" validate:"omitempty,slug"`
Doc string `json:"doc,omitempty" validate:"omitempty"` Doc string `json:"doc" validate:"omitempty"`
UpdatedAt string `json:"updated_at,omitempty"` UpdatedAt string `json:"updated_at" validate:"omitempty"`
} }
type EntityWikiLinkSnapshot struct { type EntityWikiLinkSnapshot struct {
EntityID string `json:"entity_id" validate:"required,uuidv7"` EntityID string `json:"entity_id" validate:"required,uuidv7"`
WikiID string `json:"wiki_id" validate:"required,uuidv7"` WikiID string `json:"wiki_id" validate:"required,uuidv7"`
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=reference delete binding"` Operation string `json:"operation" validate:"omitempty,oneof=reference delete binding"`
IsDeleted *int `json:"is_deleted,omitempty" validate:"omitempty,oneof=0 1"` IsDeleted *int `json:"is_deleted" validate:"omitempty,oneof=0 1"`
} }
+4 -11
View File
@@ -2,8 +2,8 @@ package services
import ( import (
"context" "context"
"errors"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"history-api/internal/dtos/request" "history-api/internal/dtos/request"
"history-api/internal/dtos/response" "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 { if len(snapshotData.GeometryEntity) > 0 {
geomLinks := make(map[string][]pgtype.UUID) geomLinks := make(map[string][]pgtype.UUID)
for _, link := range snapshotData.GeometryEntity { for _, link := range snapshotData.GeometryEntity {
if link.Operation == "delete" {
continue
}
if !validEntities[link.EntityID] || !validGeometries[link.GeometryID] { if !validEntities[link.EntityID] || !validGeometries[link.GeometryID] {
continue 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) wikiDeleteIDs := make([]string, 0)
entityDeleteIDs := make([]string, 0) entityDeleteIDs := make([]string, 0)