From dc6311f0505cf6cd0a0136490757bbd82d225737 Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 6 Feb 2026 09:23:13 -0800 Subject: [PATCH] fix(db): add multi-tenancy migration for knowledge-service tables Adds tenant_id UUID column to user_memories, system_experiences, knowledge_articles, and knowledge_chunks tables with default tenant backfill and indexes. Migration already applied to production DB. Co-Authored-By: Claude Opus 4.6 --- .../AddMultiTenancyToKnowledgeService.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 packages/services/knowledge-service/src/migrations/AddMultiTenancyToKnowledgeService.ts diff --git a/packages/services/knowledge-service/src/migrations/AddMultiTenancyToKnowledgeService.ts b/packages/services/knowledge-service/src/migrations/AddMultiTenancyToKnowledgeService.ts new file mode 100644 index 0000000..bc4928b --- /dev/null +++ b/packages/services/knowledge-service/src/migrations/AddMultiTenancyToKnowledgeService.ts @@ -0,0 +1,125 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +/** + * 为 knowledge-service 的四张表添加 tenant_id 列 + * + * 影响的表: + * - user_memories + * - system_experiences + * - knowledge_articles + * - knowledge_chunks + * + * 使用默认租户 ID 回填现有数据 + */ +export class AddMultiTenancyToKnowledgeService1738900100000 + implements MigrationInterface +{ + name = 'AddMultiTenancyToKnowledgeService1738900100000'; + + private readonly DEFAULT_TENANT_ID = + '00000000-0000-0000-0000-000000000001'; + + public async up(queryRunner: QueryRunner): Promise { + // ========== 1. user_memories 表 ========== + await queryRunner.query( + `ALTER TABLE "user_memories" ADD COLUMN IF NOT EXISTS "tenant_id" UUID`, + ); + await queryRunner.query( + `UPDATE "user_memories" SET "tenant_id" = '${this.DEFAULT_TENANT_ID}' WHERE "tenant_id" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "user_memories" ALTER COLUMN "tenant_id" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "user_memories" ALTER COLUMN "tenant_id" SET DEFAULT '${this.DEFAULT_TENANT_ID}'`, + ); + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "idx_user_memories_tenant" ON "user_memories" ("tenant_id")`, + ); + + // ========== 2. system_experiences 表 ========== + await queryRunner.query( + `ALTER TABLE "system_experiences" ADD COLUMN IF NOT EXISTS "tenant_id" UUID`, + ); + await queryRunner.query( + `UPDATE "system_experiences" SET "tenant_id" = '${this.DEFAULT_TENANT_ID}' WHERE "tenant_id" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "system_experiences" ALTER COLUMN "tenant_id" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "system_experiences" ALTER COLUMN "tenant_id" SET DEFAULT '${this.DEFAULT_TENANT_ID}'`, + ); + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "idx_system_experiences_tenant" ON "system_experiences" ("tenant_id")`, + ); + + // ========== 3. knowledge_articles 表 ========== + await queryRunner.query( + `ALTER TABLE "knowledge_articles" ADD COLUMN IF NOT EXISTS "tenant_id" UUID`, + ); + await queryRunner.query( + `UPDATE "knowledge_articles" SET "tenant_id" = '${this.DEFAULT_TENANT_ID}' WHERE "tenant_id" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_articles" ALTER COLUMN "tenant_id" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_articles" ALTER COLUMN "tenant_id" SET DEFAULT '${this.DEFAULT_TENANT_ID}'`, + ); + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "idx_knowledge_articles_tenant" ON "knowledge_articles" ("tenant_id")`, + ); + + // ========== 4. knowledge_chunks 表 ========== + await queryRunner.query( + `ALTER TABLE "knowledge_chunks" ADD COLUMN IF NOT EXISTS "tenant_id" UUID`, + ); + await queryRunner.query( + `UPDATE "knowledge_chunks" SET "tenant_id" = '${this.DEFAULT_TENANT_ID}' WHERE "tenant_id" IS NULL`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_chunks" ALTER COLUMN "tenant_id" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_chunks" ALTER COLUMN "tenant_id" SET DEFAULT '${this.DEFAULT_TENANT_ID}'`, + ); + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "idx_knowledge_chunks_tenant" ON "knowledge_chunks" ("tenant_id")`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // knowledge_chunks + await queryRunner.query( + `DROP INDEX IF EXISTS "idx_knowledge_chunks_tenant"`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_chunks" DROP COLUMN IF EXISTS "tenant_id"`, + ); + + // knowledge_articles + await queryRunner.query( + `DROP INDEX IF EXISTS "idx_knowledge_articles_tenant"`, + ); + await queryRunner.query( + `ALTER TABLE "knowledge_articles" DROP COLUMN IF EXISTS "tenant_id"`, + ); + + // system_experiences + await queryRunner.query( + `DROP INDEX IF EXISTS "idx_system_experiences_tenant"`, + ); + await queryRunner.query( + `ALTER TABLE "system_experiences" DROP COLUMN IF EXISTS "tenant_id"`, + ); + + // user_memories + await queryRunner.query( + `DROP INDEX IF EXISTS "idx_user_memories_tenant"`, + ); + await queryRunner.query( + `ALTER TABLE "user_memories" DROP COLUMN IF EXISTS "tenant_id"`, + ); + } +}