chore(db): 添加用户画像系统数据库迁移
新增表: - tag_categories: 标签分类 - user_tags: 用户标签定义 - user_tag_assignments: 用户-标签关联 - user_classification_rules: 分类规则 - user_features: 用户特征 (RFM等) - audience_segments: 人群包 - user_tag_logs: 标签变更日志 - notification_tag_targets: 通知-标签关联 - notification_user_targets: 通知-用户关联 新增枚举: - TagType, TagValueType, TagAction, SegmentUsageType, TargetLogic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b5e45c4532
commit
fff88d0323
|
|
@ -0,0 +1,255 @@
|
||||||
|
-- =============================================================================
|
||||||
|
-- User Profile System Migration
|
||||||
|
-- 用户画像系统:标签管理、分类规则、人群包
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 1. 枚举类型
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- 标签类型
|
||||||
|
CREATE TYPE "TagType" AS ENUM ('MANUAL', 'AUTO', 'COMPUTED', 'SYSTEM');
|
||||||
|
|
||||||
|
-- 标签值类型
|
||||||
|
CREATE TYPE "TagValueType" AS ENUM ('BOOLEAN', 'ENUM', 'NUMBER', 'STRING');
|
||||||
|
|
||||||
|
-- 标签变更操作
|
||||||
|
CREATE TYPE "TagAction" AS ENUM ('ASSIGN', 'UPDATE', 'REMOVE', 'EXPIRE');
|
||||||
|
|
||||||
|
-- 人群包用途
|
||||||
|
CREATE TYPE "SegmentUsageType" AS ENUM ('GENERAL', 'NOTIFICATION', 'ADVERTISING', 'ANALYTICS');
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 2. 标签分类表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "tag_categories" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"code" VARCHAR(50) NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "tag_categories_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "tag_categories_code_key" ON "tag_categories"("code");
|
||||||
|
CREATE INDEX "tag_categories_code_idx" ON "tag_categories"("code");
|
||||||
|
CREATE INDEX "tag_categories_is_enabled_idx" ON "tag_categories"("is_enabled");
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 3. 用户分类规则表 (必须在 user_tags 之前创建,因为有外键引用)
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "user_classification_rules" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"conditions" JSONB NOT NULL,
|
||||||
|
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_classification_rules_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 4. 用户标签定义表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "user_tags" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"category_id" TEXT,
|
||||||
|
"code" VARCHAR(50) NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"color" VARCHAR(20),
|
||||||
|
"type" "TagType" NOT NULL DEFAULT 'MANUAL',
|
||||||
|
"value_type" "TagValueType" NOT NULL DEFAULT 'BOOLEAN',
|
||||||
|
"enum_values" JSONB,
|
||||||
|
"rule_id" TEXT,
|
||||||
|
"is_advertisable" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"estimated_users" INTEGER,
|
||||||
|
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_tags_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "user_tags_code_key" ON "user_tags"("code");
|
||||||
|
CREATE UNIQUE INDEX "user_tags_rule_id_key" ON "user_tags"("rule_id");
|
||||||
|
CREATE INDEX "user_tags_category_id_idx" ON "user_tags"("category_id");
|
||||||
|
CREATE INDEX "user_tags_code_idx" ON "user_tags"("code");
|
||||||
|
CREATE INDEX "user_tags_type_idx" ON "user_tags"("type");
|
||||||
|
CREATE INDEX "user_tags_is_enabled_idx" ON "user_tags"("is_enabled");
|
||||||
|
CREATE INDEX "user_tags_is_advertisable_idx" ON "user_tags"("is_advertisable");
|
||||||
|
|
||||||
|
-- 外键约束
|
||||||
|
ALTER TABLE "user_tags" ADD CONSTRAINT "user_tags_category_id_fkey"
|
||||||
|
FOREIGN KEY ("category_id") REFERENCES "tag_categories"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE "user_tags" ADD CONSTRAINT "user_tags_rule_id_fkey"
|
||||||
|
FOREIGN KEY ("rule_id") REFERENCES "user_classification_rules"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 5. 用户-标签关联表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "user_tag_assignments" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"account_sequence" VARCHAR(12) NOT NULL,
|
||||||
|
"tag_id" TEXT NOT NULL,
|
||||||
|
"value" VARCHAR(100),
|
||||||
|
"assigned_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"assigned_by" TEXT,
|
||||||
|
"expires_at" TIMESTAMP(3),
|
||||||
|
"source" VARCHAR(50),
|
||||||
|
|
||||||
|
CONSTRAINT "user_tag_assignments_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "user_tag_assignments_account_sequence_tag_id_key" ON "user_tag_assignments"("account_sequence", "tag_id");
|
||||||
|
CREATE INDEX "user_tag_assignments_account_sequence_idx" ON "user_tag_assignments"("account_sequence");
|
||||||
|
CREATE INDEX "user_tag_assignments_tag_id_idx" ON "user_tag_assignments"("tag_id");
|
||||||
|
CREATE INDEX "user_tag_assignments_value_idx" ON "user_tag_assignments"("value");
|
||||||
|
CREATE INDEX "user_tag_assignments_expires_at_idx" ON "user_tag_assignments"("expires_at");
|
||||||
|
|
||||||
|
ALTER TABLE "user_tag_assignments" ADD CONSTRAINT "user_tag_assignments_tag_id_fkey"
|
||||||
|
FOREIGN KEY ("tag_id") REFERENCES "user_tags"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 6. 用户特征表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "user_features" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"account_sequence" VARCHAR(12) NOT NULL,
|
||||||
|
"rfm_recency" INTEGER,
|
||||||
|
"rfm_frequency" INTEGER,
|
||||||
|
"rfm_monetary" DECIMAL(18,2),
|
||||||
|
"rfm_score" INTEGER,
|
||||||
|
"active_level" VARCHAR(20),
|
||||||
|
"last_active_at" TIMESTAMP(3),
|
||||||
|
"value_level" VARCHAR(20),
|
||||||
|
"lifetime_value" DECIMAL(18,2),
|
||||||
|
"lifecycle_stage" VARCHAR(20),
|
||||||
|
"custom_features" JSONB,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_features_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "user_features_account_sequence_key" ON "user_features"("account_sequence");
|
||||||
|
CREATE INDEX "user_features_rfm_score_idx" ON "user_features"("rfm_score");
|
||||||
|
CREATE INDEX "user_features_active_level_idx" ON "user_features"("active_level");
|
||||||
|
CREATE INDEX "user_features_value_level_idx" ON "user_features"("value_level");
|
||||||
|
CREATE INDEX "user_features_lifecycle_stage_idx" ON "user_features"("lifecycle_stage");
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 7. 人群包表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "audience_segments" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"conditions" JSONB NOT NULL,
|
||||||
|
"estimated_users" INTEGER,
|
||||||
|
"last_calculated" TIMESTAMP(3),
|
||||||
|
"usage_type" "SegmentUsageType" NOT NULL DEFAULT 'GENERAL',
|
||||||
|
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"created_by" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "audience_segments_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX "audience_segments_usage_type_idx" ON "audience_segments"("usage_type");
|
||||||
|
CREATE INDEX "audience_segments_is_enabled_idx" ON "audience_segments"("is_enabled");
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 8. 标签变更日志表
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "user_tag_logs" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"account_sequence" VARCHAR(12) NOT NULL,
|
||||||
|
"tag_code" VARCHAR(50) NOT NULL,
|
||||||
|
"action" "TagAction" NOT NULL,
|
||||||
|
"old_value" VARCHAR(100),
|
||||||
|
"new_value" VARCHAR(100),
|
||||||
|
"reason" VARCHAR(200),
|
||||||
|
"operator_id" TEXT,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "user_tag_logs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX "user_tag_logs_account_sequence_created_at_idx" ON "user_tag_logs"("account_sequence", "created_at");
|
||||||
|
CREATE INDEX "user_tag_logs_tag_code_idx" ON "user_tag_logs"("tag_code");
|
||||||
|
CREATE INDEX "user_tag_logs_action_idx" ON "user_tag_logs"("action");
|
||||||
|
CREATE INDEX "user_tag_logs_created_at_idx" ON "user_tag_logs"("created_at");
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 9. 通知-标签关联表 (用于 BY_TAG 定向)
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "notification_tag_targets" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"notification_id" TEXT NOT NULL,
|
||||||
|
"tag_id" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "notification_tag_targets_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "notification_tag_targets_notification_id_tag_id_key" ON "notification_tag_targets"("notification_id", "tag_id");
|
||||||
|
CREATE INDEX "notification_tag_targets_tag_id_idx" ON "notification_tag_targets"("tag_id");
|
||||||
|
|
||||||
|
ALTER TABLE "notification_tag_targets" ADD CONSTRAINT "notification_tag_targets_notification_id_fkey"
|
||||||
|
FOREIGN KEY ("notification_id") REFERENCES "notifications"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE "notification_tag_targets" ADD CONSTRAINT "notification_tag_targets_tag_id_fkey"
|
||||||
|
FOREIGN KEY ("tag_id") REFERENCES "user_tags"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 10. 通知-用户关联表 (用于 SPECIFIC 定向)
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE "notification_user_targets" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"notification_id" TEXT NOT NULL,
|
||||||
|
"account_sequence" VARCHAR(12) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "notification_user_targets_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "notification_user_targets_notification_id_account_sequence_key" ON "notification_user_targets"("notification_id", "account_sequence");
|
||||||
|
CREATE INDEX "notification_user_targets_account_sequence_idx" ON "notification_user_targets"("account_sequence");
|
||||||
|
|
||||||
|
ALTER TABLE "notification_user_targets" ADD CONSTRAINT "notification_user_targets_notification_id_fkey"
|
||||||
|
FOREIGN KEY ("notification_id") REFERENCES "notifications"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- 11. 修改通知表:添加 target_logic 字段,修改 targetType 枚举
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- 先更新 TargetType 枚举添加新值
|
||||||
|
ALTER TYPE "TargetType" ADD VALUE IF NOT EXISTS 'BY_TAG';
|
||||||
|
ALTER TYPE "TargetType" ADD VALUE IF NOT EXISTS 'SPECIFIC';
|
||||||
|
|
||||||
|
-- 创建 TargetLogic 枚举
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE "TargetLogic" AS ENUM ('ANY', 'ALL');
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 添加 target_logic 列
|
||||||
|
ALTER TABLE "notifications" ADD COLUMN IF NOT EXISTS "target_logic" "TargetLogic" NOT NULL DEFAULT 'ANY';
|
||||||
Loading…
Reference in New Issue