Scope knowledge and image libraries by brand
Frontend CI / Frontend (push) Successful in 2m59s
Backend CI / Backend (push) Successful in 14m58s

This commit is contained in:
2026-06-30 12:30:10 +08:00
parent 95ddb3057f
commit ced0c4ec0f
35 changed files with 1136 additions and 326 deletions
@@ -0,0 +1,71 @@
ALTER TABLE image_assets
DROP CONSTRAINT IF EXISTS fk_image_assets_folder_brand;
ALTER TABLE image_folders
DROP CONSTRAINT IF EXISTS uq_image_folders_tenant_brand_name;
ALTER TABLE image_folders
DROP CONSTRAINT IF EXISTS uq_image_folders_tenant_brand_id;
DROP INDEX IF EXISTS idx_image_folders_tenant_brand;
DROP INDEX IF EXISTS idx_image_assets_tenant_brand_folder;
DROP INDEX IF EXISTS idx_image_assets_tenant_brand_created;
DROP INDEX IF EXISTS idx_image_assets_tenant_brand_status;
ALTER TABLE image_assets
DROP COLUMN IF EXISTS brand_id;
ALTER TABLE image_folders
DROP COLUMN IF EXISTS brand_id;
ALTER TABLE image_folders
ADD CONSTRAINT uq_image_folders_tenant_name UNIQUE (tenant_id, name);
ALTER TABLE image_folders
ADD CONSTRAINT uq_image_folders_tenant_id UNIQUE (tenant_id, id);
ALTER TABLE image_assets
ADD CONSTRAINT fk_image_assets_folder
FOREIGN KEY (tenant_id, folder_id)
REFERENCES image_folders(tenant_id, id);
CREATE INDEX IF NOT EXISTS idx_image_folders_tenant ON image_folders (tenant_id);
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_folder
ON image_assets (tenant_id, folder_id)
WHERE status = 'active' AND deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_created
ON image_assets (tenant_id, created_at DESC)
WHERE status = 'active' AND deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_status
ON image_assets (tenant_id, status, created_at DESC);
ALTER TABLE knowledge_groups
DROP CONSTRAINT IF EXISTS fk_knowledge_groups_parent_brand;
ALTER TABLE knowledge_items
DROP CONSTRAINT IF EXISTS fk_knowledge_items_group_brand;
ALTER TABLE knowledge_groups
DROP CONSTRAINT IF EXISTS uq_knowledge_groups_tenant_brand_id;
DROP INDEX IF EXISTS uk_knowledge_group_brand_name;
DROP INDEX IF EXISTS idx_knowledge_group_tenant_brand_parent;
DROP INDEX IF EXISTS idx_knowledge_item_tenant_brand_group_status;
ALTER TABLE knowledge_items
DROP COLUMN IF EXISTS brand_id;
ALTER TABLE knowledge_groups
DROP COLUMN IF EXISTS brand_id;
CREATE UNIQUE INDEX IF NOT EXISTS uk_knowledge_group_name
ON knowledge_groups(tenant_id, COALESCE(parent_id, 0), name)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_knowledge_group_tenant_parent
ON knowledge_groups(tenant_id, parent_id, sort_order)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_knowledge_item_group_status
ON knowledge_items(tenant_id, group_id, status)
WHERE deleted_at IS NULL;
@@ -0,0 +1,282 @@
ALTER TABLE knowledge_groups
ADD COLUMN IF NOT EXISTS brand_id BIGINT REFERENCES brands(id);
ALTER TABLE knowledge_items
ADD COLUMN IF NOT EXISTS brand_id BIGINT REFERENCES brands(id);
ALTER TABLE image_folders
ADD COLUMN IF NOT EXISTS brand_id BIGINT REFERENCES brands(id);
ALTER TABLE image_assets
ADD COLUMN IF NOT EXISTS brand_id BIGINT REFERENCES brands(id);
INSERT INTO brands (tenant_id, name, description, status)
SELECT tenant_id, '未归属', '系统迁移生成,用于存放无法自动推断品牌的历史知识库和图片库资料。', 'active'
FROM (
SELECT tenant_id FROM knowledge_groups WHERE brand_id IS NULL
UNION
SELECT tenant_id FROM knowledge_items WHERE brand_id IS NULL
UNION
SELECT tenant_id FROM image_folders WHERE brand_id IS NULL
UNION
SELECT tenant_id FROM image_assets WHERE brand_id IS NULL
) tenants_with_assets
WHERE NOT EXISTS (
SELECT 1
FROM brands b
WHERE b.tenant_id = tenants_with_assets.tenant_id
AND b.deleted_at IS NULL
);
WITH inferred_group_brand AS (
SELECT DISTINCT ON (prkg.knowledge_group_id)
prkg.knowledge_group_id,
pr.brand_id
FROM prompt_rule_knowledge_groups prkg
JOIN prompt_rules pr
ON pr.id = prkg.prompt_rule_id
AND pr.deleted_at IS NULL
JOIN knowledge_groups kg
ON kg.id = prkg.knowledge_group_id
AND kg.tenant_id = pr.tenant_id
AND kg.deleted_at IS NULL
WHERE pr.brand_id IS NOT NULL
ORDER BY prkg.knowledge_group_id, pr.updated_at DESC, pr.id DESC
)
UPDATE knowledge_groups kg
SET brand_id = igb.brand_id,
updated_at = NOW()
FROM inferred_group_brand igb
WHERE kg.id = igb.knowledge_group_id
AND kg.brand_id IS NULL;
UPDATE knowledge_groups child
SET brand_id = parent.brand_id,
updated_at = NOW()
FROM knowledge_groups parent
WHERE child.parent_id = parent.id
AND child.tenant_id = parent.tenant_id
AND parent.brand_id IS NOT NULL;
UPDATE knowledge_groups parent
SET brand_id = child.brand_id,
updated_at = NOW()
FROM knowledge_groups child
WHERE child.parent_id = parent.id
AND child.tenant_id = parent.tenant_id
AND parent.brand_id IS NULL
AND child.brand_id IS NOT NULL;
WITH fallback_brand AS (
SELECT DISTINCT ON (tenant_id) tenant_id, id AS brand_id
FROM brands
WHERE deleted_at IS NULL
ORDER BY tenant_id, created_at, id
)
UPDATE knowledge_groups kg
SET brand_id = fb.brand_id,
updated_at = NOW()
FROM fallback_brand fb
WHERE kg.tenant_id = fb.tenant_id
AND kg.brand_id IS NULL;
UPDATE knowledge_items ki
SET brand_id = kg.brand_id,
updated_at = NOW()
FROM knowledge_groups kg
WHERE ki.group_id = kg.id
AND ki.tenant_id = kg.tenant_id
AND kg.brand_id IS NOT NULL;
WITH inferred_asset_brand AS (
SELECT DISTINCT ON (r.image_asset_id)
r.image_asset_id,
a.brand_id
FROM image_asset_references r
JOIN articles a
ON a.id = r.article_id
AND a.tenant_id = r.tenant_id
AND a.deleted_at IS NULL
WHERE a.brand_id IS NOT NULL
ORDER BY r.image_asset_id, a.updated_at DESC, a.id DESC
)
UPDATE image_assets ia
SET brand_id = iab.brand_id,
updated_at = NOW()
FROM inferred_asset_brand iab
WHERE ia.id = iab.image_asset_id
AND ia.brand_id IS NULL;
WITH schedule_cover_asset_brand AS (
SELECT DISTINCT ON (cover_image_asset_id)
cover_image_asset_id AS image_asset_id,
brand_id
FROM schedule_tasks
WHERE cover_image_asset_id IS NOT NULL
AND brand_id IS NOT NULL
AND deleted_at IS NULL
ORDER BY cover_image_asset_id, updated_at DESC, id DESC
)
UPDATE image_assets ia
SET brand_id = scab.brand_id,
updated_at = NOW()
FROM schedule_cover_asset_brand scab
WHERE ia.id = scab.image_asset_id
AND ia.tenant_id = (SELECT tenant_id FROM brands WHERE id = scab.brand_id)
AND ia.brand_id IS NULL;
WITH schedule_folder_brand AS (
SELECT DISTINCT ON (cover_random_folder_id)
cover_random_folder_id AS folder_id,
brand_id
FROM schedule_tasks
WHERE cover_random_folder_id IS NOT NULL
AND brand_id IS NOT NULL
AND deleted_at IS NULL
ORDER BY cover_random_folder_id, updated_at DESC, id DESC
)
UPDATE image_folders f
SET brand_id = sfb.brand_id,
updated_at = NOW()
FROM schedule_folder_brand sfb
WHERE f.id = sfb.folder_id
AND f.tenant_id = (SELECT tenant_id FROM brands WHERE id = sfb.brand_id)
AND f.brand_id IS NULL;
WITH folder_brand_from_assets AS (
SELECT DISTINCT ON (folder_id)
folder_id,
brand_id
FROM image_assets
WHERE folder_id IS NOT NULL
AND brand_id IS NOT NULL
ORDER BY folder_id, updated_at DESC, id DESC
)
UPDATE image_folders f
SET brand_id = fbfa.brand_id,
updated_at = NOW()
FROM folder_brand_from_assets fbfa
WHERE f.id = fbfa.folder_id
AND f.brand_id IS NULL;
UPDATE image_assets ia
SET brand_id = f.brand_id,
updated_at = NOW()
FROM image_folders f
WHERE ia.folder_id = f.id
AND ia.tenant_id = f.tenant_id
AND f.brand_id IS NOT NULL;
WITH fallback_brand AS (
SELECT DISTINCT ON (tenant_id) tenant_id, id AS brand_id
FROM brands
WHERE deleted_at IS NULL
ORDER BY tenant_id, created_at, id
)
UPDATE image_folders f
SET brand_id = fb.brand_id,
updated_at = NOW()
FROM fallback_brand fb
WHERE f.tenant_id = fb.tenant_id
AND f.brand_id IS NULL;
UPDATE image_assets ia
SET brand_id = f.brand_id,
updated_at = NOW()
FROM image_folders f
WHERE ia.folder_id = f.id
AND ia.tenant_id = f.tenant_id
AND f.brand_id IS NOT NULL;
WITH fallback_brand AS (
SELECT DISTINCT ON (tenant_id) tenant_id, id AS brand_id
FROM brands
WHERE deleted_at IS NULL
ORDER BY tenant_id, created_at, id
)
UPDATE image_assets ia
SET brand_id = fb.brand_id,
updated_at = NOW()
FROM fallback_brand fb
WHERE ia.tenant_id = fb.tenant_id
AND ia.brand_id IS NULL;
ALTER TABLE knowledge_groups
ALTER COLUMN brand_id SET NOT NULL;
ALTER TABLE knowledge_items
ALTER COLUMN brand_id SET NOT NULL;
ALTER TABLE image_folders
ALTER COLUMN brand_id SET NOT NULL;
ALTER TABLE image_assets
ALTER COLUMN brand_id SET NOT NULL;
DROP INDEX IF EXISTS uk_knowledge_group_name;
DROP INDEX IF EXISTS idx_knowledge_group_tenant_parent;
DROP INDEX IF EXISTS idx_knowledge_item_group_status;
CREATE UNIQUE INDEX IF NOT EXISTS uk_knowledge_group_brand_name
ON knowledge_groups(tenant_id, brand_id, COALESCE(parent_id, 0), name)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_knowledge_group_tenant_brand_parent
ON knowledge_groups(tenant_id, brand_id, parent_id, sort_order)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_knowledge_item_tenant_brand_group_status
ON knowledge_items(tenant_id, brand_id, group_id, status)
WHERE deleted_at IS NULL;
ALTER TABLE knowledge_groups
ADD CONSTRAINT uq_knowledge_groups_tenant_brand_id UNIQUE (tenant_id, brand_id, id);
ALTER TABLE knowledge_items
ADD CONSTRAINT fk_knowledge_items_group_brand
FOREIGN KEY (tenant_id, brand_id, group_id)
REFERENCES knowledge_groups(tenant_id, brand_id, id);
ALTER TABLE knowledge_groups
ADD CONSTRAINT fk_knowledge_groups_parent_brand
FOREIGN KEY (tenant_id, brand_id, parent_id)
REFERENCES knowledge_groups(tenant_id, brand_id, id);
ALTER TABLE image_assets
DROP CONSTRAINT IF EXISTS fk_image_assets_folder;
ALTER TABLE image_folders
DROP CONSTRAINT IF EXISTS uq_image_folders_tenant_name;
ALTER TABLE image_folders
DROP CONSTRAINT IF EXISTS uq_image_folders_tenant_id;
ALTER TABLE image_folders
ADD CONSTRAINT uq_image_folders_tenant_brand_name UNIQUE (tenant_id, brand_id, name);
ALTER TABLE image_folders
ADD CONSTRAINT uq_image_folders_tenant_brand_id UNIQUE (tenant_id, brand_id, id);
ALTER TABLE image_assets
ADD CONSTRAINT fk_image_assets_folder_brand
FOREIGN KEY (tenant_id, brand_id, folder_id)
REFERENCES image_folders(tenant_id, brand_id, id);
DROP INDEX IF EXISTS idx_image_folders_tenant;
DROP INDEX IF EXISTS idx_image_assets_tenant_folder;
DROP INDEX IF EXISTS idx_image_assets_tenant_created;
DROP INDEX IF EXISTS idx_image_assets_tenant_status;
CREATE INDEX IF NOT EXISTS idx_image_folders_tenant_brand
ON image_folders (tenant_id, brand_id);
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_brand_folder
ON image_assets (tenant_id, brand_id, folder_id)
WHERE status = 'active' AND deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_brand_created
ON image_assets (tenant_id, brand_id, created_at DESC)
WHERE status = 'active' AND deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_image_assets_tenant_brand_status
ON image_assets (tenant_id, brand_id, status, created_at DESC);