From 75c49951b7fd40e8af1585ac69902cc4833004df Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 12 Dec 2025 12:29:11 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=9A=84=20accountSequence=20=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=92=8C=E6=8E=A8=E8=8D=90=E5=85=B3=E7=B3=BB=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. referral-service: 修复 userId 从临时值 0 导致的 "用户ID必须大于0" 错误 - 从 accountSequence 提取数值部分作为 userId (去掉 "D" 前缀) - 避免依赖 identity-service 发送的临时 userId 2. 多服务 migration 修复: accountSequence/inviterSequence 类型从 BIGINT 改为 VARCHAR(12) - identity-service: account_sequence, inviter_sequence - authorization-service: account_sequence - blockchain-service: account_sequence - referral-service: account_sequence - reward-service: account_sequence - backup-service: account_sequence 3. mpc-service 与 backup-service 集成: - mpc-service: 添加 BACKUP_SERVICE_URL, BACKUP_SERVICE_ENABLED, SERVICE_JWT_SECRET - backup-service: ALLOWED_SERVICES 添加 mpc-service 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../migration.sql | 16 +- .../backup-service/docker-compose.yml | 94 +-- .../20241204000000_init/migration.sql | 2 +- .../backup-service/prisma/schema.prisma | 2 +- .../migration.sql | 6 +- .../20241204000000_init/migration.sql | 704 +++++++++--------- .../services/mpc-service/docker-compose.yml | 4 + .../00000000000000_init/migration.sql | 4 +- .../event-handlers/user-registered.handler.ts | 16 +- .../migration.sql | 6 +- 10 files changed, 430 insertions(+), 424 deletions(-) diff --git a/backend/services/authorization-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql b/backend/services/authorization-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql index c706d79a..27615457 100644 --- a/backend/services/authorization-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql +++ b/backend/services/authorization-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql @@ -1,14 +1,14 @@ -- Step 1: Add account_sequence columns to all tables first -ALTER TABLE "authorization_roles" ADD COLUMN "account_sequence" BIGINT; -ALTER TABLE "monthly_assessments" ADD COLUMN "account_sequence" BIGINT; -ALTER TABLE "monthly_bypasses" ADD COLUMN "account_sequence" BIGINT; -ALTER TABLE "stickman_rankings" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "authorization_roles" ADD COLUMN "account_sequence" TEXT; +ALTER TABLE "monthly_assessments" ADD COLUMN "account_sequence" TEXT; +ALTER TABLE "monthly_bypasses" ADD COLUMN "account_sequence" TEXT; +ALTER TABLE "stickman_rankings" ADD COLUMN "account_sequence" TEXT; -- Step 2: Backfill account_sequence from existing user_id (which is still String at this point) -UPDATE "authorization_roles" SET "account_sequence" = CAST("user_id" AS BIGINT) WHERE "account_sequence" IS NULL; -UPDATE "monthly_assessments" SET "account_sequence" = CAST("user_id" AS BIGINT) WHERE "account_sequence" IS NULL; -UPDATE "monthly_bypasses" SET "account_sequence" = CAST("user_id" AS BIGINT) WHERE "account_sequence" IS NULL; -UPDATE "stickman_rankings" SET "account_sequence" = CAST("user_id" AS BIGINT) WHERE "account_sequence" IS NULL; +UPDATE "authorization_roles" SET "account_sequence" = "user_id" WHERE "account_sequence" IS NULL; +UPDATE "monthly_assessments" SET "account_sequence" = "user_id" WHERE "account_sequence" IS NULL; +UPDATE "monthly_bypasses" SET "account_sequence" = "user_id" WHERE "account_sequence" IS NULL; +UPDATE "stickman_rankings" SET "account_sequence" = "user_id" WHERE "account_sequence" IS NULL; -- Step 3: Make account_sequence NOT NULL ALTER TABLE "authorization_roles" ALTER COLUMN "account_sequence" SET NOT NULL; diff --git a/backend/services/backup-service/docker-compose.yml b/backend/services/backup-service/docker-compose.yml index 80c6c996..1c49e8fb 100644 --- a/backend/services/backup-service/docker-compose.yml +++ b/backend/services/backup-service/docker-compose.yml @@ -1,47 +1,47 @@ -# ============================================================================= -# Backup Service - Docker Compose (Development/Standalone) -# ============================================================================= -# For production, use the root docker-compose.yml in ../ -# -# For standalone development: -# 1. First start shared infrastructure: cd .. && ./deploy.sh up postgres redis kafka -# 2. Then: docker compose up -d --build -# ============================================================================= - -services: - backup-service: - build: - context: . - dockerfile: Dockerfile - container_name: rwa-backup-service - ports: - - "3002:3002" - environment: - # Application - - NODE_ENV=production - - APP_PORT=3002 - - APP_ENV=production - # Database (shared PostgreSQL) - - DATABASE_URL=postgresql://rwa_user:rwa_secure_password@rwa-postgres:5432/rwa_backup?schema=public - # Service Authentication - - SERVICE_JWT_SECRET=${SERVICE_JWT_SECRET:-your-service-jwt-secret} - - ALLOWED_SERVICES=identity-service,recovery-service - # Backup Encryption - - BACKUP_ENCRYPTION_KEY=${BACKUP_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef} - - BACKUP_ENCRYPTION_KEY_ID=${BACKUP_ENCRYPTION_KEY_ID:-key-v1} - # Rate Limits - - MAX_RETRIEVE_PER_DAY=3 - - MAX_STORE_PER_MINUTE=10 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3002/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - restart: unless-stopped - networks: - - rwa-network - -networks: - rwa-network: - external: true +# ============================================================================= +# Backup Service - Docker Compose (Development/Standalone) +# ============================================================================= +# For production, use the root docker-compose.yml in ../ +# +# For standalone development: +# 1. First start shared infrastructure: cd .. && ./deploy.sh up postgres redis kafka +# 2. Then: docker compose up -d --build +# ============================================================================= + +services: + backup-service: + build: + context: . + dockerfile: Dockerfile + container_name: rwa-backup-service + ports: + - "3002:3002" + environment: + # Application + - NODE_ENV=production + - APP_PORT=3002 + - APP_ENV=production + # Database (shared PostgreSQL) + - DATABASE_URL=postgresql://rwa_user:rwa_secure_password@rwa-postgres:5432/rwa_backup?schema=public + # Service Authentication + - SERVICE_JWT_SECRET=${SERVICE_JWT_SECRET:-your-service-jwt-secret} + - ALLOWED_SERVICES=identity-service,recovery-service,mpc-service + # Backup Encryption + - BACKUP_ENCRYPTION_KEY=${BACKUP_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef} + - BACKUP_ENCRYPTION_KEY_ID=${BACKUP_ENCRYPTION_KEY_ID:-key-v1} + # Rate Limits + - MAX_RETRIEVE_PER_DAY=3 + - MAX_STORE_PER_MINUTE=10 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3002/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + restart: unless-stopped + networks: + - rwa-network + +networks: + rwa-network: + external: true diff --git a/backend/services/backup-service/prisma/migrations/20241204000000_init/migration.sql b/backend/services/backup-service/prisma/migrations/20241204000000_init/migration.sql index 08adc4f8..ac1126f2 100644 --- a/backend/services/backup-service/prisma/migrations/20241204000000_init/migration.sql +++ b/backend/services/backup-service/prisma/migrations/20241204000000_init/migration.sql @@ -2,7 +2,7 @@ CREATE TABLE "backup_shares" ( "share_id" BIGSERIAL NOT NULL, "user_id" BIGINT NOT NULL, - "account_sequence" BIGINT NOT NULL, + "account_sequence" VARCHAR(12) NOT NULL, "public_key" VARCHAR(130) NOT NULL, "party_index" INTEGER NOT NULL DEFAULT 2, "threshold" INTEGER NOT NULL DEFAULT 2, diff --git a/backend/services/backup-service/prisma/schema.prisma b/backend/services/backup-service/prisma/schema.prisma index 5a82da15..367c2b27 100644 --- a/backend/services/backup-service/prisma/schema.prisma +++ b/backend/services/backup-service/prisma/schema.prisma @@ -14,7 +14,7 @@ model BackupShare { // 用户标识 (来自 identity-service) userId BigInt @unique @map("user_id") - accountSequence String @unique @map("account_sequence") // 格式: D + YYMMDD + 5位序号 + accountSequence String @unique @map("account_sequence") @db.VarChar(12) // 格式: D + YYMMDD + 5位序号 // MPC 密钥信息 publicKey String @unique @map("public_key") @db.VarChar(130) diff --git a/backend/services/blockchain-service/prisma/migrations/20241208000000_add_system_accounts_and_recovery/migration.sql b/backend/services/blockchain-service/prisma/migrations/20241208000000_add_system_accounts_and_recovery/migration.sql index 14dd9afb..d1c9aa02 100644 --- a/backend/services/blockchain-service/prisma/migrations/20241208000000_add_system_accounts_and_recovery/migration.sql +++ b/backend/services/blockchain-service/prisma/migrations/20241208000000_add_system_accounts_and_recovery/migration.sql @@ -5,7 +5,7 @@ -- AlterTable: monitored_addresses -- Add new columns for system accounts and account_sequence ALTER TABLE "monitored_addresses" ADD COLUMN "address_type" VARCHAR(20) NOT NULL DEFAULT 'USER'; -ALTER TABLE "monitored_addresses" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "monitored_addresses" ADD COLUMN "account_sequence" VARCHAR(20); ALTER TABLE "monitored_addresses" ADD COLUMN "system_account_type" VARCHAR(50); ALTER TABLE "monitored_addresses" ADD COLUMN "system_account_id" BIGINT; ALTER TABLE "monitored_addresses" ADD COLUMN "region_code" VARCHAR(10); @@ -16,7 +16,7 @@ ALTER TABLE "monitored_addresses" ALTER COLUMN "user_id" DROP NOT NULL; -- AlterTable: deposit_transactions -- Add new columns for system accounts and account_sequence ALTER TABLE "deposit_transactions" ADD COLUMN "address_type" VARCHAR(20) NOT NULL DEFAULT 'USER'; -ALTER TABLE "deposit_transactions" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "deposit_transactions" ADD COLUMN "account_sequence" VARCHAR(20); ALTER TABLE "deposit_transactions" ADD COLUMN "system_account_type" VARCHAR(50); ALTER TABLE "deposit_transactions" ADD COLUMN "system_account_id" BIGINT; @@ -26,7 +26,7 @@ ALTER TABLE "deposit_transactions" ALTER COLUMN "user_id" DROP NOT NULL; -- CreateTable: recovery_mnemonics CREATE TABLE "recovery_mnemonics" ( "id" BIGSERIAL NOT NULL, - "account_sequence" INTEGER NOT NULL, + "account_sequence" VARCHAR(20) NOT NULL, "public_key" VARCHAR(130) NOT NULL, "encrypted_mnemonic" TEXT NOT NULL, "mnemonic_hash" VARCHAR(64) NOT NULL, diff --git a/backend/services/identity-service/prisma/migrations/20241204000000_init/migration.sql b/backend/services/identity-service/prisma/migrations/20241204000000_init/migration.sql index 446b81f4..d456bde8 100644 --- a/backend/services/identity-service/prisma/migrations/20241204000000_init/migration.sql +++ b/backend/services/identity-service/prisma/migrations/20241204000000_init/migration.sql @@ -1,352 +1,352 @@ --- CreateTable -CREATE TABLE "user_accounts" ( - "user_id" BIGSERIAL NOT NULL, - "account_sequence" BIGINT NOT NULL, - "phone_number" VARCHAR(20), - "nickname" VARCHAR(100) NOT NULL, - "avatar_url" TEXT, - "inviter_sequence" BIGINT, - "referral_code" VARCHAR(10) NOT NULL, - "kyc_status" VARCHAR(20) NOT NULL DEFAULT 'NOT_VERIFIED', - "real_name" VARCHAR(100), - "id_card_number" VARCHAR(20), - "id_card_front_url" VARCHAR(500), - "id_card_back_url" VARCHAR(500), - "kyc_verified_at" TIMESTAMP(3), - "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', - "registered_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "last_login_at" TIMESTAMP(3), - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "user_accounts_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "user_devices" ( - "id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "device_id" VARCHAR(100) NOT NULL, - "device_name" VARCHAR(100), - "device_info" JSONB, - "platform" VARCHAR(20), - "device_model" VARCHAR(100), - "os_version" VARCHAR(50), - "app_version" VARCHAR(20), - "screen_width" INTEGER, - "screen_height" INTEGER, - "locale" VARCHAR(10), - "timezone" VARCHAR(50), - "added_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "last_active_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "user_devices_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "wallet_addresses" ( - "address_id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "chain_type" VARCHAR(20) NOT NULL, - "address" VARCHAR(100) NOT NULL, - "public_key" VARCHAR(130) NOT NULL, - "address_digest" VARCHAR(66) NOT NULL, - "mpc_signature_r" VARCHAR(66) NOT NULL, - "mpc_signature_s" VARCHAR(66) NOT NULL, - "mpc_signature_v" INTEGER NOT NULL, - "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', - "bound_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "wallet_addresses_pkey" PRIMARY KEY ("address_id") -); - --- CreateTable -CREATE TABLE "account_sequence_generator" ( - "id" INTEGER NOT NULL DEFAULT 1, - "current_sequence" BIGINT NOT NULL DEFAULT 0, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "account_sequence_generator_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "user_events" ( - "event_id" BIGSERIAL NOT NULL, - "event_type" VARCHAR(50) NOT NULL, - "aggregate_id" VARCHAR(100) NOT NULL, - "aggregate_type" VARCHAR(50) NOT NULL, - "event_data" JSONB NOT NULL, - "user_id" BIGINT, - "occurred_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "version" INTEGER NOT NULL DEFAULT 1, - - CONSTRAINT "user_events_pkey" PRIMARY KEY ("event_id") -); - --- CreateTable -CREATE TABLE "device_tokens" ( - "id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "device_id" VARCHAR(100) NOT NULL, - "refresh_token_hash" VARCHAR(64) NOT NULL, - "expires_at" TIMESTAMP(3) NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "revoked_at" TIMESTAMP(3), - - CONSTRAINT "device_tokens_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dead_letter_events" ( - "id" BIGSERIAL NOT NULL, - "topic" VARCHAR(100) NOT NULL, - "event_id" VARCHAR(100) NOT NULL, - "event_type" VARCHAR(50) NOT NULL, - "aggregate_id" VARCHAR(100) NOT NULL, - "aggregate_type" VARCHAR(50) NOT NULL, - "payload" JSONB, - "error_message" TEXT NOT NULL, - "error_stack" TEXT, - "retry_count" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "processed_at" TIMESTAMP(3), - - CONSTRAINT "dead_letter_events_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "sms_codes" ( - "id" BIGSERIAL NOT NULL, - "phone_number" VARCHAR(20) NOT NULL, - "code" VARCHAR(10) NOT NULL, - "purpose" VARCHAR(50) NOT NULL, - "expires_at" TIMESTAMP(3) NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "used_at" TIMESTAMP(3), - - CONSTRAINT "sms_codes_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "mpc_key_shares" ( - "share_id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "public_key" VARCHAR(130) NOT NULL, - "party_index" INTEGER NOT NULL, - "threshold" INTEGER NOT NULL DEFAULT 2, - "total_parties" INTEGER NOT NULL DEFAULT 3, - "encrypted_share_data" TEXT NOT NULL, - "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "rotated_at" TIMESTAMP(3), - - CONSTRAINT "mpc_key_shares_pkey" PRIMARY KEY ("share_id") -); - --- CreateTable -CREATE TABLE "mpc_sessions" ( - "session_id" VARCHAR(50) NOT NULL, - "session_type" VARCHAR(20) NOT NULL, - "user_id" BIGINT, - "public_key" VARCHAR(130), - "status" VARCHAR(20) NOT NULL DEFAULT 'PENDING', - "error_message" TEXT, - "message_hash" VARCHAR(66), - "signature_r" VARCHAR(66), - "signature_s" VARCHAR(66), - "signature_v" INTEGER, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "completed_at" TIMESTAMP(3), - - CONSTRAINT "mpc_sessions_pkey" PRIMARY KEY ("session_id") -); - --- CreateTable -CREATE TABLE "referral_links" ( - "link_id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "referral_code" VARCHAR(10) NOT NULL, - "short_code" VARCHAR(10) NOT NULL, - "channel" VARCHAR(50), - "campaign_id" VARCHAR(50), - "click_count" INTEGER NOT NULL DEFAULT 0, - "register_count" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "expires_at" TIMESTAMP(3), - - CONSTRAINT "referral_links_pkey" PRIMARY KEY ("link_id") -); - --- CreateIndex -CREATE UNIQUE INDEX "user_accounts_account_sequence_key" ON "user_accounts"("account_sequence"); - --- CreateIndex -CREATE UNIQUE INDEX "user_accounts_phone_number_key" ON "user_accounts"("phone_number"); - --- CreateIndex -CREATE UNIQUE INDEX "user_accounts_referral_code_key" ON "user_accounts"("referral_code"); - --- CreateIndex -CREATE INDEX "idx_phone" ON "user_accounts"("phone_number"); - --- CreateIndex -CREATE INDEX "idx_sequence" ON "user_accounts"("account_sequence"); - --- CreateIndex -CREATE INDEX "idx_referral_code" ON "user_accounts"("referral_code"); - --- CreateIndex -CREATE INDEX "idx_inviter" ON "user_accounts"("inviter_sequence"); - --- CreateIndex -CREATE INDEX "idx_kyc_status" ON "user_accounts"("kyc_status"); - --- CreateIndex -CREATE INDEX "idx_status" ON "user_accounts"("status"); - --- CreateIndex -CREATE INDEX "idx_device" ON "user_devices"("device_id"); - --- CreateIndex -CREATE INDEX "idx_user" ON "user_devices"("user_id"); - --- CreateIndex -CREATE INDEX "idx_last_active" ON "user_devices"("last_active_at"); - --- CreateIndex -CREATE INDEX "idx_platform" ON "user_devices"("platform"); - --- CreateIndex -CREATE UNIQUE INDEX "uk_user_device" ON "user_devices"("user_id", "device_id"); - --- CreateIndex -CREATE INDEX "idx_wallet_user" ON "wallet_addresses"("user_id"); - --- CreateIndex -CREATE INDEX "idx_address" ON "wallet_addresses"("address"); - --- CreateIndex -CREATE INDEX "idx_public_key" ON "wallet_addresses"("public_key"); - --- CreateIndex -CREATE UNIQUE INDEX "uk_user_chain" ON "wallet_addresses"("user_id", "chain_type"); - --- CreateIndex -CREATE UNIQUE INDEX "uk_chain_address" ON "wallet_addresses"("chain_type", "address"); - --- CreateIndex -CREATE INDEX "idx_aggregate" ON "user_events"("aggregate_type", "aggregate_id"); - --- CreateIndex -CREATE INDEX "idx_event_type" ON "user_events"("event_type"); - --- CreateIndex -CREATE INDEX "idx_event_user" ON "user_events"("user_id"); - --- CreateIndex -CREATE INDEX "idx_occurred" ON "user_events"("occurred_at"); - --- CreateIndex -CREATE UNIQUE INDEX "device_tokens_refresh_token_hash_key" ON "device_tokens"("refresh_token_hash"); - --- CreateIndex -CREATE INDEX "idx_user_device_token" ON "device_tokens"("user_id", "device_id"); - --- CreateIndex -CREATE INDEX "idx_expires" ON "device_tokens"("expires_at"); - --- CreateIndex -CREATE INDEX "idx_topic" ON "dead_letter_events"("topic"); - --- CreateIndex -CREATE INDEX "idx_dead_letter_event_type" ON "dead_letter_events"("event_type"); - --- CreateIndex -CREATE INDEX "idx_dead_letter_created" ON "dead_letter_events"("created_at"); - --- CreateIndex -CREATE INDEX "idx_processed" ON "dead_letter_events"("processed_at"); - --- CreateIndex -CREATE INDEX "idx_phone_purpose" ON "sms_codes"("phone_number", "purpose"); - --- CreateIndex -CREATE INDEX "idx_sms_expires" ON "sms_codes"("expires_at"); - --- CreateIndex -CREATE UNIQUE INDEX "mpc_key_shares_user_id_key" ON "mpc_key_shares"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "mpc_key_shares_public_key_key" ON "mpc_key_shares"("public_key"); - --- CreateIndex -CREATE INDEX "idx_mpc_public_key" ON "mpc_key_shares"("public_key"); - --- CreateIndex -CREATE INDEX "idx_mpc_status" ON "mpc_key_shares"("status"); - --- CreateIndex -CREATE INDEX "idx_session_type" ON "mpc_sessions"("session_type"); - --- CreateIndex -CREATE INDEX "idx_session_user" ON "mpc_sessions"("user_id"); - --- CreateIndex -CREATE INDEX "idx_session_status" ON "mpc_sessions"("status"); - --- CreateIndex -CREATE INDEX "idx_session_created" ON "mpc_sessions"("created_at"); - --- CreateTable -CREATE TABLE "recovery_mnemonics" ( - "id" BIGSERIAL NOT NULL, - "user_id" BIGINT NOT NULL, - "public_key" VARCHAR(130) NOT NULL, - "encrypted_mnemonic" TEXT NOT NULL, - "mnemonic_hash" VARCHAR(64) NOT NULL, - "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', - "is_backed_up" BOOLEAN NOT NULL DEFAULT false, - "revoked_at" TIMESTAMP(3), - "revoked_reason" VARCHAR(200), - "replaced_by_id" BIGINT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "recovery_mnemonics_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "uk_user_active_mnemonic" ON "recovery_mnemonics"("user_id", "status"); - --- CreateIndex -CREATE INDEX "idx_recovery_user" ON "recovery_mnemonics"("user_id"); - --- CreateIndex -CREATE INDEX "idx_recovery_public_key" ON "recovery_mnemonics"("public_key"); - --- CreateIndex -CREATE INDEX "idx_recovery_status" ON "recovery_mnemonics"("status"); - --- CreateIndex -CREATE UNIQUE INDEX "referral_links_short_code_key" ON "referral_links"("short_code"); - --- CreateIndex -CREATE INDEX "idx_referral_link_user" ON "referral_links"("user_id"); - --- CreateIndex -CREATE INDEX "idx_referral_link_code" ON "referral_links"("referral_code"); - --- CreateIndex -CREATE INDEX "idx_referral_link_channel" ON "referral_links"("channel"); - --- CreateIndex -CREATE INDEX "idx_referral_link_created" ON "referral_links"("created_at"); - --- AddForeignKey -ALTER TABLE "user_devices" ADD CONSTRAINT "user_devices_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user_accounts"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "wallet_addresses" ADD CONSTRAINT "wallet_addresses_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user_accounts"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - --- Initialize sequence generator -INSERT INTO "account_sequence_generator" ("id", "current_sequence", "updated_at") -VALUES (1, 0, CURRENT_TIMESTAMP) -ON CONFLICT ("id") DO NOTHING; +-- CreateTable +CREATE TABLE "user_accounts" ( + "user_id" BIGSERIAL NOT NULL, + "account_sequence" VARCHAR(12) NOT NULL, + "phone_number" VARCHAR(20), + "nickname" VARCHAR(100) NOT NULL, + "avatar_url" TEXT, + "inviter_sequence" VARCHAR(12), + "referral_code" VARCHAR(10) NOT NULL, + "kyc_status" VARCHAR(20) NOT NULL DEFAULT 'NOT_VERIFIED', + "real_name" VARCHAR(100), + "id_card_number" VARCHAR(20), + "id_card_front_url" VARCHAR(500), + "id_card_back_url" VARCHAR(500), + "kyc_verified_at" TIMESTAMP(3), + "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', + "registered_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "last_login_at" TIMESTAMP(3), + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "user_accounts_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "user_devices" ( + "id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "device_id" VARCHAR(100) NOT NULL, + "device_name" VARCHAR(100), + "device_info" JSONB, + "platform" VARCHAR(20), + "device_model" VARCHAR(100), + "os_version" VARCHAR(50), + "app_version" VARCHAR(20), + "screen_width" INTEGER, + "screen_height" INTEGER, + "locale" VARCHAR(10), + "timezone" VARCHAR(50), + "added_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "last_active_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "user_devices_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "wallet_addresses" ( + "address_id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "chain_type" VARCHAR(20) NOT NULL, + "address" VARCHAR(100) NOT NULL, + "public_key" VARCHAR(130) NOT NULL, + "address_digest" VARCHAR(66) NOT NULL, + "mpc_signature_r" VARCHAR(66) NOT NULL, + "mpc_signature_s" VARCHAR(66) NOT NULL, + "mpc_signature_v" INTEGER NOT NULL, + "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', + "bound_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "wallet_addresses_pkey" PRIMARY KEY ("address_id") +); + +-- CreateTable +CREATE TABLE "account_sequence_generator" ( + "id" SERIAL NOT NULL, + "date_key" VARCHAR(6) NOT NULL, + "current_sequence" INTEGER NOT NULL DEFAULT 0, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "account_sequence_generator_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex: account_sequence_generator unique constraint +CREATE UNIQUE INDEX "account_sequence_generator_date_key_key" ON "account_sequence_generator"("date_key"); + +-- CreateTable +CREATE TABLE "user_events" ( + "event_id" BIGSERIAL NOT NULL, + "event_type" VARCHAR(50) NOT NULL, + "aggregate_id" VARCHAR(100) NOT NULL, + "aggregate_type" VARCHAR(50) NOT NULL, + "event_data" JSONB NOT NULL, + "user_id" BIGINT, + "occurred_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "version" INTEGER NOT NULL DEFAULT 1, + + CONSTRAINT "user_events_pkey" PRIMARY KEY ("event_id") +); + +-- CreateTable +CREATE TABLE "device_tokens" ( + "id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "device_id" VARCHAR(100) NOT NULL, + "refresh_token_hash" VARCHAR(64) NOT NULL, + "expires_at" TIMESTAMP(3) NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "revoked_at" TIMESTAMP(3), + + CONSTRAINT "device_tokens_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dead_letter_events" ( + "id" BIGSERIAL NOT NULL, + "topic" VARCHAR(100) NOT NULL, + "event_id" VARCHAR(100) NOT NULL, + "event_type" VARCHAR(50) NOT NULL, + "aggregate_id" VARCHAR(100) NOT NULL, + "aggregate_type" VARCHAR(50) NOT NULL, + "payload" JSONB, + "error_message" TEXT NOT NULL, + "error_stack" TEXT, + "retry_count" INTEGER NOT NULL DEFAULT 0, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "processed_at" TIMESTAMP(3), + + CONSTRAINT "dead_letter_events_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "sms_codes" ( + "id" BIGSERIAL NOT NULL, + "phone_number" VARCHAR(20) NOT NULL, + "code" VARCHAR(10) NOT NULL, + "purpose" VARCHAR(50) NOT NULL, + "expires_at" TIMESTAMP(3) NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "used_at" TIMESTAMP(3), + + CONSTRAINT "sms_codes_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "mpc_key_shares" ( + "share_id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "public_key" VARCHAR(130) NOT NULL, + "party_index" INTEGER NOT NULL, + "threshold" INTEGER NOT NULL DEFAULT 2, + "total_parties" INTEGER NOT NULL DEFAULT 3, + "encrypted_share_data" TEXT NOT NULL, + "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "rotated_at" TIMESTAMP(3), + + CONSTRAINT "mpc_key_shares_pkey" PRIMARY KEY ("share_id") +); + +-- CreateTable +CREATE TABLE "mpc_sessions" ( + "session_id" VARCHAR(50) NOT NULL, + "session_type" VARCHAR(20) NOT NULL, + "user_id" BIGINT, + "public_key" VARCHAR(130), + "status" VARCHAR(20) NOT NULL DEFAULT 'PENDING', + "error_message" TEXT, + "message_hash" VARCHAR(66), + "signature_r" VARCHAR(66), + "signature_s" VARCHAR(66), + "signature_v" INTEGER, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "completed_at" TIMESTAMP(3), + + CONSTRAINT "mpc_sessions_pkey" PRIMARY KEY ("session_id") +); + +-- CreateTable +CREATE TABLE "referral_links" ( + "link_id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "referral_code" VARCHAR(10) NOT NULL, + "short_code" VARCHAR(10) NOT NULL, + "channel" VARCHAR(50), + "campaign_id" VARCHAR(50), + "click_count" INTEGER NOT NULL DEFAULT 0, + "register_count" INTEGER NOT NULL DEFAULT 0, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "expires_at" TIMESTAMP(3), + + CONSTRAINT "referral_links_pkey" PRIMARY KEY ("link_id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "user_accounts_account_sequence_key" ON "user_accounts"("account_sequence"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_accounts_phone_number_key" ON "user_accounts"("phone_number"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_accounts_referral_code_key" ON "user_accounts"("referral_code"); + +-- CreateIndex +CREATE INDEX "idx_phone" ON "user_accounts"("phone_number"); + +-- CreateIndex +CREATE INDEX "idx_sequence" ON "user_accounts"("account_sequence"); + +-- CreateIndex +CREATE INDEX "idx_referral_code" ON "user_accounts"("referral_code"); + +-- CreateIndex +CREATE INDEX "idx_inviter" ON "user_accounts"("inviter_sequence"); + +-- CreateIndex +CREATE INDEX "idx_kyc_status" ON "user_accounts"("kyc_status"); + +-- CreateIndex +CREATE INDEX "idx_status" ON "user_accounts"("status"); + +-- CreateIndex +CREATE INDEX "idx_device" ON "user_devices"("device_id"); + +-- CreateIndex +CREATE INDEX "idx_user" ON "user_devices"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_last_active" ON "user_devices"("last_active_at"); + +-- CreateIndex +CREATE INDEX "idx_platform" ON "user_devices"("platform"); + +-- CreateIndex +CREATE UNIQUE INDEX "uk_user_device" ON "user_devices"("user_id", "device_id"); + +-- CreateIndex +CREATE INDEX "idx_wallet_user" ON "wallet_addresses"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_address" ON "wallet_addresses"("address"); + +-- CreateIndex +CREATE INDEX "idx_public_key" ON "wallet_addresses"("public_key"); + +-- CreateIndex +CREATE UNIQUE INDEX "uk_user_chain" ON "wallet_addresses"("user_id", "chain_type"); + +-- CreateIndex +CREATE UNIQUE INDEX "uk_chain_address" ON "wallet_addresses"("chain_type", "address"); + +-- CreateIndex +CREATE INDEX "idx_aggregate" ON "user_events"("aggregate_type", "aggregate_id"); + +-- CreateIndex +CREATE INDEX "idx_event_type" ON "user_events"("event_type"); + +-- CreateIndex +CREATE INDEX "idx_event_user" ON "user_events"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_occurred" ON "user_events"("occurred_at"); + +-- CreateIndex +CREATE UNIQUE INDEX "device_tokens_refresh_token_hash_key" ON "device_tokens"("refresh_token_hash"); + +-- CreateIndex +CREATE INDEX "idx_user_device_token" ON "device_tokens"("user_id", "device_id"); + +-- CreateIndex +CREATE INDEX "idx_expires" ON "device_tokens"("expires_at"); + +-- CreateIndex +CREATE INDEX "idx_topic" ON "dead_letter_events"("topic"); + +-- CreateIndex +CREATE INDEX "idx_dead_letter_event_type" ON "dead_letter_events"("event_type"); + +-- CreateIndex +CREATE INDEX "idx_dead_letter_created" ON "dead_letter_events"("created_at"); + +-- CreateIndex +CREATE INDEX "idx_processed" ON "dead_letter_events"("processed_at"); + +-- CreateIndex +CREATE INDEX "idx_phone_purpose" ON "sms_codes"("phone_number", "purpose"); + +-- CreateIndex +CREATE INDEX "idx_sms_expires" ON "sms_codes"("expires_at"); + +-- CreateIndex +CREATE UNIQUE INDEX "mpc_key_shares_user_id_key" ON "mpc_key_shares"("user_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "mpc_key_shares_public_key_key" ON "mpc_key_shares"("public_key"); + +-- CreateIndex +CREATE INDEX "idx_mpc_public_key" ON "mpc_key_shares"("public_key"); + +-- CreateIndex +CREATE INDEX "idx_mpc_status" ON "mpc_key_shares"("status"); + +-- CreateIndex +CREATE INDEX "idx_session_type" ON "mpc_sessions"("session_type"); + +-- CreateIndex +CREATE INDEX "idx_session_user" ON "mpc_sessions"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_session_status" ON "mpc_sessions"("status"); + +-- CreateIndex +CREATE INDEX "idx_session_created" ON "mpc_sessions"("created_at"); + +-- CreateTable +CREATE TABLE "recovery_mnemonics" ( + "id" BIGSERIAL NOT NULL, + "user_id" BIGINT NOT NULL, + "public_key" VARCHAR(130) NOT NULL, + "encrypted_mnemonic" TEXT NOT NULL, + "mnemonic_hash" VARCHAR(64) NOT NULL, + "status" VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', + "is_backed_up" BOOLEAN NOT NULL DEFAULT false, + "revoked_at" TIMESTAMP(3), + "revoked_reason" VARCHAR(200), + "replaced_by_id" BIGINT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "recovery_mnemonics_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "uk_user_active_mnemonic" ON "recovery_mnemonics"("user_id", "status"); + +-- CreateIndex +CREATE INDEX "idx_recovery_user" ON "recovery_mnemonics"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_recovery_public_key" ON "recovery_mnemonics"("public_key"); + +-- CreateIndex +CREATE INDEX "idx_recovery_status" ON "recovery_mnemonics"("status"); + +-- CreateIndex +CREATE UNIQUE INDEX "referral_links_short_code_key" ON "referral_links"("short_code"); + +-- CreateIndex +CREATE INDEX "idx_referral_link_user" ON "referral_links"("user_id"); + +-- CreateIndex +CREATE INDEX "idx_referral_link_code" ON "referral_links"("referral_code"); + +-- CreateIndex +CREATE INDEX "idx_referral_link_channel" ON "referral_links"("channel"); + +-- CreateIndex +CREATE INDEX "idx_referral_link_created" ON "referral_links"("created_at"); + +-- AddForeignKey +ALTER TABLE "user_devices" ADD CONSTRAINT "user_devices_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user_accounts"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "wallet_addresses" ADD CONSTRAINT "wallet_addresses_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user_accounts"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; + diff --git a/backend/services/mpc-service/docker-compose.yml b/backend/services/mpc-service/docker-compose.yml index 81a051fd..3f94e472 100644 --- a/backend/services/mpc-service/docker-compose.yml +++ b/backend/services/mpc-service/docker-compose.yml @@ -46,6 +46,10 @@ services: MPC_COORDINATOR_TIMEOUT: 30000 # Share Encryption SHARE_MASTER_KEY: ${SHARE_MASTER_KEY:-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef} + # Backup Service + BACKUP_SERVICE_URL: ${BACKUP_SERVICE_URL:-http://rwa-backup-service:3002} + BACKUP_SERVICE_ENABLED: ${BACKUP_SERVICE_ENABLED:-true} + SERVICE_JWT_SECRET: ${SERVICE_JWT_SECRET:-your-service-jwt-secret-change-in-production} # Timeouts MPC_KEYGEN_TIMEOUT: 300000 MPC_SIGNING_TIMEOUT: 180000 diff --git a/backend/services/referral-service/prisma/migrations/00000000000000_init/migration.sql b/backend/services/referral-service/prisma/migrations/00000000000000_init/migration.sql index 88e2002d..97e6a8cf 100644 --- a/backend/services/referral-service/prisma/migrations/00000000000000_init/migration.sql +++ b/backend/services/referral-service/prisma/migrations/00000000000000_init/migration.sql @@ -2,7 +2,7 @@ CREATE TABLE "referral_relationships" ( "relationship_id" BIGSERIAL NOT NULL, "user_id" BIGINT NOT NULL, - "account_sequence" INTEGER NOT NULL, + "account_sequence" VARCHAR(12) NOT NULL, "referrer_id" BIGINT, "root_user_id" BIGINT, "my_referral_code" VARCHAR(20) NOT NULL, @@ -48,7 +48,7 @@ CREATE TABLE "direct_referrals" ( "direct_referral_id" BIGSERIAL NOT NULL, "referrer_id" BIGINT NOT NULL, "referral_id" BIGINT NOT NULL, - "referral_sequence" BIGINT NOT NULL, + "referral_sequence" VARCHAR(12) NOT NULL, "referral_nickname" VARCHAR(100), "referral_avatar" VARCHAR(255), "personal_planting_count" INTEGER NOT NULL DEFAULT 0, diff --git a/backend/services/referral-service/src/application/event-handlers/user-registered.handler.ts b/backend/services/referral-service/src/application/event-handlers/user-registered.handler.ts index ce7a6f12..b6c646aa 100644 --- a/backend/services/referral-service/src/application/event-handlers/user-registered.handler.ts +++ b/backend/services/referral-service/src/application/event-handlers/user-registered.handler.ts @@ -64,15 +64,17 @@ export class UserRegisteredHandler implements OnModuleInit { `Processing ${event.eventType} event: accountSequence=${payload.accountSequence}, inviterSequence=${payload.inviterSequence}`, ); - // 使用 accountSequence 作为 userId,因为 identity-service 的 userId 是内部自增ID, - // 在事件发布时可能还是临时值 0,而 accountSequence 是全局唯一的业务标识 - // 注意:userId 仍然需要是 bigint,这里我们需要从 accountSequence 字符串中提取数值部分或使用其他方式 - // 暂时保持原有逻辑,但 accountSequence 本身现在是字符串类型 + // 从 accountSequence 提取数值部分作为 userId + // accountSequence 格式: D + YYMMDD + 5位序号 (例如: D25121200000) + // 去掉 "D" 前缀后得到 11 位数字,作为全局唯一的 userId + // 这样可以避免依赖 identity-service 的临时 userId (可能是 0) + const userIdFromSequence = BigInt(payload.accountSequence.substring(1)); // 去掉 "D" 前缀 + const command = new CreateReferralRelationshipCommand( - BigInt(payload.userId), // 使用 userId - payload.accountSequence, // 现在是字符串格式 + userIdFromSequence, // 使用从 accountSequence 提取的数值作为 userId + payload.accountSequence, // 完整的 accountSequence 字符串 null, // referrerCode - 不通过推荐码查找 - payload.inviterSequence, // 通过 accountSequence 查找推荐人,现在是字符串格式 + payload.inviterSequence, // 通过 accountSequence 查找推荐人 ); const result = await this.referralService.createReferralRelationship(command); diff --git a/backend/services/reward-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql b/backend/services/reward-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql index 648baa9c..330d74ff 100644 --- a/backend/services/reward-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql +++ b/backend/services/reward-service/prisma/migrations/20241210000001_add_account_sequence/migration.sql @@ -1,19 +1,19 @@ -- Add account_sequence column to reward_ledger_entries -ALTER TABLE "reward_ledger_entries" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "reward_ledger_entries" ADD COLUMN "account_sequence" VARCHAR(20); -- Add indexes for account_sequence on reward_ledger_entries CREATE INDEX "idx_account_status" ON "reward_ledger_entries"("account_sequence", "reward_status"); CREATE INDEX "idx_account_created" ON "reward_ledger_entries"("account_sequence", "created_at" DESC); -- Add account_sequence column to reward_summaries -ALTER TABLE "reward_summaries" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "reward_summaries" ADD COLUMN "account_sequence" VARCHAR(20); -- Add unique constraint and index for account_sequence on reward_summaries CREATE UNIQUE INDEX "reward_summaries_account_sequence_key" ON "reward_summaries"("account_sequence"); CREATE INDEX "idx_summary_account" ON "reward_summaries"("account_sequence"); -- Add account_sequence column to settlement_records -ALTER TABLE "settlement_records" ADD COLUMN "account_sequence" BIGINT; +ALTER TABLE "settlement_records" ADD COLUMN "account_sequence" VARCHAR(20); -- Add index for account_sequence on settlement_records CREATE INDEX "idx_settlement_account" ON "settlement_records"("account_sequence");