同步前后端联调与文档更新

This commit is contained in:
2026-04-02 09:25:05 +08:00
parent af43beadb0
commit 6964e26ec9
113 changed files with 4317 additions and 293 deletions

View File

@@ -0,0 +1,248 @@
package postgres
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
)
type Tenant struct {
ID string
TenantCode string
Name string
Status string
}
type AdminEventRecord struct {
ID string
PublicID string
TenantID *string
TenantCode *string
TenantName *string
Slug string
DisplayName string
Summary *string
Status string
CurrentReleaseID *string
CurrentReleasePubID *string
ConfigLabel *string
ManifestURL *string
ManifestChecksum *string
RouteCode *string
}
type CreateAdminEventParams struct {
PublicID string
TenantID *string
Slug string
DisplayName string
Summary *string
Status string
}
type UpdateAdminEventParams struct {
EventID string
TenantID *string
Slug string
DisplayName string
Summary *string
Status string
ClearTenant bool
}
func (s *Store) GetTenantByCode(ctx context.Context, tenantCode string) (*Tenant, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, tenant_code, name, status
FROM tenants
WHERE tenant_code = $1
LIMIT 1
`, tenantCode)
var item Tenant
err := row.Scan(&item.ID, &item.TenantCode, &item.Name, &item.Status)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get tenant by code: %w", err)
}
return &item, nil
}
func (s *Store) ListAdminEvents(ctx context.Context, limit int) ([]AdminEventRecord, error) {
if limit <= 0 || limit > 200 {
limit = 50
}
rows, err := s.pool.Query(ctx, `
SELECT
e.id,
e.event_public_id,
e.tenant_id,
t.tenant_code,
t.name,
e.slug,
e.display_name,
e.summary,
e.status,
e.current_release_id,
er.release_public_id,
er.config_label,
er.manifest_url,
er.manifest_checksum_sha256,
er.route_code
FROM events e
LEFT JOIN tenants t ON t.id = e.tenant_id
LEFT JOIN event_releases er ON er.id = e.current_release_id
ORDER BY e.created_at DESC
LIMIT $1
`, limit)
if err != nil {
return nil, fmt.Errorf("list admin events: %w", err)
}
defer rows.Close()
items := []AdminEventRecord{}
for rows.Next() {
item, err := scanAdminEventFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate admin events: %w", err)
}
return items, nil
}
func (s *Store) GetAdminEventByPublicID(ctx context.Context, eventPublicID string) (*AdminEventRecord, error) {
row := s.pool.QueryRow(ctx, `
SELECT
e.id,
e.event_public_id,
e.tenant_id,
t.tenant_code,
t.name,
e.slug,
e.display_name,
e.summary,
e.status,
e.current_release_id,
er.release_public_id,
er.config_label,
er.manifest_url,
er.manifest_checksum_sha256,
er.route_code
FROM events e
LEFT JOIN tenants t ON t.id = e.tenant_id
LEFT JOIN event_releases er ON er.id = e.current_release_id
WHERE e.event_public_id = $1
LIMIT 1
`, eventPublicID)
return scanAdminEvent(row)
}
func (s *Store) CreateAdminEvent(ctx context.Context, tx Tx, params CreateAdminEventParams) (*AdminEventRecord, error) {
row := tx.QueryRow(ctx, `
INSERT INTO events (tenant_id, event_public_id, slug, display_name, summary, status)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, event_public_id, tenant_id, slug, display_name, summary, status, current_release_id
`, params.TenantID, params.PublicID, params.Slug, params.DisplayName, params.Summary, params.Status)
var item AdminEventRecord
if err := row.Scan(
&item.ID,
&item.PublicID,
&item.TenantID,
&item.Slug,
&item.DisplayName,
&item.Summary,
&item.Status,
&item.CurrentReleaseID,
); err != nil {
return nil, fmt.Errorf("create admin event: %w", err)
}
return &item, nil
}
func (s *Store) UpdateAdminEvent(ctx context.Context, tx Tx, params UpdateAdminEventParams) (*AdminEventRecord, error) {
row := tx.QueryRow(ctx, `
UPDATE events
SET tenant_id = CASE WHEN $7 THEN NULL ELSE $2 END,
slug = $3,
display_name = $4,
summary = $5,
status = $6
WHERE id = $1
RETURNING id, event_public_id, tenant_id, slug, display_name, summary, status, current_release_id
`, params.EventID, params.TenantID, params.Slug, params.DisplayName, params.Summary, params.Status, params.ClearTenant)
var item AdminEventRecord
if err := row.Scan(
&item.ID,
&item.PublicID,
&item.TenantID,
&item.Slug,
&item.DisplayName,
&item.Summary,
&item.Status,
&item.CurrentReleaseID,
); err != nil {
return nil, fmt.Errorf("update admin event: %w", err)
}
return &item, nil
}
func scanAdminEvent(row pgx.Row) (*AdminEventRecord, error) {
var item AdminEventRecord
err := row.Scan(
&item.ID,
&item.PublicID,
&item.TenantID,
&item.TenantCode,
&item.TenantName,
&item.Slug,
&item.DisplayName,
&item.Summary,
&item.Status,
&item.CurrentReleaseID,
&item.CurrentReleasePubID,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan admin event: %w", err)
}
return &item, nil
}
func scanAdminEventFromRows(rows pgx.Rows) (*AdminEventRecord, error) {
var item AdminEventRecord
err := rows.Scan(
&item.ID,
&item.PublicID,
&item.TenantID,
&item.TenantCode,
&item.TenantName,
&item.Slug,
&item.DisplayName,
&item.Summary,
&item.Status,
&item.CurrentReleaseID,
&item.CurrentReleasePubID,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
)
if err != nil {
return nil, fmt.Errorf("scan admin event row: %w", err)
}
return &item, nil
}

View File

@@ -261,6 +261,36 @@ func (s *Store) GetEventConfigBuildByID(ctx context.Context, buildID string) (*E
return scanEventConfigBuild(row)
}
func (s *Store) ListEventConfigBuildsByEventID(ctx context.Context, eventID string, limit int) ([]EventConfigBuild, error) {
if limit <= 0 || limit > 100 {
limit = 20
}
rows, err := s.pool.Query(ctx, `
SELECT id, event_id, source_id, build_no, build_status, build_log, manifest_jsonb::text, asset_index_jsonb::text
FROM event_config_builds
WHERE event_id = $1
ORDER BY build_no DESC
LIMIT $2
`, eventID, limit)
if err != nil {
return nil, fmt.Errorf("list event config builds: %w", err)
}
defer rows.Close()
items := []EventConfigBuild{}
for rows.Next() {
item, err := scanEventConfigBuildFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate event config builds: %w", err)
}
return items, nil
}
func scanEventConfigSource(row pgx.Row) (*EventConfigSource, error) {
var item EventConfigSource
err := row.Scan(
@@ -321,3 +351,21 @@ func scanEventConfigBuild(row pgx.Row) (*EventConfigBuild, error) {
}
return &item, nil
}
func scanEventConfigBuildFromRows(rows pgx.Rows) (*EventConfigBuild, error) {
var item EventConfigBuild
err := rows.Scan(
&item.ID,
&item.EventID,
&item.SourceID,
&item.BuildNo,
&item.BuildStatus,
&item.BuildLog,
&item.ManifestJSON,
&item.AssetIndexJSON,
)
if err != nil {
return nil, fmt.Errorf("scan event config build row: %w", err)
}
return &item, nil
}

View File

@@ -261,3 +261,85 @@ func (s *Store) CreateGameSession(ctx context.Context, tx Tx, params CreateGameS
}
return &session, nil
}
func (s *Store) ListEventReleasesByEventID(ctx context.Context, eventID string, limit int) ([]EventRelease, error) {
if limit <= 0 || limit > 100 {
limit = 20
}
rows, err := s.pool.Query(ctx, `
SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
FROM event_releases
WHERE event_id = $1
ORDER BY release_no DESC
LIMIT $2
`, eventID, limit)
if err != nil {
return nil, fmt.Errorf("list event releases by event id: %w", err)
}
defer rows.Close()
items := []EventRelease{}
for rows.Next() {
item, err := scanEventReleaseFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate event releases by event id: %w", err)
}
return items, nil
}
func (s *Store) GetEventReleaseByPublicID(ctx context.Context, releasePublicID string) (*EventRelease, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, release_public_id, event_id, release_no, config_label, manifest_url, manifest_checksum_sha256, route_code, build_id, status, published_at
FROM event_releases
WHERE release_public_id = $1
LIMIT 1
`, releasePublicID)
var item EventRelease
err := row.Scan(
&item.ID,
&item.PublicID,
&item.EventID,
&item.ReleaseNo,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
&item.BuildID,
&item.Status,
&item.PublishedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get event release by public id: %w", err)
}
return &item, nil
}
func scanEventReleaseFromRows(rows pgx.Rows) (*EventRelease, error) {
var item EventRelease
err := rows.Scan(
&item.ID,
&item.PublicID,
&item.EventID,
&item.ReleaseNo,
&item.ConfigLabel,
&item.ManifestURL,
&item.ManifestChecksum,
&item.RouteCode,
&item.BuildID,
&item.Status,
&item.PublishedAt,
)
if err != nil {
return nil, fmt.Errorf("scan event release row: %w", err)
}
return &item, nil
}

