feat(media-supply): persist favorites with multi-group membership
Move media favorites from client state to PostgreSQL with server-side search and pagination. - Store favorites keyed by (tenant, user, group, resource) so a resource can belong to multiple groups; the 500-resource cap counts distinct resources, not memberships. - Add favorite-group CRUD and group/global removal routes, with silent cleanup of delisted or invisible resources on read. - Serve favorite resource details server-side, 10 per page, with name/ID search; favorites-page removal is group-scoped, resources-page removal is global. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS media_supply_favorite_resources;
|
||||
DROP TABLE IF EXISTS media_supply_favorite_groups;
|
||||
DROP TABLE IF EXISTS media_supply_favorite_owners;
|
||||
@@ -0,0 +1,57 @@
|
||||
CREATE TABLE IF NOT EXISTS media_supply_favorite_owners (
|
||||
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
revision BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (tenant_id, user_id),
|
||||
CONSTRAINT chk_media_supply_favorite_owner_revision CHECK (revision >= 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media_supply_favorite_groups (
|
||||
tenant_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
group_key VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (tenant_id, user_id, group_key),
|
||||
CONSTRAINT fk_media_supply_favorite_group_owner
|
||||
FOREIGN KEY (tenant_id, user_id)
|
||||
REFERENCES media_supply_favorite_owners (tenant_id, user_id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT chk_media_supply_favorite_group_key_not_blank CHECK (BTRIM(group_key) <> ''),
|
||||
CONSTRAINT chk_media_supply_favorite_group_name_not_blank CHECK (BTRIM(name) <> '')
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_media_supply_favorite_group_name
|
||||
ON media_supply_favorite_groups (tenant_id, user_id, LOWER(name));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_group_owner_order
|
||||
ON media_supply_favorite_groups (tenant_id, user_id, sort_order, created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media_supply_favorite_resources (
|
||||
tenant_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
group_key VARCHAR(64) NOT NULL,
|
||||
resource_id BIGINT NOT NULL REFERENCES supplier_media_resources(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (tenant_id, user_id, group_key, resource_id),
|
||||
CONSTRAINT fk_media_supply_favorite_resource_group
|
||||
FOREIGN KEY (tenant_id, user_id, group_key)
|
||||
REFERENCES media_supply_favorite_groups (tenant_id, user_id, group_key)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_group_page
|
||||
ON media_supply_favorite_resources (
|
||||
tenant_id,
|
||||
user_id,
|
||||
group_key,
|
||||
created_at DESC,
|
||||
resource_id DESC
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_resource_lookup
|
||||
ON media_supply_favorite_resources (resource_id, tenant_id, user_id);
|
||||
Reference in New Issue
Block a user