UPDATE: Submission module
All checks were successful
Build and Release / release (push) Successful in 1m14s

This commit is contained in:
2026-05-04 09:55:17 +07:00
parent f3f2e09fd5
commit bcc2e192c1
48 changed files with 2918 additions and 359 deletions

View File

@@ -22,4 +22,7 @@ CREATE INDEX idx_projects_status_updated
ON projects (project_status, updated_at DESC);
CREATE INDEX idx_projects_title_trgm
ON projects USING GIN (title gin_trgm_ops);
ON projects USING GIN (title gin_trgm_ops);

View File

@@ -1,17 +1,21 @@
CREATE TABLE IF NOT EXISTS entities (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT,
description TEXT,
thumbnail_url TEXT,
status SMALLINT,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_entities_name_search
ON entities USING GIN (name gin_trgm_ops);
CREATE INDEX idx_entities_project_id ON entities(project_id);
CREATE INDEX idx_entities_created_active
ON entities(created_at DESC)
WHERE is_deleted = false;

View File

@@ -2,8 +2,9 @@ CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE TABLE IF NOT EXISTS wikis (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title TEXT,
content TEXT,
content JSONB,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
@@ -13,9 +14,12 @@ CREATE TABLE IF NOT EXISTS wikis (
CREATE TABLE IF NOT EXISTS entity_wikis (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
wiki_id UUID REFERENCES wikis(id) ON DELETE CASCADE,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, wiki_id)
);
CREATE INDEX idx_entity_wikis_project_id ON entity_wikis(project_id);
CREATE INDEX idx_entity_wikis_wiki_id
ON entity_wikis(wiki_id);
@@ -27,6 +31,8 @@ CREATE INDEX idx_wikis_title_search
ON wikis USING GIN (title gin_trgm_ops)
WHERE is_deleted = false;
CREATE INDEX idx_wikis_project_id ON wikis(project_id);
CREATE TRIGGER trigger_wikis_updated_at
BEFORE UPDATE ON wikis
FOR EACH ROW

View File

@@ -4,6 +4,7 @@ CREATE EXTENSION IF NOT EXISTS postgis;
CREATE TABLE IF NOT EXISTS geometries (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
geo_type SMALLINT NOT NULL DEFAULT 1,
draw_geometry JSONB NOT NULL,
binding JSONB,
@@ -18,15 +19,12 @@ CREATE TABLE IF NOT EXISTS geometries (
CREATE TABLE IF NOT EXISTS entity_geometries (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
geometry_id UUID REFERENCES geometries(id) ON DELETE CASCADE,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, geometry_id)
);
DROP INDEX IF EXISTS idx_geom_draw_geometry;
DROP INDEX IF EXISTS idx_geom_bbox;
DROP INDEX IF EXISTS idx_geom_time_range;
DROP INDEX IF EXISTS idx_entity_geometries_geometry;
DROP INDEX IF EXISTS idx_geom_binding;
DROP INDEX IF EXISTS idx_geom_updated_at;
CREATE INDEX idx_entity_geometries_project_id ON entity_geometries(project_id);
CREATE INDEX idx_geom_draw_geometry
ON geometries USING GIN (draw_geometry);
@@ -49,6 +47,8 @@ CREATE INDEX idx_geom_updated_at
ON geometries (updated_at DESC)
WHERE is_deleted = false;
CREATE INDEX idx_geometries_project_id ON geometries(project_id);
DROP TRIGGER IF EXISTS trigger_geometries_updated_at ON geometries;
CREATE TRIGGER trigger_geometries_updated_at
BEFORE UPDATE ON geometries

View File

@@ -34,3 +34,9 @@ LIMIT sqlc.arg('limit');
-- name: GetCommitsByIDs :many
SELECT * FROM commits WHERE id = ANY($1::uuid[]) AND is_deleted = false;
-- name: UpdateCommitSnapshot :one
UPDATE commits
SET snapshot_json = $2
WHERE id = $1
RETURNING *;

View File

@@ -1,8 +1,8 @@
-- name: CreateEntity :one
INSERT INTO entities (
name, description, thumbnail_url
id, name, slug, description, project_id, status
) VALUES (
$1, $2, $3
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3, $4, $5
)
RETURNING *;
@@ -17,8 +17,10 @@ WHERE id = $1 AND is_deleted = false;
UPDATE entities
SET
name = COALESCE(sqlc.narg('name'), name),
slug = COALESCE(sqlc.narg('slug'), slug),
description = COALESCE(sqlc.narg('description'), description),
thumbnail_url = COALESCE(sqlc.narg('thumbnail_url'), thumbnail_url)
project_id = COALESCE(sqlc.narg('project_id'), project_id),
status = COALESCE(sqlc.narg('status'), status)
WHERE id = sqlc.arg('id') AND is_deleted = false
RETURNING *;
@@ -34,6 +36,7 @@ WHERE id = $1;
SELECT *
FROM entities
WHERE is_deleted = false
AND (sqlc.narg('project_id')::uuid IS NULL OR project_id = sqlc.narg('project_id')::uuid)
AND name ILIKE '%' || sqlc.arg('name')::text || '%'
AND (sqlc.narg('cursor_id')::uuid IS NULL OR id < sqlc.narg('cursor_id')::uuid)
ORDER BY id DESC
@@ -41,3 +44,13 @@ LIMIT sqlc.arg('limit_count');
-- name: GetEntitiesByIDs :many
SELECT * FROM entities WHERE id = ANY($1::uuid[]) AND is_deleted = false;
-- name: GetEntitiesByProjectId :many
SELECT *
FROM entities
WHERE project_id = $1 AND is_deleted = false;
-- name: DeleteEntitiesByIDs :exec
UPDATE entities
SET is_deleted = true
WHERE id = ANY($1::uuid[]);

View File

@@ -1,15 +1,15 @@
-- name: CreateGeometry :one
INSERT INTO geometries (
geo_type, draw_geometry, binding, time_start, time_end, bbox
id, geo_type, draw_geometry, binding, time_start, time_end, bbox, project_id
) VALUES (
$1, $2, $3, $4, $5, ST_MakeEnvelope(sqlc.arg('min_lng')::float8, sqlc.arg('min_lat')::float8, sqlc.arg('max_lng')::float8, sqlc.arg('max_lat')::float8, 4326)
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3, $4, $5, ST_MakeEnvelope(sqlc.arg('min_lng')::float8, sqlc.arg('min_lat')::float8, sqlc.arg('max_lng')::float8, sqlc.arg('max_lat')::float8, 4326), $6
)
RETURNING id, geo_type, draw_geometry, binding, time_start, time_end,
RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id,
ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat,
is_deleted, created_at, updated_at;
-- name: GetGeometryById :one
SELECT id, geo_type, draw_geometry, binding, time_start, time_end,
SELECT id, geo_type, draw_geometry, binding, time_start, time_end, project_id,
ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat,
is_deleted, created_at, updated_at
FROM geometries
@@ -23,6 +23,7 @@ SET
binding = COALESCE(sqlc.narg('binding'), binding),
time_start = COALESCE(sqlc.narg('time_start'), time_start),
time_end = COALESCE(sqlc.narg('time_end'), time_end),
project_id = COALESCE(sqlc.narg('project_id'), project_id),
bbox = CASE
WHEN sqlc.narg('update_bbox')::boolean = true THEN
ST_MakeEnvelope(sqlc.narg('min_lng')::float8, sqlc.narg('min_lat')::float8, sqlc.narg('max_lng')::float8, sqlc.narg('max_lat')::float8, 4326)
@@ -30,7 +31,7 @@ SET
END,
updated_at = now()
WHERE id = sqlc.arg('id') AND is_deleted = false
RETURNING id, geo_type, draw_geometry, binding, time_start, time_end,
RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id,
ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat,
is_deleted, created_at, updated_at;
@@ -42,7 +43,7 @@ WHERE id = $1;
-- name: SearchGeometries :many
SELECT
g.id, g.geo_type, g.draw_geometry, g.binding, g.time_start, g.time_end,
g.id, g.geo_type, g.draw_geometry, g.binding, g.time_start, g.time_end, g.project_id,
ST_XMin(g.bbox)::float8 as min_lng,
ST_YMin(g.bbox)::float8 as min_lat,
ST_XMax(g.bbox)::float8 as max_lng,
@@ -50,6 +51,7 @@ SELECT
g.is_deleted, g.created_at, g.updated_at
FROM geometries g
WHERE g.is_deleted = false
AND (sqlc.narg('project_id')::uuid IS NULL OR g.project_id = sqlc.narg('project_id')::uuid)
AND (
sqlc.narg('search_min_lng')::float8 IS NULL OR
sqlc.narg('search_min_lat')::float8 IS NULL OR
@@ -85,13 +87,18 @@ RETURNING geometry_id;
-- name: CreateEntityGeometries :exec
INSERT INTO entity_geometries (
entity_id, geometry_id
entity_id, geometry_id, project_id
)
SELECT $1, unnest(@geometry_ids::uuid[]);
SELECT $1, unnest(@geometry_ids::uuid[]), $2
ON CONFLICT DO NOTHING;
-- name: DeleteEntityGeometriesByProjectID :exec
DELETE FROM entity_geometries
WHERE project_id = $1;
-- name: GetGeometriesByIDs :many
SELECT
id, geo_type, draw_geometry, binding, time_start, time_end,
id, geo_type, draw_geometry, binding, time_start, time_end, project_id,
ST_XMin(bbox)::float8 as min_lng,
ST_YMin(bbox)::float8 as min_lat,
ST_XMax(bbox)::float8 as max_lng,
@@ -99,3 +106,27 @@ SELECT
is_deleted, created_at, updated_at
FROM geometries
WHERE id = ANY($1::uuid[]) AND is_deleted = false;
-- name: GetGeometriesByProjectId :many
SELECT
id, geo_type, draw_geometry, binding, time_start, time_end, project_id,
ST_XMin(bbox)::float8 as min_lng,
ST_YMin(bbox)::float8 as min_lat,
ST_XMax(bbox)::float8 as max_lng,
ST_YMax(bbox)::float8 as max_lat,
is_deleted, created_at, updated_at
FROM geometries
WHERE project_id = $1 AND is_deleted = false;
-- name: DeleteGeometriesByIDs :exec
UPDATE geometries
SET is_deleted = true
WHERE id = ANY($1::uuid[]);
-- name: BulkDeleteEntityGeometriesByGeometryID :exec
DELETE FROM entity_geometries
WHERE geometry_id = $1;
-- name: DeleteEntityGeometry :exec
DELETE FROM entity_geometries
WHERE entity_id = $1 AND geometry_id = $2;

View File

@@ -17,7 +17,7 @@ SELECT
'avatar_url', up.avatar_url
)::json AS user,
'[]'::json AS commits,
'{}'::uuid[] AS submission_ids,
'[]'::json AS submissions,
'[]'::json AS members
FROM inserted_project p
JOIN users u ON p.user_id = u.id
@@ -32,9 +32,9 @@ SELECT
'[]'
)::json AS commits,
COALESCE(
(SELECT array_agg(id) FROM submissions WHERE project_id = p.id),
'{}'
)::uuid[] AS submission_ids,
(SELECT json_agg(json_build_object('id', s.id, 'status', s.status)) FROM submissions s WHERE s.project_id = p.id),
'[]'
)::json AS submissions,
json_build_object(
'id', u.id,
'email', u.email,
@@ -85,7 +85,10 @@ RETURNING
FROM commits c WHERE c.project_id = projects.id AND c.is_deleted = false),
'[]'
)::json AS commits,
COALESCE((SELECT array_agg(id) FROM submissions WHERE project_id = projects.id), '{}')::uuid[] AS submission_ids,
COALESCE(
(SELECT json_agg(json_build_object('id', s.id, 'status', s.status)) FROM submissions s WHERE s.project_id = projects.id),
'[]'
)::json AS submissions,
COALESCE(
(SELECT json_agg(json_build_object(
'user_id', pm.user_id, 'role', pm.role,
@@ -113,9 +116,9 @@ SELECT
'[]'
)::json AS commits,
COALESCE(
(SELECT array_agg(id) FROM submissions WHERE project_id = p.id),
'{}'
)::uuid[] AS submission_ids,
(SELECT json_agg(json_build_object('id', s.id, 'status', s.status)) FROM submissions s WHERE s.project_id = p.id),
'[]'
)::json AS submissions,
json_build_object(
'id', u.id,
'email', u.email,
@@ -187,9 +190,9 @@ SELECT
'[]'
)::json AS commits,
COALESCE(
(SELECT array_agg(id) FROM submissions WHERE project_id = p.id),
'{}'
)::uuid[] AS submission_ids,
(SELECT json_agg(json_build_object('id', s.id, 'status', s.status)) FROM submissions s WHERE s.project_id = p.id),
'[]'
)::json AS submissions,
json_build_object(
'id', u.id,
'email', u.email,
@@ -225,7 +228,10 @@ SELECT
FROM commits c WHERE c.project_id = p.id AND c.is_deleted = false),
'[]'
)::json AS commits,
COALESCE((SELECT array_agg(id) FROM submissions WHERE project_id = p.id), '{}')::uuid[] AS submission_ids,
COALESCE(
(SELECT json_agg(json_build_object('id', s.id, 'status', s.status)) FROM submissions s WHERE s.project_id = p.id),
'[]'
)::json AS submissions,
json_build_object(
'id', u.id,
'email', u.email,

View File

@@ -1,8 +1,8 @@
-- name: CreateWiki :one
INSERT INTO wikis (
title, content
id, title, content, project_id
) VALUES (
$1, $2
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3
)
RETURNING *;
@@ -15,7 +15,8 @@ WHERE id = $1 AND is_deleted = false;
UPDATE wikis
SET
title = COALESCE(sqlc.narg('title'), title),
content = COALESCE(sqlc.narg('content'), content)
content = COALESCE(sqlc.narg('content'), content),
project_id = COALESCE(sqlc.narg('project_id'), project_id)
WHERE id = sqlc.arg('id') AND is_deleted = false
RETURNING *;
@@ -25,10 +26,12 @@ SET
is_deleted = true
WHERE id = $1;
-- name: SearchWikis :many
SELECT w.*
FROM wikis w
WHERE w.is_deleted = false
AND (sqlc.narg('project_id')::uuid IS NULL OR w.project_id = sqlc.narg('project_id')::uuid)
AND w.title ILIKE '%' || sqlc.arg('title')::text || '%'
AND (
sqlc.narg('entity_id')::uuid IS NULL OR
@@ -51,10 +54,33 @@ RETURNING wiki_id;
-- name: CreateEntityWikis :exec
INSERT INTO entity_wikis (
entity_id, wiki_id
entity_id, wiki_id, project_id
)
SELECT $1, unnest(@wiki_ids::uuid[]);
SELECT $1, unnest(@wiki_ids::uuid[]), $2
ON CONFLICT DO NOTHING;
-- name: DeleteEntityWikisByProjectID :exec
DELETE FROM entity_wikis
WHERE project_id = $1;
-- name: GetWikisByIDs :many
SELECT * FROM wikis WHERE id = ANY($1::uuid[]) AND is_deleted = false;
-- name: GetWikisByProjectId :many
SELECT *
FROM wikis
WHERE project_id = $1 AND is_deleted = false;
-- name: DeleteWikisByIDs :exec
UPDATE wikis
SET is_deleted = true
WHERE id = ANY($1::uuid[]);
-- name: BulkDeleteEntityWikisByWikiID :exec
DELETE FROM entity_wikis
WHERE wiki_id = $1;
-- name: DeleteEntityWiki :exec
DELETE FROM entity_wikis
WHERE entity_id = $1 AND wiki_id = $2;

View File

@@ -70,51 +70,6 @@ CREATE TABLE IF NOT EXISTS verification_medias (
PRIMARY KEY (verification_id, media_id)
);
CREATE TABLE IF NOT EXISTS entities (
id UUID PRIMARY KEY DEFAULT uuidv7(),
name TEXT NOT NULL,
description TEXT,
thumbnail_url TEXT,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wikis (
id UUID PRIMARY KEY DEFAULT uuidv7(),
title TEXT,
content TEXT,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS entity_wikis (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
wiki_id UUID REFERENCES wikis(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, wiki_id)
);
CREATE TABLE IF NOT EXISTS geometries (
id UUID PRIMARY KEY DEFAULT uuidv7(),
geo_type SMALLINT NOT NULL DEFAULT 1,
draw_geometry JSONB NOT NULL,
binding JSONB,
time_start INT,
time_end INT,
bbox GEOMETRY(Polygon, 4326),
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS entity_geometries (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
geometry_id UUID REFERENCES geometries(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, geometry_id)
);
CREATE TABLE IF NOT EXISTS projects (
id UUID PRIMARY KEY DEFAULT uuidv7(),
title TEXT NOT NULL,
@@ -128,6 +83,60 @@ CREATE TABLE IF NOT EXISTS projects (
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS entities (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT,
description TEXT,
status SMALLINT,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wikis (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title TEXT,
content JSONB,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS entity_wikis (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
wiki_id UUID REFERENCES wikis(id) ON DELETE CASCADE,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, wiki_id)
);
CREATE INDEX idx_entity_wikis_project_id ON entity_wikis(project_id);
CREATE TABLE IF NOT EXISTS geometries (
id UUID PRIMARY KEY DEFAULT uuidv7(),
geo_type SMALLINT NOT NULL DEFAULT 1,
draw_geometry JSONB NOT NULL,
binding JSONB,
time_start INT,
time_end INT,
bbox GEOMETRY(Polygon, 4326),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS entity_geometries (
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
geometry_id UUID REFERENCES geometries(id) ON DELETE CASCADE,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
PRIMARY KEY (entity_id, geometry_id)
);
CREATE INDEX idx_entity_geometries_project_id ON entity_geometries(project_id);
CREATE TABLE IF NOT EXISTS commits (
id UUID PRIMARY KEY DEFAULT uuidv7(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,