View File

@@ -0,0 +1,660 @@
package postgres
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
)
type ResourceMap struct {
ID string
PublicID string
Code string
Name string
Status string
Description *string
CurrentVersionID *string
CreatedAt time.Time
UpdatedAt time.Time
}
type ResourceMapVersion struct {
ID string
PublicID string
MapID string
VersionCode string
Status string
MapmetaURL string
TilesRootURL string
PublishedAssetRoot *string
BoundsJSON json.RawMessage
MetadataJSON json.RawMessage
CreatedAt time.Time
UpdatedAt time.Time
}
type ResourcePlayfield struct {
ID string
PublicID string
Code string
Name string
Kind string
Status string
Description *string
CurrentVersionID *string
CreatedAt time.Time
UpdatedAt time.Time
}
type ResourcePlayfieldVersion struct {
ID string
PublicID string
PlayfieldID string
VersionCode string
Status string
SourceType string
SourceURL string
PublishedAssetRoot *string
ControlCount *int
BoundsJSON json.RawMessage
MetadataJSON json.RawMessage
CreatedAt time.Time
UpdatedAt time.Time
}
type ResourcePack struct {
ID string
PublicID string
Code string
Name string
Status string
Description *string
CurrentVersionID *string
CreatedAt time.Time
UpdatedAt time.Time
}
type ResourcePackVersion struct {
ID string
PublicID string
ResourcePackID string
VersionCode string
Status string
ContentEntryURL *string
AudioRootURL *string
ThemeProfileCode *string
PublishedAssetRoot *string
MetadataJSON json.RawMessage
CreatedAt time.Time
UpdatedAt time.Time
}
type CreateResourceMapParams struct {
PublicID string
Code string
Name string
Status string
Description *string
}
type CreateResourceMapVersionParams struct {
PublicID string
MapID string
VersionCode string
Status string
MapmetaURL string
TilesRootURL string
PublishedAssetRoot *string
BoundsJSON map[string]any
MetadataJSON map[string]any
}
type CreateResourcePlayfieldParams struct {
PublicID string
Code string
Name string
Kind string
Status string
Description *string
}
type CreateResourcePlayfieldVersionParams struct {
PublicID string
PlayfieldID string
VersionCode string
Status string
SourceType string
SourceURL string
PublishedAssetRoot *string
ControlCount *int
BoundsJSON map[string]any
MetadataJSON map[string]any
}
type CreateResourcePackParams struct {
PublicID string
Code string
Name string
Status string
Description *string
}
type CreateResourcePackVersionParams struct {
PublicID string
ResourcePackID string
VersionCode string
Status string
ContentEntryURL *string
AudioRootURL *string
ThemeProfileCode *string
PublishedAssetRoot *string
MetadataJSON map[string]any
}
func (s *Store) ListResourceMaps(ctx context.Context, limit int) ([]ResourceMap, error) {
if limit <= 0 || limit > 200 {
limit = 50
}
rows, err := s.pool.Query(ctx, `
SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
FROM maps
ORDER BY created_at DESC
LIMIT $1
`, limit)
if err != nil {
return nil, fmt.Errorf("list resource maps: %w", err)
}
defer rows.Close()
items := []ResourceMap{}
for rows.Next() {
item, err := scanResourceMapFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource maps: %w", err)
}
return items, nil
}
func (s *Store) GetResourceMapByPublicID(ctx context.Context, publicID string) (*ResourceMap, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
FROM maps
WHERE map_public_id = $1
LIMIT 1
`, publicID)
return scanResourceMap(row)
}
func (s *Store) CreateResourceMap(ctx context.Context, tx Tx, params CreateResourceMapParams) (*ResourceMap, error) {
row := tx.QueryRow(ctx, `
INSERT INTO maps (map_public_id, code, name, status, description)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, map_public_id, code, name, status, description, current_version_id, created_at, updated_at
`, params.PublicID, params.Code, params.Name, params.Status, params.Description)
return scanResourceMap(row)
}
func (s *Store) ListResourceMapVersions(ctx context.Context, mapID string) ([]ResourceMapVersion, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
FROM map_versions
WHERE map_id = $1
ORDER BY created_at DESC
`, mapID)
if err != nil {
return nil, fmt.Errorf("list resource map versions: %w", err)
}
defer rows.Close()
items := []ResourceMapVersion{}
for rows.Next() {
item, err := scanResourceMapVersionFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource map versions: %w", err)
}
return items, nil
}
func (s *Store) GetResourceMapVersionByPublicID(ctx context.Context, mapPublicID, versionPublicID string) (*ResourceMapVersion, error) {
row := s.pool.QueryRow(ctx, `
SELECT mv.id, mv.version_public_id, mv.map_id, mv.version_code, mv.status, mv.mapmeta_url, mv.tiles_root_url, mv.published_asset_root,
mv.bounds_jsonb::text, mv.metadata_jsonb::text, mv.created_at, mv.updated_at
FROM map_versions mv
JOIN maps m ON m.id = mv.map_id
WHERE m.map_public_id = $1
AND mv.version_public_id = $2
LIMIT 1
`, mapPublicID, versionPublicID)
return scanResourceMapVersion(row)
}
func (s *Store) CreateResourceMapVersion(ctx context.Context, tx Tx, params CreateResourceMapVersionParams) (*ResourceMapVersion, error) {
boundsJSON, err := marshalJSONMap(params.BoundsJSON)
if err != nil {
return nil, fmt.Errorf("marshal map bounds: %w", err)
}
metadataJSON, err := marshalJSONMap(params.MetadataJSON)
if err != nil {
return nil, fmt.Errorf("marshal map metadata: %w", err)
}
row := tx.QueryRow(ctx, `
INSERT INTO map_versions (
version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url,
published_asset_root, bounds_jsonb, metadata_jsonb
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb)
RETURNING id, version_public_id, map_id, version_code, status, mapmeta_url, tiles_root_url, published_asset_root,
bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
`, params.PublicID, params.MapID, params.VersionCode, params.Status, params.MapmetaURL, params.TilesRootURL, params.PublishedAssetRoot, boundsJSON, metadataJSON)
return scanResourceMapVersion(row)
}
func (s *Store) SetResourceMapCurrentVersion(ctx context.Context, tx Tx, mapID, versionID string) error {
_, err := tx.Exec(ctx, `UPDATE maps SET current_version_id = $2 WHERE id = $1`, mapID, versionID)
if err != nil {
return fmt.Errorf("set resource map current version: %w", err)
}
return nil
}
func (s *Store) ListResourcePlayfields(ctx context.Context, limit int) ([]ResourcePlayfield, error) {
if limit <= 0 || limit > 200 {
limit = 50
}
rows, err := s.pool.Query(ctx, `
SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
FROM playfields
ORDER BY created_at DESC
LIMIT $1
`, limit)
if err != nil {
return nil, fmt.Errorf("list resource playfields: %w", err)
}
defer rows.Close()
items := []ResourcePlayfield{}
for rows.Next() {
item, err := scanResourcePlayfieldFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource playfields: %w", err)
}
return items, nil
}
func (s *Store) GetResourcePlayfieldByPublicID(ctx context.Context, publicID string) (*ResourcePlayfield, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
FROM playfields
WHERE playfield_public_id = $1
LIMIT 1
`, publicID)
return scanResourcePlayfield(row)
}
func (s *Store) CreateResourcePlayfield(ctx context.Context, tx Tx, params CreateResourcePlayfieldParams) (*ResourcePlayfield, error) {
row := tx.QueryRow(ctx, `
INSERT INTO playfields (playfield_public_id, code, name, kind, status, description)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, playfield_public_id, code, name, kind, status, description, current_version_id, created_at, updated_at
`, params.PublicID, params.Code, params.Name, params.Kind, params.Status, params.Description)
return scanResourcePlayfield(row)
}
func (s *Store) ListResourcePlayfieldVersions(ctx context.Context, playfieldID string) ([]ResourcePlayfieldVersion, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
FROM playfield_versions
WHERE playfield_id = $1
ORDER BY created_at DESC
`, playfieldID)
if err != nil {
return nil, fmt.Errorf("list resource playfield versions: %w", err)
}
defer rows.Close()
items := []ResourcePlayfieldVersion{}
for rows.Next() {
item, err := scanResourcePlayfieldVersionFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource playfield versions: %w", err)
}
return items, nil
}
func (s *Store) GetResourcePlayfieldVersionByPublicID(ctx context.Context, playfieldPublicID, versionPublicID string) (*ResourcePlayfieldVersion, error) {
row := s.pool.QueryRow(ctx, `
SELECT pv.id, pv.version_public_id, pv.playfield_id, pv.version_code, pv.status, pv.source_type, pv.source_url, pv.published_asset_root,
pv.control_count, pv.bounds_jsonb::text, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
FROM playfield_versions pv
JOIN playfields p ON p.id = pv.playfield_id
WHERE p.playfield_public_id = $1
AND pv.version_public_id = $2
LIMIT 1
`, playfieldPublicID, versionPublicID)
return scanResourcePlayfieldVersion(row)
}
func (s *Store) CreateResourcePlayfieldVersion(ctx context.Context, tx Tx, params CreateResourcePlayfieldVersionParams) (*ResourcePlayfieldVersion, error) {
boundsJSON, err := marshalJSONMap(params.BoundsJSON)
if err != nil {
return nil, fmt.Errorf("marshal playfield bounds: %w", err)
}
metadataJSON, err := marshalJSONMap(params.MetadataJSON)
if err != nil {
return nil, fmt.Errorf("marshal playfield metadata: %w", err)
}
row := tx.QueryRow(ctx, `
INSERT INTO playfield_versions (
version_public_id, playfield_id, version_code, status, source_type, source_url,
published_asset_root, control_count, bounds_jsonb, metadata_jsonb
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb)
RETURNING id, version_public_id, playfield_id, version_code, status, source_type, source_url, published_asset_root,
control_count, bounds_jsonb::text, metadata_jsonb::text, created_at, updated_at
`, params.PublicID, params.PlayfieldID, params.VersionCode, params.Status, params.SourceType, params.SourceURL, params.PublishedAssetRoot, params.ControlCount, boundsJSON, metadataJSON)
return scanResourcePlayfieldVersion(row)
}
func (s *Store) SetResourcePlayfieldCurrentVersion(ctx context.Context, tx Tx, playfieldID, versionID string) error {
_, err := tx.Exec(ctx, `UPDATE playfields SET current_version_id = $2 WHERE id = $1`, playfieldID, versionID)
if err != nil {
return fmt.Errorf("set resource playfield current version: %w", err)
}
return nil
}
func (s *Store) ListResourcePacks(ctx context.Context, limit int) ([]ResourcePack, error) {
if limit <= 0 || limit > 200 {
limit = 50
}
rows, err := s.pool.Query(ctx, `
SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
FROM resource_packs
ORDER BY created_at DESC
LIMIT $1
`, limit)
if err != nil {
return nil, fmt.Errorf("list resource packs: %w", err)
}
defer rows.Close()
items := []ResourcePack{}
for rows.Next() {
item, err := scanResourcePackFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource packs: %w", err)
}
return items, nil
}
func (s *Store) GetResourcePackByPublicID(ctx context.Context, publicID string) (*ResourcePack, error) {
row := s.pool.QueryRow(ctx, `
SELECT id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
FROM resource_packs
WHERE resource_pack_public_id = $1
LIMIT 1
`, publicID)
return scanResourcePack(row)
}
func (s *Store) CreateResourcePack(ctx context.Context, tx Tx, params CreateResourcePackParams) (*ResourcePack, error) {
row := tx.QueryRow(ctx, `
INSERT INTO resource_packs (resource_pack_public_id, code, name, status, description)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, resource_pack_public_id, code, name, status, description, current_version_id, created_at, updated_at
`, params.PublicID, params.Code, params.Name, params.Status, params.Description)
return scanResourcePack(row)
}
func (s *Store) ListResourcePackVersions(ctx context.Context, resourcePackID string) ([]ResourcePackVersion, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
FROM resource_pack_versions
WHERE resource_pack_id = $1
ORDER BY created_at DESC
`, resourcePackID)
if err != nil {
return nil, fmt.Errorf("list resource pack versions: %w", err)
}
defer rows.Close()
items := []ResourcePackVersion{}
for rows.Next() {
item, err := scanResourcePackVersionFromRows(rows)
if err != nil {
return nil, err
}
items = append(items, *item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate resource pack versions: %w", err)
}
return items, nil
}
func (s *Store) GetResourcePackVersionByPublicID(ctx context.Context, resourcePackPublicID, versionPublicID string) (*ResourcePackVersion, error) {
row := s.pool.QueryRow(ctx, `
SELECT pv.id, pv.version_public_id, pv.resource_pack_id, pv.version_code, pv.status, pv.content_entry_url, pv.audio_root_url,
pv.theme_profile_code, pv.published_asset_root, pv.metadata_jsonb::text, pv.created_at, pv.updated_at
FROM resource_pack_versions pv
JOIN resource_packs rp ON rp.id = pv.resource_pack_id
WHERE rp.resource_pack_public_id = $1
AND pv.version_public_id = $2
LIMIT 1
`, resourcePackPublicID, versionPublicID)
return scanResourcePackVersion(row)
}
func (s *Store) CreateResourcePackVersion(ctx context.Context, tx Tx, params CreateResourcePackVersionParams) (*ResourcePackVersion, error) {
metadataJSON, err := marshalJSONMap(params.MetadataJSON)
if err != nil {
return nil, fmt.Errorf("marshal resource pack metadata: %w", err)
}
row := tx.QueryRow(ctx, `
INSERT INTO resource_pack_versions (
version_public_id, resource_pack_id, version_code, status, content_entry_url,
audio_root_url, theme_profile_code, published_asset_root, metadata_jsonb
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)
RETURNING id, version_public_id, resource_pack_id, version_code, status, content_entry_url, audio_root_url,
theme_profile_code, published_asset_root, metadata_jsonb::text, created_at, updated_at
`, params.PublicID, params.ResourcePackID, params.VersionCode, params.Status, params.ContentEntryURL, params.AudioRootURL, params.ThemeProfileCode, params.PublishedAssetRoot, metadataJSON)
return scanResourcePackVersion(row)
}
func (s *Store) SetResourcePackCurrentVersion(ctx context.Context, tx Tx, resourcePackID, versionID string) error {
_, err := tx.Exec(ctx, `UPDATE resource_packs SET current_version_id = $2 WHERE id = $1`, resourcePackID, versionID)
if err != nil {
return fmt.Errorf("set resource pack current version: %w", err)
}
return nil
}
func scanResourceMap(row pgx.Row) (*ResourceMap, error) {
var item ResourceMap
err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource map: %w", err)
}
return &item, nil
}
func scanResourceMapFromRows(rows pgx.Rows) (*ResourceMap, error) {
var item ResourceMap
err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource map row: %w", err)
}
return &item, nil
}
func scanResourceMapVersion(row pgx.Row) (*ResourceMapVersion, error) {
var item ResourceMapVersion
var boundsJSON string
var metadataJSON string
err := row.Scan(&item.ID, &item.PublicID, &item.MapID, &item.VersionCode, &item.Status, &item.MapmetaURL, &item.TilesRootURL, &item.PublishedAssetRoot, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource map version: %w", err)
}
item.BoundsJSON = json.RawMessage(boundsJSON)
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func scanResourceMapVersionFromRows(rows pgx.Rows) (*ResourceMapVersion, error) {
var item ResourceMapVersion
var boundsJSON string
var metadataJSON string
err := rows.Scan(&item.ID, &item.PublicID, &item.MapID, &item.VersionCode, &item.Status, &item.MapmetaURL, &item.TilesRootURL, &item.PublishedAssetRoot, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource map version row: %w", err)
}
item.BoundsJSON = json.RawMessage(boundsJSON)
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func scanResourcePlayfield(row pgx.Row) (*ResourcePlayfield, error) {
var item ResourcePlayfield
err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource playfield: %w", err)
}
return &item, nil
}
func scanResourcePlayfieldFromRows(rows pgx.Rows) (*ResourcePlayfield, error) {
var item ResourcePlayfield
err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Kind, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource playfield row: %w", err)
}
return &item, nil
}
func scanResourcePlayfieldVersion(row pgx.Row) (*ResourcePlayfieldVersion, error) {
var item ResourcePlayfieldVersion
var boundsJSON string
var metadataJSON string
err := row.Scan(&item.ID, &item.PublicID, &item.PlayfieldID, &item.VersionCode, &item.Status, &item.SourceType, &item.SourceURL, &item.PublishedAssetRoot, &item.ControlCount, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource playfield version: %w", err)
}
item.BoundsJSON = json.RawMessage(boundsJSON)
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func scanResourcePlayfieldVersionFromRows(rows pgx.Rows) (*ResourcePlayfieldVersion, error) {
var item ResourcePlayfieldVersion
var boundsJSON string
var metadataJSON string
err := rows.Scan(&item.ID, &item.PublicID, &item.PlayfieldID, &item.VersionCode, &item.Status, &item.SourceType, &item.SourceURL, &item.PublishedAssetRoot, &item.ControlCount, &boundsJSON, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource playfield version row: %w", err)
}
item.BoundsJSON = json.RawMessage(boundsJSON)
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func scanResourcePack(row pgx.Row) (*ResourcePack, error) {
var item ResourcePack
err := row.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource pack: %w", err)
}
return &item, nil
}
func scanResourcePackFromRows(rows pgx.Rows) (*ResourcePack, error) {
var item ResourcePack
err := rows.Scan(&item.ID, &item.PublicID, &item.Code, &item.Name, &item.Status, &item.Description, &item.CurrentVersionID, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource pack row: %w", err)
}
return &item, nil
}
func scanResourcePackVersion(row pgx.Row) (*ResourcePackVersion, error) {
var item ResourcePackVersion
var metadataJSON string
err := row.Scan(&item.ID, &item.PublicID, &item.ResourcePackID, &item.VersionCode, &item.Status, &item.ContentEntryURL, &item.AudioRootURL, &item.ThemeProfileCode, &item.PublishedAssetRoot, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("scan resource pack version: %w", err)
}
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func scanResourcePackVersionFromRows(rows pgx.Rows) (*ResourcePackVersion, error) {
var item ResourcePackVersion
var metadataJSON string
err := rows.Scan(&item.ID, &item.PublicID, &item.ResourcePackID, &item.VersionCode, &item.Status, &item.ContentEntryURL, &item.AudioRootURL, &item.ThemeProfileCode, &item.PublishedAssetRoot, &metadataJSON, &item.CreatedAt, &item.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("scan resource pack version row: %w", err)
}
item.MetadataJSON = json.RawMessage(metadataJSON)
return &item, nil
}
func marshalJSONMap(value map[string]any) (string, error) {
if value == nil {
value = map[string]any{}
}
raw, err := json.Marshal(value)
if err != nil {
return "", err
}
return string(raw), nil
}