diff --git a/backend/services/identity-service/DEPLOYMENT.md b/backend/services/identity-service/DEPLOYMENT.md new file mode 100644 index 00000000..10cddfe7 --- /dev/null +++ b/backend/services/identity-service/DEPLOYMENT.md @@ -0,0 +1,253 @@ +# Identity Service 部署和测试指南 + +## 项目架构验证 + +本项目采用DDD(领域驱动设计)四层架构: + +### 1. 表现层(API Layer) +- 位置:`src/api/` +- 职责:处理HTTP请求,参数验证,响应格式化 +- 主要组件:Controllers, DTOs, Validators + +### 2. 应用层(Application Layer) +- 位置:`src/application/` +- 职责:编排业务流程,事务管理,命令查询分离 +- 主要组件:Commands, Queries, Application Services + +### 3. 领域层(Domain Layer) +- 位置:`src/domain/` +- 职责:业务逻辑,领域模型,业务规则 +- 主要组件:Aggregates, Entities, Value Objects, Domain Services, Events + +### 4. 基础设施层(Infrastructure Layer) +- 位置:`src/infrastructure/` +- 职责:技术实现细节,外部服务集成 +- 主要组件:Repositories, Redis, Kafka, External Services + +## 构建和部署 + +### 环境准备 + +1. **Node.js**: 需要 Node.js 20.x 或更高版本 +2. **Docker**: 用于容器化部署 +3. **PostgreSQL**: 数据库(可通过Docker运行) +4. **Redis**: 缓存服务(可通过Docker运行) +5. **Kafka**: 消息队列(可通过Docker运行) + +### 本地开发 + +1. **克隆项目并安装依赖** +```bash +cd identity-service +npm install +``` + +2. **配置环境变量** +```bash +cp .env.example .env +# 编辑 .env 文件,配置数据库连接等信息 +``` + +3. **启动依赖服务(使用Docker)** +```bash +docker-compose up -d postgres redis kafka +``` + +4. **初始化数据库** +```bash +# 生成Prisma Client +npm run prisma:generate + +# 运行数据库迁移 +npm run prisma:migrate +``` + +5. **启动服务** +```bash +# 开发模式(支持热重载) +npm run start:dev + +# 生产模式 +npm run build +npm run start:prod +``` + +### Docker部署 + +1. **使用Docker Compose一键部署** +```bash +# 构建并启动所有服务 +docker-compose up -d + +# 查看日志 +docker-compose logs -f identity-service + +# 停止服务 +docker-compose down +``` + +2. **单独构建Docker镜像** +```bash +# 构建镜像 +docker build -t identity-service:latest . + +# 运行容器 +docker run -d \ + -p 3000:3000 \ + -e DATABASE_URL="postgresql://user:pass@host:5432/db" \ + -e REDIS_HOST="redis-host" \ + -e KAFKA_BROKERS="kafka:9092" \ + --name identity-service \ + identity-service:latest +``` + +## API测试 + +### 1. 访问Swagger文档 +启动服务后访问:http://localhost:3000/api/docs + +### 2. 主要API端点 + +#### 自动创建账户(首次使用) +```bash +curl -X POST http://localhost:3000/api/v1/user/auto-create \ + -H "Content-Type: application/json" \ + -d '{ + "deviceId": "device-123-456", + "deviceName": "iPhone 15 Pro", + "inviterReferralCode": "ABCD1234", + "provinceCode": "GD", + "cityCode": "GZ" + }' +``` + +#### 用助记词恢复账户 +```bash +curl -X POST http://localhost:3000/api/v1/user/recover-by-mnemonic \ + -H "Content-Type: application/json" \ + -d '{ + "accountSequence": 1000001, + "mnemonic": "your twelve word mnemonic phrase here", + "newDeviceId": "device-789", + "deviceName": "New Device" + }' +``` + +#### 自动登录(Token刷新) +```bash +curl -X POST http://localhost:3000/api/v1/user/auto-login \ + -H "Content-Type: application/json" \ + -d '{ + "refreshToken": "your-refresh-token", + "deviceId": "device-123-456" + }' +``` + +#### 获取个人资料(需要认证) +```bash +curl -X GET http://localhost:3000/api/v1/user/my-profile \ + -H "Authorization: Bearer your-access-token" +``` + +#### 绑定手机号 +```bash +# 1. 发送验证码 +curl -X POST http://localhost:3000/api/v1/user/send-sms-code \ + -H "Content-Type: application/json" \ + -d '{ + "phoneNumber": "+8613812345678", + "type": "BIND_PHONE" + }' + +# 2. 绑定手机号 +curl -X POST http://localhost:3000/api/v1/user/bind-phone \ + -H "Authorization: Bearer your-access-token" \ + -H "Content-Type: application/json" \ + -d '{ + "phoneNumber": "+8613812345678", + "smsCode": "123456" + }' +``` + +## 生产环境部署建议 + +### 1. 数据库优化 +- 使用连接池 +- 设置合适的索引 +- 定期备份 + +### 2. Redis配置 +- 启用持久化(RDB或AOF) +- 设置合适的内存限制 +- 配置密码认证 + +### 3. Kafka配置 +- 设置合适的分区数 +- 配置副本因子 +- 监控消费者滞后 + +### 4. 安全建议 +- 使用强密码的JWT密钥 +- 启用HTTPS +- 配置CORS白名单 +- 使用环境变量管理敏感信息 + +### 5. 监控和日志 +- 集成监控工具(Prometheus、Grafana) +- 配置结构化日志 +- 设置告警规则 + +## 故障排查 + +### 常见问题 + +1. **数据库连接失败** + - 检查DATABASE_URL配置 + - 确认PostgreSQL服务正在运行 + - 检查网络连接 + +2. **Redis连接失败** + - 检查REDIS_HOST和REDIS_PORT + - 确认Redis服务正在运行 + - 检查防火墙设置 + +3. **Kafka连接失败** + - 检查KAFKA_BROKERS配置 + - 确认Kafka和Zookeeper正在运行 + - 检查网络连接 + +4. **Prisma错误** + - 运行 `npm run prisma:generate` + - 检查数据库模式是否最新 + - 运行 `npm run prisma:migrate` + +### 日志查看 +```bash +# Docker日志 +docker-compose logs -f identity-service + +# 本地开发日志 +npm run start:dev +``` + +## 性能优化 + +1. **启用查询优化** + - 使用Prisma的`select`和`include`优化查询 + - 实现分页 + - 使用Redis缓存热数据 + +2. **并发处理** + - 使用NestJS的异步特性 + - 合理配置线程池 + - 使用消息队列处理耗时任务 + +3. **监控指标** + - API响应时间 + - 数据库查询性能 + - Redis命中率 + - Kafka消费延迟 + +## 联系支持 + +如有问题,请查看项目文档或联系开发团队。 diff --git a/backend/services/identity-service/Dockerfile b/backend/services/identity-service/Dockerfile index f9d1b1da..21ba2455 100644 --- a/backend/services/identity-service/Dockerfile +++ b/backend/services/identity-service/Dockerfile @@ -4,7 +4,7 @@ FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ -COPY src/infrastructure/persistence/prisma ./src/infrastructure/persistence/prisma/ +COPY prisma ./prisma/ RUN npm ci @@ -20,7 +20,7 @@ WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist -COPY --from=builder /app/src/infrastructure/persistence/prisma ./src/infrastructure/persistence/prisma +COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/package*.json ./ ENV NODE_ENV=production diff --git a/backend/services/identity-service/package.json b/backend/services/identity-service/package.json index fc9db1c0..986b076d 100644 --- a/backend/services/identity-service/package.json +++ b/backend/services/identity-service/package.json @@ -6,7 +6,7 @@ "private": true, "license": "UNLICENSED", "prisma": { - "schema": "src/infrastructure/persistence/prisma/schema.prisma" + "schema": "prisma/schema.prisma" }, "scripts": { "build": "nest build", diff --git a/backend/services/identity-service/src/infrastructure/persistence/prisma/migrations/.gitkeep b/backend/services/identity-service/prisma/migrations/.gitkeep similarity index 100% rename from backend/services/identity-service/src/infrastructure/persistence/prisma/migrations/.gitkeep rename to backend/services/identity-service/prisma/migrations/.gitkeep diff --git a/backend/services/identity-service/src/infrastructure/persistence/prisma/schema.prisma b/backend/services/identity-service/src/infrastructure/persistence/prisma/schema.prisma deleted file mode 100644 index e15bef5e..00000000 --- a/backend/services/identity-service/src/infrastructure/persistence/prisma/schema.prisma +++ /dev/null @@ -1,133 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -model UserAccount { - userId BigInt @id @default(autoincrement()) @map("user_id") - accountSequence BigInt @unique @map("account_sequence") - - phoneNumber String? @unique @map("phone_number") @db.VarChar(20) - nickname String @db.VarChar(100) - avatarUrl String? @map("avatar_url") @db.VarChar(500) - - inviterSequence BigInt? @map("inviter_sequence") - referralCode String @unique @map("referral_code") @db.VarChar(10) - - provinceCode String @map("province_code") @db.VarChar(10) - cityCode String @map("city_code") @db.VarChar(10) - address String? @db.VarChar(500) - - kycStatus String @default("NOT_VERIFIED") @map("kyc_status") @db.VarChar(20) - realName String? @map("real_name") @db.VarChar(100) - idCardNumber String? @map("id_card_number") @db.VarChar(20) - idCardFrontUrl String? @map("id_card_front_url") @db.VarChar(500) - idCardBackUrl String? @map("id_card_back_url") @db.VarChar(500) - kycVerifiedAt DateTime? @map("kyc_verified_at") - - status String @default("ACTIVE") @db.VarChar(20) - - registeredAt DateTime @default(now()) @map("registered_at") - lastLoginAt DateTime? @map("last_login_at") - updatedAt DateTime @updatedAt @map("updated_at") - - devices UserDevice[] - walletAddresses WalletAddress[] - - @@index([phoneNumber], name: "idx_phone") - @@index([accountSequence], name: "idx_sequence") - @@index([referralCode], name: "idx_referral_code") - @@index([inviterSequence], name: "idx_inviter") - @@index([provinceCode, cityCode], name: "idx_province_city") - @@index([kycStatus], name: "idx_kyc_status") - @@index([status], name: "idx_status") - @@map("user_accounts") -} - -model UserDevice { - id BigInt @id @default(autoincrement()) - userId BigInt @map("user_id") - deviceId String @map("device_id") @db.VarChar(100) - deviceName String? @map("device_name") @db.VarChar(100) - - addedAt DateTime @default(now()) @map("added_at") - lastActiveAt DateTime @default(now()) @map("last_active_at") - - user UserAccount @relation(fields: [userId], references: [userId], onDelete: Cascade) - - @@unique([userId, deviceId], name: "uk_user_device") - @@index([deviceId], name: "idx_device") - @@index([userId], name: "idx_user") - @@index([lastActiveAt], name: "idx_last_active") - @@map("user_devices") -} - -model WalletAddress { - addressId BigInt @id @default(autoincrement()) @map("address_id") - userId BigInt @map("user_id") - - chainType String @map("chain_type") @db.VarChar(20) - address String @db.VarChar(100) - - encryptedMnemonic String? @map("encrypted_mnemonic") @db.Text - - status String @default("ACTIVE") @db.VarChar(20) - - boundAt DateTime @default(now()) @map("bound_at") - - user UserAccount @relation(fields: [userId], references: [userId], onDelete: Cascade) - - @@unique([userId, chainType], name: "uk_user_chain") - @@unique([chainType, address], name: "uk_chain_address") - @@index([userId], name: "idx_wallet_user") - @@index([address], name: "idx_address") - @@map("wallet_addresses") -} - -model AccountSequenceGenerator { - id Int @id @default(1) - currentSequence BigInt @default(0) @map("current_sequence") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("account_sequence_generator") -} - -model UserEvent { - eventId BigInt @id @default(autoincrement()) @map("event_id") - eventType String @map("event_type") @db.VarChar(50) - - aggregateId String @map("aggregate_id") @db.VarChar(100) - aggregateType String @map("aggregate_type") @db.VarChar(50) - - eventData Json @map("event_data") - - userId BigInt? @map("user_id") - occurredAt DateTime @default(now()) @map("occurred_at") @db.Timestamp(6) - version Int @default(1) - - @@index([aggregateType, aggregateId], name: "idx_aggregate") - @@index([eventType], name: "idx_event_type") - @@index([userId], name: "idx_event_user") - @@index([occurredAt], name: "idx_occurred") - @@map("user_events") -} - -model DeviceToken { - id BigInt @id @default(autoincrement()) - userId BigInt @map("user_id") - deviceId String @map("device_id") @db.VarChar(100) - - refreshTokenHash String @unique @map("refresh_token_hash") @db.VarChar(64) - - expiresAt DateTime @map("expires_at") - createdAt DateTime @default(now()) @map("created_at") - revokedAt DateTime? @map("revoked_at") - - @@index([userId, deviceId], name: "idx_user_device_token") - @@index([expiresAt], name: "idx_expires") - @@map("device_tokens") -}