×

“迷你京东”全栈架构设计与实现

万邦科技Lex 万邦科技Lex 发表于2026-03-20 09:18:09 浏览21 评论0

抢沙发发表评论

“迷你京东”全栈架构设计与实现

一、项目概述

1.1 项目背景与目标

随着电子商务的快速发展,用户对购物体验的要求越来越高。"迷你京东"作为一个简化版的电商平台,旨在还原京东核心购物流程的同时,采用现代化的技术栈和架构设计,打造一个高性能、高可用、易扩展的全栈电商解决方案。

1.1.1 核心业务场景

┌─────────────────────────────────────────────────────────────────────────────┐
│                        迷你京东核心业务流程                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │  用户    │───▶│  浏览    │───▶│  加购    │───▶│  结算    │             │
│   │  注册    │    │  商品    │    │  商品    │    │  订单    │             │
│   └──────────┘    └──────────┘    └──────────┘    └──────────┘             │
│        │               │               │               │               │
│        ▼               ▼               ▼               ▼               │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │  登录    │    │  搜索    │    │  库存    │    │  支付    │             │
│   │  认证    │    │  推荐    │    │  扣减    │    │  处理    │             │
│   └──────────┘    └──────────┘    └──────────┘    └──────────┘             │
│        │               │               │               │               │
│        ▼               ▼               ▼               ▼               │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │  个人    │    │  评价    │    │  物流    │    │  售后    │             │
│   │  中心    │    │  系统    │    │  跟踪    │    │  服务    │             │
│   └──────────┘    └──────────┘    └──────────┘    └──────────┘             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

1.1.2 技术选型目标

维度
目标
实现策略
性能
首屏加载 < 1.5s
SSR + 边缘缓存 + 资源优化
并发
支持 10K QPS
微服务 + 负载均衡 + 缓存
可用性
99.9% SLA
多活部署 + 熔断降级
扩展性
水平扩展
云原生 + 容器化 + 自动伸缩
开发效率
快速迭代
全栈框架 + 自动化工具链

1.2 系统架构总览

┌─────────────────────────────────────────────────────────────────────────────┐
│                           迷你京东全栈架构图                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                          CDN & Edge Network                        │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │  静态资源   │  │  API网关   │  │  边缘计算   │  │  DDoS防护  │  │   │
│  │  │  (Cloudflare)│  │  (Kong)    │  │  (Vercel)  │  │  (WAF)     │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                          Load Balancer                              │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │  Nginx     │  │  HAProxy   │  │  Traefik   │  │  ALB       │  │   │
│  │  │  (L7)      │  │  (L4)      │  │  (K8s)     │  │  (AWS)     │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         Microservices Layer                          │   │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐         │   │
│  │  │用户服务 │ │商品服务 │ │订单服务 │ │支付服务 │ │库存服务 │         │   │
│  │  │(Node)  │ │(Go)    │ │(Java)  │ │(Python)│ │(Rust)  │         │   │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘         │   │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐         │   │
│  │  │营销服务 │ │搜索服务 │ │推荐服务 │ │物流服务 │ │评价服务 │         │   │
│  │  │(Node)  │ │(Elastic)│ │(Python)│ │(Go)    │ │(Node)  │         │   │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                          Data Layer                                   │   │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐         │   │
│  │  │Postgres │ │  Redis  │ │ MongoDB │ │  ES     │ │  MinIO  │         │   │
│  │  │(主从)   │ │(集群)   │ │(文档)   │ │(搜索)   │ │(对象)   │         │   │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

二、技术栈详解

2.1 前端技术栈

2.1.1 核心技术选型

// tech-stack.ts - 技术栈配置
interface TechStack {
  framework: {
    name: 'Next.js 14';
    reason: 'SSR/SSG支持、App Router、TypeScript集成、生态完善';
    version: '^14.0.0';
  };
  language: {
    name: 'TypeScript';
    reason: '类型安全、IDE支持、重构友好、减少运行时错误';
    version: '^5.0.0';
  };
  stateManagement: {
    name: 'Zustand + React Query';
    reason: '轻量级状态管理、服务端状态同步、缓存优化';
    packages: ['zustand', '@tanstack/react-query'];
  };
  styling: {
    name: 'Tailwind CSS + Stitches';
    reason: '原子化CSS、设计系统、性能优化、主题定制';
    packages: ['tailwindcss', '@stitches/react'];
  };
  ui: {
    name: 'Radix UI + Ant Design';
    reason: '无障碍组件、企业级UI、可访问性、国际化';
    packages: ['@radix-ui/*', 'antd', 'lucide-react'];
  };
  testing: {
    name: 'Vitest + Playwright';
    reason: '快速测试、E2E覆盖、类型安全、CI集成';
    packages: ['vitest', 'playwright', '@testing-library/react'];
  };
  build: {
    name: 'Turbopack (实验性)';
    reason: '极速构建、Rust驱动、开发体验提升';
    version: '^1.0.0';
  };
}

// next.config.js - Next.js配置
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
    appDocumentPreloading: true,
    optimisticClientCache: true,
    scrollRestoration: true,
  },
  images: {
    domains: ['cdn.minijd.com', 'images.unsplash.com'],
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [375, 640, 750, 828, 1080, 1200, 1920, 2048],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
  compiler: {
    removeConsole: process.env.NODE_ENV === 'production',
  },
  swcMinify: true,
  compress: true,
  poweredByHeader: false,
  generateEtags: true,
  httpAgentOptions: {
    keepAlive: true,
  },
  env: {
    API_BASE_URL: process.env.API_BASE_URL || 'http://localhost:3001',
    CDN_BASE_URL: process.env.CDN_BASE_URL || 'https://cdn.minijd.com',
  },
};

module.exports = nextConfig;

2.1.2 前端项目结构

frontend/
├── app/                          # App Router 路由
│   ├── (auth)/                   # 认证组
│   │   ├── login/page.tsx
│   │   └── register/page.tsx
│   ├── (main)/                   # 主站组
│   │   ├── layout.tsx
│   │   ├── page.tsx              # 首页
│   │   ├── product/
│   │   │   └── [id]/
│   │   │       └── page.tsx      # 商品详情
│   │   ├── search/
│   │   │   └── page.tsx          # 搜索结果
│   │   ├── cart/
│   │   │   └── page.tsx          # 购物车
│   │   ├── order/
│   │   │   ├── checkout/
│   │   │   │   └── page.tsx      # 结算页
│   │   │   └── [id]/
│   │   │       └── page.tsx      # 订单详情
│   │   └── user/
│   │       └── profile/
│   │           └── page.tsx      # 个人中心
│   ├── api/                      # API路由
│   │   ├── auth/
│   │   │   └── route.ts
│   │   └── upload/
│   │       └── route.ts
│   ├── globals.css
│   ├── layout.tsx
│   └── not-found.tsx
├── components/                   # 组件库
│   ├── ui/                       # 基础UI组件
│   │   ├── Button/
│   │   ├── Input/
│   │   ├── Modal/
│   │   └── ...
│   ├── layout/                   # 布局组件
│   │   ├── Header/
│   │   ├── Footer/
│   │   ├── Sidebar/
│   │   └── ...
│   ├── product/                  # 商品相关
│   │   ├── ProductCard/
│   │   ├── ProductList/
│   │   ├── ProductGallery/
│   │   └── ...
│   ├── cart/                     # 购物车
│   │   ├── CartItem/
│   │   ├── CartSummary/
│   │   └── ...
│   └── common/                   # 通用组件
│       ├── Loading/
│       ├── ErrorBoundary/
│       └── ...
├── lib/                          # 工具库
│   ├── api/                      # API客户端
│   │   ├── client.ts
│   │   ├── endpoints.ts
│   │   └── hooks.ts
│   ├── store/                    # 状态管理
│   │   ├── userStore.ts
│   │   ├── cartStore.ts
│   │   └── productStore.ts
│   ├── utils/                    # 工具函数
│   │   ├── format.ts
│   │   ├── validation.ts
│   │   └── helpers.ts
│   └── constants/                # 常量
│       ├── routes.ts
│       └── enums.ts
├── hooks/                        # 自定义Hooks
│   ├── useAuth.ts
│   ├── useCart.ts
│   ├── useProduct.ts
│   └── useInfiniteScroll.ts
├── providers/                    # 上下文提供者
│   ├── AuthProvider.tsx
│   ├── CartProvider.tsx
│   └── ThemeProvider.tsx
├── styles/                       # 样式
│   ├── globals.css
│   ├── variables.css
│   └── components/
├── types/                        # 类型定义
│   ├── api.d.ts
│   ├── product.d.ts
│   ├── user.d.ts
│   └── order.d.ts
├── tests/                        # 测试
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── public/                       # 静态资源
│   ├── images/
│   ├── icons/
│   └── fonts/
├── next.config.js
├── tailwind.config.js
├── tsconfig.json
└── package.json

2.2 后端技术栈

2.2.1 微服务架构设计

# docker-compose.yml - 服务编排
version: '3.8'

services:
  # API Gateway
  api-gateway:
    build: ./services/api-gateway
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - SERVICES_USER=http://user-service:3001
      - SERVICES_PRODUCT=http://product-service:3002
      - SERVICES_ORDER=http://order-service:3003
      - SERVICES_PAYMENT=http://payment-service:3004
      - SERVICES_INVENTORY=http://inventory-service:3005
    depends_on:
      - user-service
      - product-service
      - order-service
    networks:
      - minijd-network

  # User Service
  user-service:
    build: ./services/user-service
    ports:
      - "3001:3001"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/minijd_user
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - postgres
      - redis
    networks:
      - minijd-network
    deploy:
      replicas: 2

  # Product Service
  product-service:
    build: ./services/product-service
    ports:
      - "3002:3002"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/minijd_product
      - ELASTICSEARCH_URL=http://elasticsearch:9200
      - MINIO_ENDPOINT=minio:9000
    depends_on:
      - postgres
      - elasticsearch
      - minio
    networks:
      - minijd-network
    deploy:
      replicas: 3

  # Order Service
  order-service:
    build: ./services/order-service
    ports:
      - "3003:3003"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/minijd_order
      - RABBITMQ_URL=amqp://rabbitmq:5672
    depends_on:
      - postgres
      - rabbitmq
    networks:
      - minijd-network
    deploy:
      replicas: 2

  # Payment Service
  payment-service:
    build: ./services/payment-service
    ports:
      - "3004:3004"
    environment:
      - NODE_ENV=production
      - STRIPE_API_KEY=${STRIPE_API_KEY}
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/minijd_payment
    depends_on:
      - postgres
    networks:
      - minijd-network
    deploy:
      replicas: 2

  # Inventory Service
  inventory-service:
    build: ./services/inventory-service
    ports:
      - "3005:3005"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/minijd_inventory
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - redis
    networks:
      - minijd-network
    deploy:
      replicas: 2

  # Search Service
  search-service:
    build: ./services/search-service
    ports:
      - "3006:3006"
    environment:
      - ELASTICSEARCH_URL=http://elasticsearch:9200
    depends_on:
      - elasticsearch
    networks:
      - minijd-network
    deploy:
      replicas: 2

  # Message Queue
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "5672:5672"
      - "15672:15672"
    environment:
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS=password
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    networks:
      - minijd-network

  # Database
  postgres:
    image: postgres:15-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=minijd
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    networks:
      - minijd-network

  # Cache
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    networks:
      - minijd-network

  # Search Engine
  elasticsearch:
    image: elasticsearch:8.10.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data
    networks:
      - minijd-network

  # Object Storage
  minio:
    image: minio/minio
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      - MINIO_ROOT_USER=minijd
      - MINIO_ROOT_PASSWORD=minijd123
    command: server /data --console-address ":9001"
    volumes:
      - minio-data:/data
    networks:
      - minijd-network

  # Monitoring
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    networks:
      - minijd-network

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
    depends_on:
      - prometheus
    networks:
      - minijd-network

networks:
  minijd-network:
    driver: bridge

volumes:
  postgres-data:
  redis-data:
  es-data:
  minio-data:
  rabbitmq-data:
  prometheus-data:
  grafana-data:

2.2.2 各服务技术实现

// services/user-service/src/index.ts - 用户服务
import express from 'express';
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { z } from 'zod';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
import cors from 'cors';
import winston from 'winston';

// 配置
const app = express();
const prisma = new PrismaClient();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
const PORT = process.env.PORT || 3001;

// 日志配置
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// 中间件
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 限制每IP 100个请求
  message: '请求过于频繁,请稍后再试'
}));

// 输入验证Schema
const registerSchema = z.object({
  email: z.string().email('邮箱格式不正确'),
  password: z.string().min(8, '密码至少8位')
    .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, '密码需包含大小写字母和数字'),
  username: z.string().min(2, '用户名至少2个字符').max(20, '用户名最多20个字符'),
  phone: z.string().regex(/^1[3-9]\d{9}$/, '手机号格式不正确').optional()
});

const loginSchema = z.object({
  email: z.string().email('邮箱格式不正确'),
  password: z.string()
});

// 用户注册
app.post('/api/auth/register', async (req, res) => {
  try {
    // 验证输入
    const validatedData = registerSchema.parse(req.body);
    
    // 检查用户是否存在
    const existingUser = await prisma.user.findUnique({
      where: { email: validatedData.email }
    });
    
    if (existingUser) {
      return res.status(409).json({ error: '该邮箱已被注册' });
    }
    
    // 加密密码
    const hashedPassword = await bcrypt.hash(validatedData.password, 12);
    
    // 创建用户
    const user = await prisma.user.create({
      data: {
        email: validatedData.email,
        password: hashedPassword,
        username: validatedData.username,
        phone: validatedData.phone,
        role: 'USER',
        status: 'ACTIVE'
      },
      select: {
        id: true,
        email: true,
        username: true,
        createdAt: true
      }
    });
    
    // 生成Token
    const token = jwt.sign(
      { userId: user.id, email: user.email, role: 'USER' },
      JWT_SECRET,
      { expiresIn: '7d' }
    );
    
    logger.info(`新用户注册: ${user.email}`);
    
    res.status(201).json({
      message: '注册成功',
      user,
      token
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ error: error.errors[0].message });
    }
    logger.error('注册失败:', error);
    res.status(500).json({ error: '注册失败,请稍后重试' });
  }
});

// 用户登录
app.post('/api/auth/login', async (req, res) => {
  try {
    // 验证输入
    const validatedData = loginSchema.parse(req.body);
    
    // 查找用户
    const user = await prisma.user.findUnique({
      where: { email: validatedData.email }
    });
    
    if (!user) {
      return res.status(401).json({ error: '邮箱或密码错误' });
    }
    
    // 检查用户状态
    if (user.status !== 'ACTIVE') {
      return res.status(403).json({ error: '账户已被禁用' });
    }
    
    // 验证密码
    const isPasswordValid = await bcrypt.compare(validatedData.password, user.password);
    
    if (!isPasswordValid) {
      return res.status(401).json({ error: '邮箱或密码错误' });
    }
    
    // 生成Token
    const token = jwt.sign(
      { userId: user.id, email: user.email, role: user.role },
      JWT_SECRET,
      { expiresIn: '7d' }
    );
    
    // 更新最后登录时间
    await prisma.user.update({
      where: { id: user.id },
      data: { lastLoginAt: new Date() }
    });
    
    logger.info(`用户登录: ${user.email}`);
    
    res.json({
      message: '登录成功',
      user: {
        id: user.id,
        email: user.email,
        username: user.username,
        avatar: user.avatar
      },
      token
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ error: error.errors[0].message });
    }
    logger.error('登录失败:', error);
    res.status(500).json({ error: '登录失败,请稍后重试' });
  }
});

// 获取用户信息
app.get('/api/user/profile', authenticateToken, async (req, res) => {
  try {
    const user = await prisma.user.findUnique({
      where: { id: req.user.userId },
      select: {
        id: true,
        email: true,
        username: true,
        phone: true,
        avatar: true,
        gender: true,
        birthday: true,
        createdAt: true,
        addresses: true,
        orders: {
          take: 5,
          orderBy: { createdAt: 'desc' },
          select: {
            id: true,
            orderNo: true,
            status: true,
            totalAmount: true,
            createdAt: true
          }
        }
      }
    });
    
    if (!user) {
      return res.status(404).json({ error: '用户不存在' });
    }
    
    res.json(user);
  } catch (error) {
    logger.error('获取用户信息失败:', error);
    res.status(500).json({ error: '获取用户信息失败' });
  }
});

// 更新用户信息
app.put('/api/user/profile', authenticateToken, async (req, res) => {
  try {
    const updateSchema = z.object({
      username: z.string().min(2).max(20).optional(),
      phone: z.string().regex(/^1[3-9]\d{9}$/).optional(),
      avatar: z.string().url().optional(),
      gender: z.enum(['MALE', 'FEMALE', 'OTHER']).optional(),
      birthday: z.string().datetime().optional()
    });
    
    const validatedData = updateSchema.parse(req.body);
    
    const user = await prisma.user.update({
      where: { id: req.user.userId },
      data: validatedData,
      select: {
        id: true,
        email: true,
        username: true,
        phone: true,
        avatar: true,
        gender: true,
        birthday: true,
        updatedAt: true
      }
    });
    
    res.json({ message: '更新成功', user });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ error: error.errors[0].message });
    }
    logger.error('更新用户信息失败:', error);
    res.status(500).json({ error: '更新失败' });
  }
});

// 身份验证中间件
function authenticateToken(req: any, res: any, next: any) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: '请先登录' });
  }
  
  jwt.verify(token, JWT_SECRET, (err: any, user: any) => {
    if (err) {
      return res.status(403).json({ error: '登录已过期,请重新登录' });
    }
    req.user = user;
    next();
  });
}

// 错误处理中间件
app.use((err: any, req: any, res: any, next: any) => {
  logger.error('未处理的错误:', err);
  res.status(500).json({ error: '服务器内部错误' });
});

// 启动服务
app.listen(PORT, () => {
  logger.info(`用户服务运行在端口 ${PORT}`);
});
// services/product-service/main.go - 商品服务 (Go)
package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/elastic/go-elasticsearch/v8"
	"github.com/go-redis/redis/v8"
	"github.com/gorilla/mux"
	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// 全局变量
var (
	db           *pgxpool.Pool
	redisClient  *redis.Client
	esClient     *elasticsearch.Client
	logger       *log.Logger
	ctx          = context.Background()

	// Prometheus metrics
	httpRequestsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "Total number of HTTP requests",
		},
		[]string{"method", "endpoint", "status"},
	)
	httpRequestDuration = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "http_request_duration_seconds",
			Help:    "HTTP request duration in seconds",
			Buckets: []float64{0.1, 0.3, 0.5, 0.7, 1, 2, 5, 10},
		},
		[]string{"method", "endpoint"},
	)
)

func init() {
	prometheus.MustRegister(httpRequestsTotal)
	prometheus.MustRegister(httpRequestDuration)
	logger = log.New(os.Stdout, "[ProductService] ", log.LstdFlags|log.Lshortfile)
}

// 商品模型
type Product struct {
	ID          int64     `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Price       float64   `json:"price"`
	OriginalPrice float64 `json:"original_price,omitempty"`
	Stock       int       `json:"stock"`
	CategoryID  int64     `json:"category_id"`
	Brand       string    `json:"brand,omitempty"`
	Images      []string  `json:"images,omitempty"`
	SellerID    int64     `json:"seller_id"`
	Status      string    `json:"status"` // ACTIVE, INACTIVE, DELETED
	Rating      float64   `json:"rating"`
	ReviewCount int       `json:"review_count"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

// 初始化数据库连接
func initDB() (*pgxpool.Pool, error) {
	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		dbURL = "postgresql://postgres:password@localhost:5432/minijd_product"
	}

	config, err := pgxpool.ParseConfig(dbURL)
	if err != nil {
		return nil, err
	}

	config.MaxConns = 20
	config.MinConns = 5
	config.HealthCheckPeriod = 30 * time.Second

	pool, err := pgxpool.ConnectConfig(ctx, config)
	if err != nil {
		return nil, err
	}

	// 测试连接
	if err := pool.Ping(ctx); err != nil {
		return nil, err
	}

	logger.Println("数据库连接成功")
	return pool, nil
}

// 初始化Redis连接
func initRedis() *redis.Client {
	client := redis.NewClient(&redis.Options{
		Addr:     os.Getenv("REDIS_URL"),
		Password: "",
		DB:       0,
	})

	// 测试连接
	if _, err := client.Ping(ctx).Result(); err != nil {
		logger.Printf("Redis连接失败: %v", err)
	}

	logger.Println("Redis连接成功")
	return client
}

// 初始化Elasticsearch连接
func initElasticsearch() (*elasticsearch.Client, error) {
	cfg := elasticsearch.Config{
		Addresses: []string{
			os.Getenv("ELASTICSEARCH_URL"),
		},
		Username: "",
		Password: "",
	}

	client, err := elasticsearch.NewClient(cfg)
	if err != nil {
		return nil, err
	}

	// 测试连接
	res, err := client.Info()
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	logger.Println("Elasticsearch连接成功")
	return client, nil
}

// 创建商品
func createProduct(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	
	var product Product
	if err := json.NewDecoder(r.Body).Decode(&product); err != nil {
		http.Error(w, "无效的请求体", http.StatusBadRequest)
		return
	}

	// 验证必填字段
	if product.Name == "" || product.Price <= 0 {
		http.Error(w, "商品名称和价格不能为空", http.StatusBadRequest)
		return
	}

	// 插入数据库
	query := `
		INSERT INTO products (name, description, price, original_price, stock, 
			category_id, brand, seller_id, status, rating, review_count, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
		RETURNING id, created_at, updated_at
	`

	err := db.QueryRow(ctx, query,
		product.Name,
		product.Description,
		product.Price,
		product.OriginalPrice,
		product.Stock,
		product.CategoryID,
		product.Brand,
		product.SellerID,
		"ACTIVE",
		0.0,
		0,
		time.Now(),
		time.Now(),
	).Scan(&product.ID, &product.CreatedAt, &product.UpdatedAt)

	if err != nil {
		logger.Printf("创建商品失败: %v", err)
		http.Error(w, "创建商品失败", http.StatusInternalServerError)
		return
	}

	// 同步到Elasticsearch
	go syncProductToES(product)

	// 清除相关缓存
	go clearProductCache(product.CategoryID)

	duration := time.Since(startTime).Seconds()
	httpRequestDuration.WithLabelValues("POST", "/api/products").Observe(duration)
	httpRequestsTotal.WithLabelValues("POST", "/api/products", "201").Inc()

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(product)
}

// 获取商品详情
func getProduct(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	vars := mux.Vars(r)
	productID := vars["id"]

	// 尝试从缓存获取
	cacheKey := "product:" + productID
	cached, err := redisClient.Get(ctx, cacheKey).Result()
	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("X-Cache", "HIT")
		w.Write([]byte(cached))
		
		duration := time.Since(startTime).Seconds()
		httpRequestDuration.WithLabelValues("GET", "/api/products/:id").Observe(duration)
		httpRequestsTotal.WithLabelValues("GET", "/api/products/:id", "200").Inc()
		return
	}

	// 从数据库获取
	var product Product
	query := `
		SELECT id, name, description, price, original_price, stock, 
			category_id, brand, images, seller_id, status, rating, review_count, created_at, updated_at
		FROM products
		WHERE id = $1 AND status = 'ACTIVE'
	`

	err = db.QueryRow(ctx, query, productID).Scan(
		&product.ID,
		&product.Name,
		&product.Description,
		&product.Price,
		&product.OriginalPrice,
		&product.Stock,
		&product.CategoryID,
		&product.Brand,
		&product.Images,
		&product.SellerID,
		&product.Status,
		&product.Rating,
		&product.ReviewCount,
		&product.CreatedAt,
		&product.UpdatedAt,
	)

	if err != nil {
		logger.Printf("获取商品失败: %v", err)
		http.Error(w, "商品不存在", http.StatusNotFound)
		return
	}

	// 设置缓存
	productJSON, _ := json.Marshal(product)
	redisClient.Set(ctx, cacheKey, productJSON, 30*time.Minute)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("X-Cache", "MISS")
	json.NewEncoder(w).Encode(product)

	duration := time.Since(startTime).Seconds()
	httpRequestDuration.WithLabelValues("GET", "/api/products/:id").Observe(duration)
	httpRequestsTotal.WithLabelValues("GET", "/api/products/:id", "200").Inc()
}

// 搜索商品
func searchProducts(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	query := r.URL.Query().Get("q")
	page := 1
	limit := 20

	if p := r.URL.Query().Get("page"); p != "" {
		page = parseInt(p)
	}
	if l := r.URL.Query().Get("limit"); l != "" {
		limit = parseInt(l)
	}

	// 构建Elasticsearch查询
	esQuery := map[string]interface{}{
		"query": map[string]interface{}{
			"multi_match": map[string]interface{}{
				"query":  query,
				"fields": []string{"name^3", "description^2", "brand", "category_name"},
			},
		},
		"from": (page - 1) * limit,
		"size": limit,
		"sort": []map[string]interface{}{
			{"_score": map[string]string{"order": "desc"}},
			{"rating": map[string]string{"order": "desc"}},
		},
		"highlight": map[string]interface{}{
			"fields": map[string]interface{}{
				"name":        map[string]interface{}{"number_of_fragments": 0},
				"description": map[string]interface{}{"fragment_size": 150},
			},
		},
	}

	esQueryJSON, _ := json.Marshal(esQuery)

	res, err := esClient.Search(
		esClient.Search.WithContext(ctx),
		esClient.Search.WithIndex("products"),
		esClient.Search.WithBody(strings.NewReader(string(esQueryJSON))),
		esClient.Search.WithTrackTotalHits(true),
	)

	if err != nil {
		logger.Printf("搜索失败: %v", err)
		http.Error(w, "搜索失败", http.StatusInternalServerError)
		return
	}
	defer res.Body.Close()

	var esResult map[string]interface{}
	json.NewDecoder(res.Body).Decode(&esResult)

	// 处理结果
	hits := esResult["hits"].(map[string]interface{})
	total := int(hits["total"].(map[string]interface{})["value"].(float64))
	items := hits["hits"].([]interface{})

	products := make([]map[string]interface{}, len(items))
	for i, hit := range items {
		source := hit.(map[string]interface{})["_source"]
		highlight := hit.(map[string]interface{})["highlight"]
		products[i] = map[string]interface{}{
			"product":  source,
			"highlight": highlight,
		}
	}

	result := map[string]interface{}{
		"total":   total,
		"page":    page,
		"limit":   limit,
		"pages":   (total + limit - 1) / limit,
		"results": products,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(result)

	duration := time.Since(startTime).Seconds()
	httpRequestDuration.WithLabelValues("GET", "/api/products/search").Observe(duration)
	httpRequestsTotal.WithLabelValues("GET", "/api/products/search", "200").Inc()
}

// 同步商品到Elasticsearch
func syncProductToES(product Product) {
	doc := map[string]interface{}{
		"id":           product.ID,
		"name":         product.Name,
		"description":  product.Description,
		"price":        product.Price,
		"category_id":  product.CategoryID,
		"brand":        product.Brand,
		"seller_id":    product.SellerID,
		"status":       product.Status,
		"rating":       product.Rating,
		"review_count": product.ReviewCount,
		"created_at":   product.CreatedAt,
	}

	docJSON, _ := json.Marshal(doc)

	_, err := esClient.Index(
		"products",
		strings.NewReader(string(docJSON)),
		esClient.Index.WithContext(ctx),
		esClient.Index.WithDocumentID(fmt.Sprintf("%d", product.ID)),
		esClient.Index.WithRefresh("true"),
	)

	if err != nil {
		logger.Printf("同步到ES失败: %v", err)
	}
}

// 清除商品缓存
func clearProductCache(categoryID int64) {
	pattern := "products:category:*"
	iter := redisClient.Scan(ctx, 0, pattern, 100).Iterator()
	for iter.Next(ctx) {
		redisClient.Del(ctx, iter.Val())
	}
}

// 辅助函数
func parseInt(s string) int {
	n, _ := strconv.Atoi(s)
	return n
}

// 健康检查
func healthCheck(w http.ResponseWriter, r *http.Request) {
	response := map[string]interface{}{
		"status":    "healthy",
		"timestamp": time.Now().Unix(),
		"version":   "1.0.0",
		"services": map[string]string{
			"database":  "connected",
			"redis":     "connected",
			"elasticsearch": "connected",
		},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func main() {
	var err error

	// 初始化服务
	db, err = initDB()
	if err != nil {
		log.Fatalf("数据库初始化失败: %v", err)
	}
	defer db.Close()

	redisClient = initRedis()
	esClient, err = initElasticsearch()
	if err != nil {
		log.Printf("Elasticsearch初始化失败: %v", err)
	}

	// 路由
	r := mux.NewRouter()

	// API路由
	api := r.PathPrefix("/api").Subrouter()
	api.HandleFunc("/products", createProduct).Methods("POST")
	api.HandleFunc("/products/{id}", getProduct).Methods("GET")
	api.HandleFunc("/products/search", searchProducts).Methods("GET")
	api.HandleFunc("/products", listProducts).Methods("GET")

	// 健康检查
	r.HandleFunc("/health", healthCheck).Methods("GET")

	// 监控端点
	r.Handle("/metrics", promhttp.Handler())

	// 启动服务
	port := os.Getenv("PORT")
	if port == "" {
		port = "3002"
	}

	logger.Printf("商品服务启动在端口 %s", port)
	log.Fatal(http.ListenAndServe(":"+port, r))
}

2.3 数据库设计

2.3.1 ER图设计

┌─────────────────────────────────────────────────────────────────────────────┐
│                              迷你京东数据库ER图                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌──────────────┐       ┌──────────────┐       ┌──────────────┐           │
│  │    users     │       │   categories │       │    sellers   │           │
│  ├──────────────┤       ├──────────────┤       ├──────────────┤           │
│  │ PK id        │◄──────│ PK id        │◄──────│ PK id        │           │
│  │   username   │       │   name       │       │   shop_name  │           │
│  │   email      │       │   parent_id  │───────│   logo       │           │
│  │   password   │       │   level      │       │   description│           │
│  │   phone      │       │   sort_order │       │   status     │           │
│  │   avatar     │       │   icon       │       │   rating     │           │
│  │   role       │       └──────────────┘       └──────┬───────┘           │
│  │   status     │              │                        │                 │
│  └──────┬───────┘              │                        │                 │
│         │                     │                        │                 │
│         │ 1:N                 │ 1:N                    │ 1:N              │
│         ▼                     ▼                        ▼                 │
│  ┌──────────────┐       ┌──────────────┐       ┌──────────────┐           │
│  │  addresses   │       │   products   │       │  seller_     │           │
│  ├──────────────┤       ├──────────────┤       │  verifications│           │
│  │ PK id        │       │ PK id        │       ├──────────────┤           │
│  │ FK user_id   │       │ FK category_id│      │ PK id        │           │
│  │   receiver   │       │ FK seller_id │      │ FK seller_id │           │
│  │   phone      │       │   name       │      │   type       │           │
│  │   province   │       │   description│      │   status     │           │
│  │   city       │       │   price      │      │   verified_at│           │
│  │   district   │       │   original_price│     └──────────────┘           │
│  │   address    │       │   stock      │                                    │
│  │   is_default │       │   images     │                                    │
│  └──────────────┘       │   brand      │                                    │
│                         │   rating     │                                    │
│                         │   status     │                                    │
│                         └──────┬───────┘                                    │
│                                │                                            │
│                                │ 1:N                                        │
│                                ▼                                            │
│                         ┌──────────────┐                                   │
│                         │ product_skus │                                   │
│                         ├──────────────┤                                   │
│                         │ PK id        │                                   │
│                         │ FK product_id│                                   │
│                         │   sku_code   │                                   │
│                         │   specs      │                                   │
│                         │   price      │                                   │
│                         │   stock      │                                   │
│                         │   image      │                                   │
│                         └──────┬───────┘                                   │
│                                │                                            │
│                                │ N:M                                        │
│                                ▼                                            │
│                         ┌──────────────┐                                   │
│                         │    carts     │                                   │
│                         ├──────────────┤                                   │
│                         │ PK id        │                                   │
│                         │ FK user_id   │                                   │
│                         │ FK sku_id    │                                   │
│                         │   quantity   │                                   │
│                         │   selected   │                                   │
│                         │   added_at   │                                   │
│                         └──────────────┘                                   │
│                                                                             │
│  ┌──────────────┐       ┌──────────────┐       ┌──────────────┐           │
│  │    orders    │       │ order_items  │       │  payments    │           │
│  ├──────────────┤       ├──────────────┤       ├──────────────┤           │
│  │ PK id        │──────►│ PK id        │       │ PK id        │           │
│  │ FK user_id   │       │ FK order_id  │◄──────│ FK order_id  │           │
│  │   order_no   │       │ FK sku_id    │       │   amount     │           │
│  │   status     │       │   quantity   │       │   method     │           │
│  │   total_amount│      │   price      │       │   transaction│           │
│  │   pay_amount │       │   discount   │       │   status     │           │
│  │   freight    │       │   subtotal   │       │   paid_at    │           │
│  │   address    │       └──────────────┘       └──────────────┘           │
│  │   created_at │                                                           │
│  └──────┬───────┘                                                           │
│         │                                                                    │
│         │ 1:N                                                                │
│         ▼                                                                    │
│  ┌──────────────┐       ┌──────────────┐       ┌──────────────┐           │
│  │  order_logs  │       │  shipments   │       │  reviews     │           │
│  ├──────────────┤       ├──────────────┤       ├──────────────┤           │
│  │ PK id        │       │ PK id        │       │ PK id        │           │
│  │ FK order_id  │       │ FK order_id  │       │ FK user_id   │           │
│  │   action     │       │   tracking_no│       │ FK product_id│           │
│  │   operator   │       │   carrier    │       │   rating     │           │
│  │   remark     │       │   status     │       │   content    │           │
│  │   created_at │       │   shipped_at │       │   images     │           │
│  └──────────────┘       │   delivered_at│       │   created_at │           │
│                         └──────────────┘       └──────────────┘           │
│                                                                             │
│  ┌──────────────┐       ┌──────────────┐       ┌──────────────┐           │
│  │ inventories  │       │  coupons     │       │  favorites   │           │
│  ├──────────────┤       ├──────────────┤       ├──────────────┤           │
│  │ PK id        │       │ PK id        │       │ PK id        │           │
│  │ FK sku_id    │       │   code       │       │ FK user_id   │           │
│  │   warehouse  │       │   name       │       │ FK product_id│           │
│  │   quantity   │       │   type       │       │   created_at │           │
│  │   locked     │       │   value      │       └──────────────┘           │
│  │   updated_at │       │   min_amount │                                    │
│  └──────────────┘       │   start_time │                                    │
│                         │   end_time   │                                    │
│                         │   total_limit │                                    │
│                         │   per_limit  │                                    │
│                         │   used_count │                                    │
│                         └──────────────┘                                    │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

2.3.2 核心表结构SQL

-- 用户表
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    phone VARCHAR(20) UNIQUE,
    avatar TEXT,
    gender VARCHAR(10) CHECK (gender IN ('MALE', 'FEMALE', 'OTHER')),
    birthday DATE,
    role VARCHAR(20) NOT NULL DEFAULT 'USER' CHECK (role IN ('USER', 'SELLER', 'ADMIN')),
    status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'INACTIVE', 'BANNED')),
    last_login_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 用户地址表
CREATE TABLE addresses (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    receiver VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL,
    province VARCHAR(50) NOT NULL,
    city VARCHAR(50) NOT NULL,
    district VARCHAR(50) NOT NULL,
    address TEXT NOT NULL,
    zip_code VARCHAR(10),
    is_default BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 商品分类表
CREATE TABLE categories (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    parent_id BIGINT REFERENCES categories(id) ON DELETE SET NULL,
    level INT NOT NULL DEFAULT 1,
    path VARCHAR(500),
    sort_order INT NOT NULL DEFAULT 0,
    icon VARCHAR(255),
    image VARCHAR(255),
    status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'INACTIVE')),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 商家表
CREATE TABLE sellers (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    shop_name VARCHAR(100) NOT NULL UNIQUE,
    logo VARCHAR(255),
    description TEXT,
    license_no VARCHAR(50),
    license_image VARCHAR(255),
    contact_person VARCHAR(50),
    contact_phone VARCHAR(20),
    province VARCHAR(50),
    city VARCHAR(50),
    district VARCHAR(50),
    address TEXT,
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'ACTIVE', 'REJECTED', 'SUSPENDED')),
    rating DECIMAL(2,1) NOT NULL DEFAULT 5.0 CHECK (rating >= 0 AND rating <= 5),
    sales_count INT NOT NULL DEFAULT 0,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 商品表
CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    category_id BIGINT NOT NULL REFERENCES categories(id) ON DELETE RESTRICT,
    seller_id BIGINT NOT NULL REFERENCES sellers(id) ON DELETE CASCADE,
    name VARCHAR(200) NOT NULL,
    subtitle VARCHAR(500),
    description TEXT,
    price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
    original_price DECIMAL(10,2) CHECK (original_price >= 0),
    stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
    sales INT NOT NULL DEFAULT 0,
    images JSONB DEFAULT '[]',
    brand VARCHAR(100),
    weight DECIMAL(10,3),
    dimensions VARCHAR(50),
    warranty VARCHAR(100),
    rating DECIMAL(2,1) NOT NULL DEFAULT 5.0 CHECK (rating >= 0 AND rating <= 5),
    review_count INT NOT NULL DEFAULT 0,
    view_count INT NOT NULL DEFAULT 0,
    status VARCHAR(20) NOT NULL DEFAULT 'DRAFT' CHECK (status IN ('DRAFT', 'ACTIVE', 'INACTIVE', 'DELETED')),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 商品SKU表
CREATE TABLE product_skus (
    id BIGSERIAL PRIMARY KEY,
    product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
    sku_code VARCHAR(50) NOT NULL UNIQUE,
    specs JSONB NOT NULL,
    price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
    original_price DECIMAL(10,2) CHECK (original_price >= 0),
    stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
    image VARCHAR(255),
    barcode VARCHAR(50),
    status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'INACTIVE')),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 订单表
CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    order_no VARCHAR(32) NOT NULL UNIQUE,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN (
        'PENDING',      -- 待支付
        'PAID',         -- 已支付
        'SHIPPED',      -- 已发货
        'DELIVERED',    -- 已送达
        'COMPLETED',    -- 已完成
        'CANCELLED',    -- 已取消
        'REFUNDING',    -- 退款中
        'REFUNDED'      -- 已退款
    )),
    total_amount DECIMAL(12,2) NOT NULL CHECK (total_amount >= 0),
    pay_amount DECIMAL(12,2) NOT NULL CHECK (pay_amount >= 0),
    freight DECIMAL(10,2) NOT NULL DEFAULT 0 CHECK (freight >= 0),
    discount DECIMAL(10,2) NOT NULL DEFAULT 0 CHECK (discount >= 0),
    coupon_discount DECIMAL(10,2) NOT NULL DEFAULT 0 CHECK (coupon_discount >= 0),
    points_used INT NOT NULL DEFAULT 0,
    points_amount DECIMAL(10,2) NOT NULL DEFAULT 0,
    receiver_name VARCHAR(50) NOT NULL,
    receiver_phone VARCHAR(20) NOT NULL,
    receiver_address TEXT NOT NULL,
    shipping_method VARCHAR(50),
    shipping_no VARCHAR(50),
    buyer_note VARCHAR(500),
    seller_note VARCHAR(500),
    paid_at TIMESTAMP WITH TIME ZONE,
    shipped_at TIMESTAMP WITH TIME ZONE,
    delivered_at TIMESTAMP WITH TIME ZONE,
    completed_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 订单项表
CREATE TABLE order_items (
    id BIGSERIAL PRIMARY KEY,
    order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
    product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE RESTRICT,
    sku_id BIGINT NOT NULL REFERENCES product_skus(id) ON DELETE RESTRICT,
    product_name VARCHAR(200) NOT NULL,
    sku_specs JSONB,
    sku_image VARCHAR(255),
    price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
    original_price DECIMAL(10,2) CHECK (original_price >= 0),
    quantity INT NOT NULL CHECK (quantity > 0),
    subtotal DECIMAL(12,2) NOT NULL CHECK (subtotal >= 0),
    discount DECIMAL(10,2) NOT NULL DEFAULT 0,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 支付记录表
CREATE TABLE payments (
    id BIGSERIAL PRIMARY KEY,
    order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE RESTRICT,
    payment_no VARCHAR(32) NOT NULL UNIQUE,
    method VARCHAR(20) NOT NULL CHECK (method IN ('ALIPAY', 'WECHAT', 'CARD', 'BALANCE')),
    amount DECIMAL(12,2) NOT NULL CHECK (amount > 0),
    currency VARCHAR(3) NOT NULL DEFAULT 'CNY',
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'SUCCESS', 'FAILED', 'REFUNDED')),
    transaction_id VARCHAR(64),
    gateway_response JSONB,
    paid_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 库存表
CREATE TABLE inventories (
    id BIGSERIAL PRIMARY KEY,
    sku_id BIGINT NOT NULL REFERENCES product_skus(id) ON DELETE CASCADE,
    warehouse_id VARCHAR(50) NOT NULL,
    quantity INT NOT NULL DEFAULT 0 CHECK (quantity >= 0),
    locked_quantity INT NOT NULL DEFAULT 0 CHECK (locked_quantity >= 0),
    available_quantity INT GENERATED ALWAYS AS (quantity - locked_quantity) STORED,
    cost_price DECIMAL(10,2) CHECK (cost_price >= 0),
    last_restock_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    UNIQUE(sku_id, warehouse_id)
);

-- 优惠券表
CREATE TABLE coupons (
    id BIGSERIAL PRIMARY KEY,
    code VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    type VARCHAR(20) NOT NULL CHECK (type IN ('FIXED', 'PERCENTAGE', 'FREE_SHIPPING')),
    value DECIMAL(10,2) NOT NULL CHECK (value > 0),
    min_amount DECIMAL(10,2) NOT NULL DEFAULT 0 CHECK (min_amount >= 0),
    max_discount DECIMAL(10,2) CHECK (max_discount > 0),
    total_limit INT NOT NULL DEFAULT 0,
    per_limit INT NOT NULL DEFAULT 1,
    used_count INT NOT NULL DEFAULT 0,
    start_time TIMESTAMP WITH TIME ZONE NOT NULL,
    end_time TIMESTAMP WITH TIME ZONE NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'INACTIVE', 'EXPIRED')),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 用户优惠券表
CREATE TABLE user_coupons (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    coupon_id BIGINT NOT NULL REFERENCES coupons(id) ON DELETE CASCADE,
    status VARCHAR(20) NOT NULL DEFAULT 'UNUSED' CHECK (status IN ('UNUSED', 'USED', 'EXPIRED')),
    used_at TIMESTAMP WITH TIME ZONE,
    order_id BIGINT REFERENCES orders(id) ON DELETE SET NULL,
    received_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    UNIQUE(user_id, coupon_id)
);

-- 购物车表
CREATE TABLE carts (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    sku_id BIGINT NOT NULL REFERENCES product_skus(id) ON DELETE CASCADE,
    quantity INT NOT NULL CHECK (quantity > 0),
    selected BOOLEAN NOT NULL DEFAULT TRUE,
    added_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    UNIQUE(user_id, sku_id)
);

-- 商品评价表
CREATE TABLE reviews (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
    order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
    sku_id BIGINT REFERENCES product_skus(id) ON DELETE SET NULL,
    rating INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
    title VARCHAR(200),
    content TEXT,
    images JSONB DEFAULT '[]',
    is_anonymous BOOLEAN NOT NULL DEFAULT FALSE,
    reply TEXT,
    reply_at TIMESTAMP WITH TIME ZONE,
    helpful_count INT NOT NULL DEFAULT 0,
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED')),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- 收藏夹表
CREATE TABLE favorites (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    UNIQUE(user_id, product_id)
);

-- 索引优化
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_phone ON users(phone);
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_products_seller ON products(seller_id);
CREATE INDEX idx_products_status ON products(status);
CREATE INDEX idx_products_created ON products(created_at DESC);
CREATE INDEX idx_products_price ON products(price);
CREATE INDEX idx_products_rating ON products(rating);
CREATE INDEX idx_skus_product ON product_skus(product_id);
CREATE INDEX idx_orders_user ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created ON orders(created_at DESC);
CREATE INDEX idx_orders_order_no ON orders(order_no);
CREATE INDEX idx_order_items_order ON order_items(order_id);
CREATE INDEX idx_cart_user ON carts(user_id);
CREATE INDEX idx_cart_selected ON carts(user_id, selected);
CREATE INDEX idx_reviews_product ON reviews(product_id);
CREATE INDEX idx_reviews_user ON reviews(user_id);
CREATE INDEX idx_reviews_status ON reviews(status);

-- 全文搜索索引 (PostgreSQL)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_products_name_fts ON products USING gin(name gin_trgm_ops);
CREATE INDEX idx_products_description_fts ON products USING gin(description gin_trgm_ops);

-- 触发器函数:更新时间戳
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ language 'plpgsql';

-- 为需要的表添加更新时间戳触发器
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_products_updated_at BEFORE UPDATE ON products
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_orders_updated_at BEFORE UPDATE ON orders
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

三、核心功能实现

3.1 用户认证与授权

// services/user-service/src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import { logger } from '../utils/logger';
import { AppError } from '../utils/errors';

const prisma = new PrismaClient();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET || 'refresh-secret-key';

// 扩展Request类型
declare global {
  namespace Express {
    interface Request {
      user?: {
        userId: number;
        email: string;
        role: string;
        sessionId: string;
      };
    }
  }
}

// 生成访问令牌
export const generateAccessToken = (userId: number, email: string, role: string): string => {
  return jwt.sign(
    { userId, email, role, type: 'access' },
    JWT_SECRET,
    { expiresIn: '15m' }
  );
};

// 生成刷新令牌
export const generateRefreshToken = (userId: number, sessionId: string): string => {
  return jwt.sign(
    { userId, sessionId, type: 'refresh' },
    REFRESH_TOKEN_SECRET,
    { expiresIn: '7d' }
  );
};

// 验证访问令牌
export const verifyAccessToken = (token: string): any => {
  try {
    return jwt.verify(token, JWT_SECRET);
  } catch (error) {
    throw new AppError('令牌无效或已过期', 401);
  }
};

// 验证刷新令牌
export const verifyRefreshToken = (token: string): any => {
  try {
    return jwt.verify(token, REFRESH_TOKEN_SECRET);
  } catch (error) {
    throw new AppError('刷新令牌无效或已过期', 401);
  }
};

// 认证中间件
export const authenticate = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new AppError('请先登录', 401);
    }
    
    const token = authHeader.substring(7);
    const decoded = verifyAccessToken(token);
    
    // 检查用户是否仍然存在且状态正常
    const user = await prisma.user.findUnique({
      where: { id: decoded.userId },
      select: { id: true, email: true, role: true, status: true }
    });
    
    if (!user) {
      throw new AppError('用户不存在', 401);
    }
    
    if (user.status !== 'ACTIVE') {
      throw new AppError('账户已被禁用', 403);
    }
    
    req.user = {
      userId: user.id,
      email: user.email,
      role: user.role,
      sessionId: decoded.sessionId || ''
    };
    
    next();
  } catch (error) {
    if (error instanceof AppError) {
      return res.status(error.statusCode).json({ error: error.message });
    }
    logger.error('认证失败:', error);
    res.status(401).json({ error: '认证失败' });
  }
};

// 角色权限中间件
export const authorize = (...roles: string[]) => {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: '请先登录' });
    }
    
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: '没有权限执行此操作' });
    }
    
    next();
  };
};

// 可选的认证中间件(用于可能已登录或未登录的场景)
export const optionalAuth = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return next();
    }
    
    const token = authHeader.substring(7);
    const decoded = verifyAccessToken(token);
    
    const user = await prisma.user.findUnique({
      where: { id: decoded.userId },
      select: { id: true, email: true, role: true, status: true }
    });
    
    if (user && user.status === 'ACTIVE') {
      req.user = {
        userId: user.id,
        email: user.email,
        role: user.role,
        sessionId: decoded.sessionId || ''
      };
    }
    
    next();
  } catch (error) {
    // 静默失败,继续处理请求
    next();
  }
};

// 刷新令牌
export const refreshToken = async (req: Request, res: Response) => {
  try {
    const { refreshToken } = req.body;
    
    if (!refreshToken) {
      return res.status(400).json({ error: '刷新令牌不能为空' });
    }
    
    const decoded = verifyRefreshToken(refreshToken);
    
    // 检查session是否有效
    const session = await prisma.userSession.findFirst({
      where: {
        userId: decoded.userId,
        sessionId: decoded.sessionId,
        status: 'ACTIVE',
        expiresAt: { gt: new Date() }
      }
    });
    
    if (!session) {
      return res.status(401).json({ error: '会话已失效' });
    }
    
    // 获取用户信息
    const user = await prisma.user.findUnique({
      where: { id: decoded.userId },
      select: { id: true, email: true, role: true }
    });
    
    if (!user) {
      return res.status(401).json({ error: '用户不存在' });
    }
    
    // 生成新的访问令牌
    const accessToken = generateAccessToken(user.id, user.email, user.role);
    
    // 生成新的刷新令牌(可选:滚动刷新)
    const newRefreshToken = generateRefreshToken(user.id, session.sessionId);
    
    res.json({
      accessToken,
      refreshToken: newRefreshToken,
      expiresIn: 900 // 15分钟
    });
  } catch (error) {
    if (error instanceof AppError) {
      return res.status(error.statusCode).json({ error: error.message });
    }
    logger.error('刷新令牌失败:', error);
    res.status(401).json({ error: '刷新令牌失败' });
  }
};

// 登出
export const logout = async (req: Request, res: Response) => {
  try {
    const authHeader = req.headers.authorization;
    
    if (authHeader && authHeader.startsWith('Bearer ')) {
      const token = authHeader.substring(7);
      const decoded = verifyAccessToken(token);
      
      // 使当前session失效
      await prisma.userSession.updateMany({
        where: {
          userId: decoded.userId,
          sessionId: decoded.sessionId
        },
        data: { status: 'REVOKED' }
      });
    }
    
    res.json({ message: '登出成功' });
  } catch (error) {
    logger.error('登出失败:', error);
    res.json({ message: '登出成功' }); // 即使失败也返回成功
  }
};

// 退出所有设备
export const logoutAll = async (req: Request, res: Response) => {
  try {
    if (!req.user) {
      return res.status(401).json({ error: '请先登录' });
    }
    
    await prisma.userSession.updateMany({
      where: { userId: req.user.userId },
      data: { status: 'REVOKED' }
    });
    
    res.json({ message: '已退出所有设备' });
  } catch (error) {
    logger.error('退出所有设备失败:', error);
    res.status(500).json({ error: '操作失败' });
  }
};

3.2 商品搜索与推荐

// services/search-service/src/search.ts
import { Client } from '@elastic/elasticsearch';
import Redis from 'ioredis';
import { logger } from './utils/logger';
import { Product, SearchResponse, SearchParams, RecommendationResponse } from './types';

class SearchService {
  private esClient: Client;
  private redisClient: Redis;
  private indexName = 'products';

  constructor() {
    this.esClient = new Client({
      node: process.env.ELASTICSEARCH_URL || 'http://localhost:9200',
      maxRetries: 5,
      requestTimeout: 30000
    });
    
    this.redisClient = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
  }

  // 搜索商品
  async search(params: SearchParams): Promise<SearchResponse> {
    const cacheKey = this.buildCacheKey(params);
    
    // 尝试从缓存获取
    try {
      const cached = await this.redisClient.get(cacheKey);
      if (cached) {
        logger.debug(`缓存命中: ${cacheKey}`);
        return JSON.parse(cached);
      }
    } catch (error) {
      logger.warn('Redis缓存读取失败:', error);
    }

    const startTime = Date.now();
    
    try {
      const query = this.buildElasticsearchQuery(params);
      
      const response = await this.esClient.search({
        index: this.indexName,
        body: query,
        track_total_hits: true
      });

      const result = this.transformSearchResponse(response, params, Date.now() - startTime);
      
      // 缓存结果
      try {
        await this.redisClient.setex(
          cacheKey,
          300, // 5分钟缓存
          JSON.stringify(result)
        );
      } catch (error) {
        logger.warn('Redis缓存写入失败:', error);
      }

      return result;
    } catch (error) {
      logger.error('搜索失败:', error);
      throw new Error('搜索服务暂时不可用');
    }
  }

  // 构建Elasticsearch查询
  private buildElasticsearchQuery(params: SearchParams) {
    const {
      keyword,
      categoryId,
      brand,
      minPrice,
      maxPrice,
      sortBy = 'relevance',
      sortOrder = 'desc',
      page = 1,
      limit = 20,
      filters = {},
      attributes = {}
    } = params;

    const query: any = {
      bool: {
        must: [],
        filter: [],
        should: [],
        must_not: []
      }
    };

    // 关键词搜索
    if (keyword) {
      query.bool.must.push({
        multi_match: {
          query: keyword,
          fields: [
            'name^4',
            'name.pinyin^3',
            'description^2',
            'brand^2',
            'category_names^2',
            'sku_names'
          ],
          type: 'best_fields',
          fuzziness: 'AUTO',
          operator: 'or'
        }
      });
    } else {
      // 无关键词时返回所有商品
      query.bool.must.push({
        match_all: {}
      });
    }

    // 分类过滤
    if (categoryId) {
      query.bool.filter.push({
        term: { category_id: categoryId }
      });
    }

    // 品牌过滤
    if (brand) {
      if (Array.isArray(brand)) {
        query.bool.filter.push({
          terms: { brand: brand }
        });
      } else {
        query.bool.filter.push({
          term: { brand: brand }
        });
      }
    }

    // 价格范围过滤
    if (minPrice !== undefined || maxPrice !== undefined) {
      const priceFilter: any = { range: { price: {} } };
      if (minPrice !== undefined) {
        priceFilter.range.price.gte = minPrice;
      }
      if (maxPrice !== undefined) {
        priceFilter.range.price.lte = maxPrice;
      }
      query.bool.filter.push(priceFilter);
    }

    // 状态过滤
    query.bool.filter.push({
      term: { status: 'ACTIVE' }
    });

    // 其他过滤器
    Object.entries(filters).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        if (Array.isArray(value)) {
          query.bool.filter.push({
            terms: { [key]: value }
          });
        } else {
          query.bool.filter.push({
            term: { [key]: value }
          });
        }
      }
    });

    // 属性过滤
    Object.entries(attributes).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        query.bool.filter.push({
          term: { [`attributes.${key}`]: value }
        });
      }
    });

    // 构建排序
    const sort: any[] = this.buildSort(sortBy, sortOrder);

    return {
      from: (page - 1) * limit,
      size: limit,
      query,
      sort,
      aggs: this.buildAggregations(),
      highlight: {
        fields: {
          name: { number_of_fragments: 0 },
          description: { fragment_size: 150, number_of_fragments: 3 }
        }
      }
    };
  }

  // 构建排序
  private buildSort(sortBy: string, sortOrder: string) {
    const sort: any[] = [];

    switch (sortBy) {
      case 'price':
        sort.push({ price: { order: sortOrder } });
        break;
      case 'sales':
        sort.push({ sales: { order: sortOrder } });
        break;
      case 'rating':
        sort.push({ rating: { order: sortOrder } });
        break;
      case 'newest':
        sort.push({ created_at: { order: 'desc' } });
        break;
      case 'relevance':
      default:
        sort.push({ _score: { order: 'desc' } });
        sort.push({ sales: { order: 'desc' } });
        sort.push({ rating: { order: 'desc' } });
        break;
    }

    // 确保结果稳定
    sort.push({ id: { order: 'asc' } });

    return sort;
  }

  // 构建聚合
  private buildAggregations() {
    return {
      brands: {
        terms: { field: 'brand', size: 20 }
      },
      categories: {
        terms: { field: 'category_id', size: 20 }
      },
      price_ranges: {
        range: {
          field: 'price',
          ranges: [
            { to: 50 },
            { from: 50, to: 100 },
            { from: 100, to: 200 },
            { from: 200, to: 500 },
            { from: 500, to: 1000 },
            { from: 1000 }
          ]
        }
      },
      attributes: {
        nested: {
          path: 'attributes'
        },
        aggregations: {
          names: {
            terms: { field: 'attributes.name', size: 20 },
            aggregations: {
              values: {
                terms: { field: 'attributes.value', size: 10 }
              }
            }
          }
        }
      }
    };
  }

  // 转换搜索响应
  private transformSearchResponse(
    esResponse: any,
    params: SearchParams,
    took: number
  ): SearchResponse {
    const hits = esResponse.hits.hits;
    const total = esResponse.hits.total.value;

    const products: Product[] = hits.map((hit: any) => ({
      id: hit._source.id,
      name: hit.highlight?.name?.[0] || hit._source.name,
      description: hit._source.description,
      price: hit._source.price,
      originalPrice: hit._source.original_price,
      brand: hit._source.brand,
      categoryId: hit._source.category_id,
      categoryNames: hit._source.category_names,
      images: hit._source.images,
      rating: hit._source.rating,
      reviewCount: hit._source.review_count,
      sales: hit._source.sales,
      stock: hit._source.stock,
      attributes: hit._source.attributes,
      highlights: hit.highlight,
      score: hit._score
    }));

    const aggregations = esResponse.aggregations || {};

    return {
      products,
      total,
      page: params.page || 1,
      limit: params.limit || 20,
      pages: Math.ceil(total / (params.limit || 20)),
      took,
      aggregations: this.transformAggregations(aggregations)
    };
  }

  // 转换聚合结果
  private transformAggregations(aggregations: any) {
    return {
      brands: aggregations.brands?.buckets.map((bucket: any) => ({
        name: bucket.key,
        count: bucket.doc_count
      })) || [],
      categories: aggregations.categories?.buckets.map((bucket: any) => ({
        id: bucket.key,
        count: bucket.doc_count
      })) || [],
      priceRanges: aggregations.price_ranges?.buckets.map((bucket: any) => ({
        range: bucket.key,
        count: bucket.doc_count
      })) || [],
      attributes: this.transformAttributeAggregations(aggregations.attributes)
    };
  }

  private transformAttributeAggregations(attributesAgg: any) {
    if (!attributesAgg?.names?.buckets) return [];

    return attributesAgg.names.buckets.map((nameBucket: any) => ({
      name: nameBucket.key,
      values: nameBucket.values?.buckets.map((valueBucket: any) => ({
        value: valueBucket.key,
        count: valueBucket.doc_count
      })) || []
    }));
  }

  // 构建缓存键
  private buildCacheKey(params: SearchParams): string {
    const keyParts = [
      'search',
      params.keyword || '',
      params.categoryId || '',
      Array.isArray(params.brand) ? params.brand.sort().join(',') : params.brand || '',
      params.minPrice ?? '',
      params.maxPrice ?? '',
      params.sortBy || 'relevance',
      params.sortOrder || 'desc',
      params.page || 1,
      params.limit || 20,
      JSON.stringify(params.filters || {}),
      JSON.stringify(params.attributes || {})
    ];

    return keyParts.join(':');
  }

  // 获取搜索建议
  async getSuggestions(keyword: string, limit: number = 10): Promise<string[]> {
    if (!keyword || keyword.length < 2) {
      return [];
    }

    try {
      const response = await this.esClient.search({
        index: this.indexName,
        body: {
          suggest: {
            product_suggest: {
              prefix: keyword,
              completion: {
                field: 'suggest',
                size: limit,
                fuzzy: {
                  fuzziness: 1
                }
              }
            }
          }
        }
      });

      const suggestions = response.suggest?.product_suggest?.[0]?.options || [];
      return suggestions.map((option: any) => option.text);
    } catch (error) {
      logger.error('获取搜索建议失败:', error);
      return [];
    }
  }

  // 获取热门搜索词
  async getHotKeywords(limit: number = 20): Promise<{ keyword: string; count: number }[]> {
    const cacheKey = `hot_keywords:${limit}`;
    
    try {
      const cached = await this.redisClient.get(cacheKey);
      if (cached) {
        return JSON.parse(cached);
      }
    } catch (error) {
      logger.warn('Redis读取失败:', error);
    }

    try {
      const response = await this.esClient.search({
        index: this.indexName,
        body: {
          size: 0,
          aggs: {
            hot_keywords: {
              terms: {
                field: 'search_keywords.keyword',
                size: limit,
                order: { _count: 'desc' }
              }
            }
          }
        }
      });

      const buckets = response.aggregations?.hot_keywords?.buckets || [];
      const result = buckets.map((bucket: any) => ({
        keyword: bucket.key,
        count: bucket.doc_count
      }));

      // 缓存1小时
      await this.redisClient.setex(cacheKey, 3600, JSON.stringify(result));
      
      return result;
    } catch (error) {
      logger.error('获取热门搜索词失败:', error);
      return [];
    }
  }

  // 记录用户搜索行为
  async recordSearch(userId: number, keyword: string, resultCount: number): Promise<void> {
    try {
      // 记录到搜索日志
      await this.redisClient.lpush('search_logs', JSON.stringify({
        userId,
        keyword,
        resultCount,
        timestamp: new Date().toISOString()
      }));

      // 保持最近10000条记录
      await this.redisClient.ltrim('search_logs', 0, 9999);

      // 更新搜索词计数
      const key = `search_count:${keyword.toLowerCase()}`;
      await this.redisClient.zincrby('search_rankings', 1, keyword.toLowerCase());
    } catch (error) {
      logger.warn('记录搜索行为失败:', error);
    }
  }

  // 获取个性化推荐
  async getRecommendations(
    userId: number,
    limit: number = 20
  ): Promise<RecommendationResponse> {
    const cacheKey = `recommendations:${userId}:${limit}`;
    
    try {
      const cached = await this.redisClient.get(cacheKey);
      if (cached) {
        return JSON.parse(cached);
      }
    } catch (error) {
      logger.warn('Redis读取失败:', error);
    }

    try {
      // 1. 获取用户行为数据
      const userBehavior = await this.getUserBehavior(userId);
      
      // 2. 基于用户行为的推荐
      const behaviorBased = await this.getBehaviorBasedRecommendations(userBehavior, limit);
      
      // 3. 基于相似用户的推荐
      const collaborative = await this.getCollaborativeFilteringRecommendations(userId, limit);
      
      // 4. 基于热门商品的推荐
      const popular = await this.getPopularRecommendations(limit);
      
      // 5. 合并和去重
      const recommendations = this.mergeRecommendations(
        behaviorBased,
        collaborative,
        popular,
        limit
      );

      const result: RecommendationResponse = {
        products: recommendations,
        algorithm: 'hybrid',
        generatedAt: new Date().toISOString()
      };

      // 缓存30分钟
      await this.redisClient.setex(cacheKey, 1800, JSON.stringify(result));
      
      return result;
    } catch (error) {
      logger.error('获取推荐失败:', error);
      return {
        products: [],
        algorithm: 'fallback',
        generatedAt: new Date().toISOString()
      };
    }
  }

  // 获取用户行为
  private async getUserBehavior(userId: number) {
    const [viewed, purchased, favorited, searched] = await Promise.all([
      this.redisClient.zrevrange(`user:${userId}:viewed`, 0, 99),
      this.redisClient.zrevrange(`user:${userId}:purchased`, 0, 99),
      this.redisClient.zrevrange(`user:${userId}:favorited`, 0, 99),
      this.redisClient.lrange(`user:${userId}:searches`, 0, 19)
    ]);

    return {
      viewed: viewed.map(Number),
      purchased: purchased.map(Number),
      favorited: favorited.map(Number),
      searched: searched
    };
  }

  // 基于行为的推荐
  private async getBehaviorBasedRecommendations(
    behavior: any,
    limit: number
  ): Promise<Product[]> {
    const productIds = [
      ...behavior.viewed,
      ...behavior.purchased,
      ...behavior.favorited
    ];

    if (productIds.length === 0) {
      return [];
    }

    // 获取这些商品的分类和品牌
    const categories = await this.redisClient.smembers(`products:categories:${productIds.slice(0, 10).join(',')}`);
    const brands = await this.redisClient.smembers(`products:brands:${productIds.slice(0, 10).join(',')}`);

    // 搜索相似商品
    const searchParams: SearchParams = {
      categoryId: categories.length > 0 ? parseInt(categories[0]) : undefined,
      brand: brands.length > 0 ? brands[0] : undefined,
      page: 1,
      limit,
      sortBy: 'relevance'
    };

    const result = await this.search(searchParams);
    
    // 排除已交互过的商品
    return result.products.filter(
      p => !productIds.includes(p.id)
    ).slice(0, limit);
  }

  // 基于协同过滤的推荐
  private async getCollaborativeFilteringRecommendations(
    userId: number,
    limit: number
  ): Promise<Product[]> {
    // 找到相似用户
    const similarUsers = await this.findSimilarUsers(userId, 10);
    
    if (similarUsers.length === 0) {
      return [];
    }

    // 获取相似用户购买的商品
    const productScores: Map<number, number> = new Map();
    
    for (const similarUserId of similarUsers) {
      const products = await this.redisClient.zrevrange(
        `user:${similarUserId}:purchased`,
        0,
        49
      );
      
      products.forEach((productId, index) => {
        const currentScore = productScores.get(Number(productId)) || 0;
        // 越靠前的商品权重越高
        productScores.set(Number(productId), currentScore + (50 - index));
      });
    }

    // 排序并获取商品详情
    const sortedProductIds = Array.from(productScores.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, limit * 2)
      .map(([id]) => id);

    const products = await this.getProductsByIds(sortedProductIds);
    
    // 排除用户已交互的商品
    const userBehavior = await this.getUserBehavior(userId);
    const interactedIds = new Set([
      ...userBehavior.viewed,
      ...userBehavior.purchased,
      ...userBehavior.favorited
    ]);

    return products.filter(p => !interactedIds.has(p.id)).slice(0, limit);
  }

  // 找到相似用户
  private async findSimilarUsers(userId: number, limit: number): Promise<number[]> {
    // 简化的协同过滤:基于共同购买的商品
    const userPurchases = await this.redisClient.zrevrange(
      `user:${userId}:purchased`,
      0,
      99
    );

    if (userPurchases.length === 0) {
      return [];
    }

    const purchaseSets = await Promise.all(
      userPurchases.slice(0, 20).map(async (productId) => {
        const buyers = await this.redisClient.zrevrange(
          `product:${productId}:buyers`,
          0,
          99
        );
        return new Set(buyers.map(Number));
      })
    );

    // 计算用户相似度
    const similarities: Map<number, number> = new Map();
    
    purchaseSets.forEach(set => {
      set.forEach(otherUserId => {
        if (otherUserId !== userId) {
          const currentScore = similarities.get(otherUserId) || 0;
          similarities.set(otherUserId, currentScore + 1);
        }
      });
    });

    return Array.from(similarities.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, limit)
      .map(([id]) => id);
  }

  // 获取热门推荐
  private async getPopularRecommendations(limit: number): Promise<Product[]> {
    const cacheKey = 'popular_products';
    
    try {
      const cached = await this.redisClient.get(cacheKey);
      if (cached) {
        return JSON.parse(cached);
      }
    } catch (error) {
      logger.warn('Redis读取失败:', error);
    }

    const result = await this.search({
      page: 1,
      limit,
      sortBy: 'sales',
      sortOrder: 'desc'
    });

    // 缓存1小时
    await this.redisClient.setex(cacheKey, 3600, JSON.stringify(result.products));
    
    return result.products;
  }

  // 合并推荐结果
  private mergeRecommendations(
    behaviorBased: Product[],
    collaborative: Product[],
    popular: Product[],
    limit: number
  ): Product[] {
    const seen = new Set<number>();
    const merged: Product[] = [];

    const addUnique = (products: Product[]) => {
      for (const product of products) {
        if (!seen.has(product.id) && merged.length < limit) {
          seen.add(product.id);
          merged.push(product);
        }
      }
    };

    // 加权合并:行为推荐 > 协同过滤 > 热门
    addUnique(behaviorBased);
    addUnique(collaborative);
    addUnique(popular);

    return merged;
  }

  // 根据ID获取商品
  private async getProductsByIds(ids: number[]): Promise<Product[]> {
    try {
      const response = await this.esClient.search({
        index: this.indexName,
        body: {
          query: {
            ids: { values: ids }
          },
          size: ids.length
        }
      });

      return response.hits.hits.map((hit: any) => ({
        id: hit._source.id,
        name: hit._source.name,
        description: hit._source.description,
        price: hit._source.price,
        originalPrice: hit._source.original_price,
        brand: hit._source.brand,
        categoryId: hit._source.category_id,
        categoryNames: hit._source.category_names,
        images: hit._source.images,
        rating: hit._source.rating,
        reviewCount: hit._source.review_count,
        sales: hit._source.sales,
        stock: hit._source.stock,
        attributes: hit._source.attributes
      }));
    } catch (error) {
      logger.error('获取商品详情失败:', error);
      return [];
    }
  }

  // 关闭连接
  async close(): Promise<void> {
    await this.esClient.close();
    await this.redisClient.quit();
  }
}

export default SearchService;

3.3 购物车与订单处理

// services/order-service/src/services/CartService.ts
import { Pool } from 'pg';
import Redis from 'ioredis';
import { logger } from '../utils/logger';
import { CartItem, AddToCartDTO, UpdateCartDTO } from '../types';

export class CartService {
  private db: Pool;
  private redis: Redis;

  constructor(db: Pool, redis: Redis) {
    this.db = db;
    this.redis = redis;
  }

  // 获取用户购物车
  async getCart(userId: number): Promise<CartItem[]> {
    const cacheKey = `cart:${userId}`;
    
    try {
      // 尝试从缓存获取
      const cached = await this.redis.get(cacheKey);
      if (cached) {
        logger.debug(`购物车缓存命中: ${userId}`);
        return JSON.parse(cached);
      }
    } catch (error) {
      logger.warn('Redis读取失败:', error);
    }

    // 从数据库获取
    const query = `
      SELECT 
        c.id,
        c.sku_id,
        c.quantity,
        c.selected,
        c.added_at,
        p.id as product_id,
        p.name as product_name,
        p.images as product_images,
        p.price as product_price,
        p.status as product_status,
        sku.specs,
        sku.price as sku_price,
        sku.stock as sku_stock,
        sku.image as sku_image
      FROM carts c
      JOIN product_skus sku ON c.sku_id = sku.id
      JOIN products p ON sku.product_id = p.id
      WHERE c.user_id = $1
      ORDER BY c.added_at DESC
    `;

    const result = await this.db.query(query, [userId]);
    const cartItems: CartItem[] = result.rows.map(row => ({
      id: row.id,
      userId: userId,
      skuId: row.sku_id,
      quantity: row.quantity,
      selected: row.selected,
      addedAt: row.added_at,
      product: {
        id: row.product_id,
        name: row.product_name,
        images: row.product_images,
        price: parseFloat(row.product_price),
        status: row.product_status
      },
      sku: {
        specs: row.specs,
        price: parseFloat(row.sku_price),
        stock: row.sku_stock,
        image: row.sku_image
      }
    }));

    // 缓存购物车(5分钟)
    try {
      await this.redis.setex(cacheKey, 300, JSON.stringify(cartItems));
    } catch (error) {
      logger.warn('Redis写入失败:', error);
    }

    return cartItems;
  }

  // 添加到购物车
  async addToCart(userId: number, dto: AddToCartDTO): Promise<CartItem> {
    const { skuId, quantity = 1 } = dto;

    // 验证SKU存在且有库存
    const skuCheck = await this.db.query(`
      SELECT sku.id, sku.stock, sku.price, p.status as product_status
      FROM product_skus sku
      JOIN products p ON sku.product_id = p.id
      WHERE sku.id = $1 AND sku.status = 'ACTIVE' AND p.status = 'ACTIVE'
    `, [skuId]);

    if (skuCheck.rows.length === 0) {
      throw new Error('商品不存在或已下架');
    }

    const sku = skuCheck.rows[0];
    
    if (sku.stock < quantity) {
      throw new Error('库存不足');
    }

    // 检查购物车是否已有该商品
    const existingItem = await this.db.query(
      'SELECT id, quantity FROM carts WHERE user_id = $1 AND sku_id = $2',
      [userId, skuId]
    );

    let cartItem: CartItem;

    if (existingItem.rows.length > 0) {
      // 更新数量
      const newQuantity = existingItem.rows[0].quantity + quantity;
      
      if (newQuantity > sku.stock) {
        throw new Error('超出库存限制');
      }

      const updateResult = await this.db.query(
        'UPDATE carts SET quantity = $1, updated_at = NOW() WHERE id = $2 RETURNING *',
        [newQuantity, existingItem.rows[0].id]
      );

      cartItem = this.mapCartItem(updateResult.rows[0]);
    } else {
      // 新增购物车项
      const insertResult = await this.db.query(`
        INSERT INTO carts (user_id, sku_id, quantity, selected, added_at, updated_at)
        VALUES ($1, $2, $3, true, NOW(), NOW())
        RETURNING *
      `, [userId, skuId, quantity]);

      cartItem = this.mapCartItem(insertResult.rows[0]);
    }

    // 清除缓存
    await this.clearCartCache(userId);

    // 发布购物车更新事件
    await this.publishCartEvent(userId, 'ITEM_ADDED', cartItem);

    return cartItem;
  }

  // 更新购物车项
  async updateCartItem(userId: number, cartItemId: number, dto: UpdateCartDTO): Promise<CartItem> {
    const { quantity, selected } = dto;

    // 验证购物车项属于当前用户
    const cartCheck = await this.db.query(
      'SELECT id, sku_id, quantity FROM carts WHERE id = $1 AND user_id = $2',
      [cartItemId, userId]
    );

    if (cartCheck.rows.length === 0) {
      throw new Error('购物车项不存在');
    }

    const cartItem = cartCheck.rows[0];

    // 如果更新数量,验证库存
    if (quantity !== undefined) {
      const stockCheck = await this.db.query(
        'SELECT stock FROM product_skus WHERE id = $1',
        [cartItem.sku_id]
      );

      if (stockCheck.rows[0].stock < quantity) {
        throw new Error('库存不足');
      }

      if (quantity <= 0) {
        // 删除购物车项
        await this.db.query('DELETE FROM carts WHERE id = $1', [cartItemId]);
        await this.clearCartCache(userId);
        await this.publishCartEvent(userId, 'ITEM_REMOVED', { id: cartItemId });
        throw new Error('商品已从购物车移除');
      }

      await this.db.query(
        'UPDATE carts SET quantity = $1, updated_at = NOW() WHERE id = $2',
        [quantity, cartItemId]
      );
    }

    // 如果更新选中状态
    if (selected !== undefined) {
      await this.db.query(
        'UPDATE carts SET selected = $1, updated_at = NOW() WHERE id = $2',
        [selected, cartItemId]
      );
    }

    // 清除缓存
    await this.clearCartCache(userId);

    // 获取更新后的购物车项
    const updatedItem = await this.getCartItemById(cartItemId);
    await this.publishCartEvent(userId, 'ITEM_UPDATED', updatedItem);

    return updatedItem;
  }

  // 删除购物车项
  async removeFromCart(userId: number, cartItemId: number): Promise<void> {
    const result = await this.db.query(
      'DELETE FROM carts WHERE id = $1 AND user_id = $2 RETURNING id',
      [cartItemId, userId]
    );

    if (result.rowCount === 0) {
      throw new Error('购物车项不存在');
    }

    await this.clearCartCache(userId);
    await this.publishCartEvent(userId, 'ITEM_REMOVED', { id: cartItemId });
  }

  // 清空购物车
  async clearCart(userId: number): Promise<void> {
    await this.db.query('DELETE FROM carts WHERE user_id = $1', [userId]);
    await this.clearCartCache(userId);
    await this.publishCartEvent(userId, 'CART_CLEARED', { userId });
  }

  // 全选/取消全选
  async toggleSelectAll(userId: number, selected: boolean): Promise<CartItem[]> {
    await this.db.query(
      'UPDATE carts SET selected = $1, updated_at = NOW() WHERE user_id = $2',
      [selected, userId]
    );

    await this.clearCartCache(userId);
    return this.getCart(userId);
  }

  // 获取选中的购物车项
  async getSelectedItems(userId: number): Promise<CartItem[]> {
    const cartItems = await this.getCart(userId);
    return cartItems.filter(item => item.selected);
  }

  // 获取购物车数量
  async getCartCount(userId: number): Promise<number> {
    const cacheKey = `cart:${userId}:count`;
    
    try {
      const cached = await this.redis.get(cacheKey);
      if (cached) {
        return parseInt(cached);
      }
    } catch (error) {
      logger.warn('Redis读取失败:', error);
    }

    const result = await this.db.query(
      'SELECT COALESCE(SUM(quantity), 0) as count FROM carts WHERE user_id = $1',
      [userId]
    );

    const count = parseInt(result.rows[0].count);
    
    try {
      await this.redis.setex(cacheKey, 300, count.toString());
    } catch (error) {
      logger.warn('Redis写入失败:', error);
    }

    return count;
  }

  // 获取有效购物车项(用于结算)
  async getValidCartItemsForCheckout(userId: number): Promise<{
    items: CartItem[];
    totalAmount: number;
    skuIds: number[];
  }> {
    const cartItems = await this.getSelectedItems(userId);

    if (cartItems.length === 0) {
      throw new Error('购物车为空,请先添加商品');
    }

    // 验证库存和价格
    const validItems: CartItem[] = [];
    let totalAmount = 0;
    const skuIds: number[] = [];

    for (const item of cartItems) {
      // 重新检查库存
      const stockCheck = await this.db.query(
        'SELECT stock, price FROM product_skus WHERE id = $1',
        [item.skuId]
      );

      const sku = stockCheck.rows[0];

      if (!sku || sku.stock < item.quantity) {
        // 标记库存不足的商品
        logger.warn(`商品库存不足: skuId=${item.skuId}, requested=${item.quantity}, available=${sku?.stock || 0}`);
        continue;
      }

      // 检查商品价格是否有变动
      const currentPrice = parseFloat(sku.price);
      const originalPrice = item.sku.price;

      validItems.push({
        ...item,
        sku: {
          ...item.sku,
          price: currentPrice,
          stock: sku.stock
        }
      });

      totalAmount += currentPrice * item.quantity;
      skuIds.push(item.skuId);
    }

    if (validItems.length === 0) {
      throw new Error('所选商品库存不足,请调整数量后重试');
    }

    return {
      items: validItems,
      totalAmount,
      skuIds
    };
  }

  // 私有方法:映射购物车项
  private mapCartItem(row: any): CartItem {
    return {
      id: row.id,
      userId: row.user_id,
      skuId: row.sku_id,
      quantity: row.quantity,
      selected: row.selected,
      addedAt: row.added_at,
      product: row.product ? JSON.parse(row.product) : null,
      sku: row.sku ? JSON.parse(row.sku) : null
    };
  }

  // 私有方法:根据ID获取购物车项
  private async getCartItemById(cartItemId: number): Promise<CartItem> {
    const result = await this.db.query(
      `SELECT 
        c.*,
        p.id as product_id,
        p.name as product_name,
        p.images as product_images,
        p.price as product_price,
        p.status as product_status,
        sku.specs,
        sku.price as sku_price,
        sku.stock as sku_stock,
        sku.image as sku_image
      FROM carts c
      JOIN product_skus sku ON c.sku_id = sku.id
      JOIN products p ON sku.product_id = p.id
      WHERE c.id = $1`,
      [cartItemId]
    );

    if (result.rows.length === 0) {
      throw new Error('购物车项不存在');
    }

    const row = result.rows[0];
    return {
      id: row.id,
      userId: row.user_id,
      skuId: row.sku_id,
      quantity: row.quantity,
      selected: row.selected,
      addedAt: row.added_at,
      product: {
        id: row.product_id,
        name: row.product_name,
        images: row.product_images,
        price: parseFloat(row.product_price),
        status: row.product_status
      },
      sku: {
        specs: row.specs,
        price: parseFloat(row.sku_price),
        stock: row.sku_stock,
        image: row.sku_image
      }
    };
  }

  // 私有方法:清除购物车缓存
  private async clearCartCache(userId: number): Promise<void> {
    const keys = [
      `cart:${userId}`,
      `cart:${userId}:count`,
      `checkout:${userId}:*`
    ];

    try {
      for (const key of keys) {
        if (key.includes('*')) {
          const pattern = key.replace('*', '');
          const stream = this.redis.scanStream({ match: `${pattern}*` });
          stream.on('data', (keys) => {
            if (keys.length) {
              this.redis.del(...keys);
            }
          });
        } else {
          await this.redis.del(key);
        }
      }
    } catch (error) {
      logger.warn('清除缓存失败:', error);
    }
  }

  // 私有方法:发布购物车事件
  private async publishCartEvent(userId: number, event: string, data: any): Promise<void> {
    try {
      const eventData = {
        event,
        userId,
        data,
        timestamp: new Date().toISOString()
      };

      await this.redis.publish('cart_events', JSON.stringify(eventData));
    } catch (error) {
      logger.warn('发布事件失败:', error);
    }
  }
}
// services/order-service/src/services/OrderService.ts
import { Pool } from 'pg';
import Redis from 'ioredis';
import { v4 as uuidv4 } from 'uuid';
import { EventEmitter } from 'events';
import { logger } from '../utils/logger';
import { CartService } from './CartService';
import {
  CreateOrderDTO,
  Order,
  OrderItem,
  OrderStatus,
  PaymentInfo,
  ShippingInfo
} from '../types';

export class OrderService extends EventEmitter {
  private db: Pool;
  private redis: Redis;
  private cartService: CartService;

  constructor(db: Pool, redis: Redis, cartService: CartService) {
    super();
    this.db = db;
    this.redis = redis;
    this.cartService = cartService;
  }

  // 创建订单
  async createOrder(userId: number, dto: CreateOrderDTO): Promise<Order> {
    const { addressId, cartItemIds, note, couponId, pointsToUse } = dto;

    // 开启事务
    const client = await this.db.connect();
    
    try {
      await client.query('BEGIN');

      // 1. 获取并验证收货地址
      const address = await this.getAddress(client, userId, addressId);
      if (!address) {
        throw new Error('收货地址不存在');
      }

      // 2. 获取购物车项并验证
      let cartItems: CartItem[];
      if (cartItemIds && cartItemIds.length > 0) {
        cartItems = await this.getCartItemsByIds(client, userId, cartItemIds);
      } else {
        // 使用所有选中的购物车项
        cartItems = await this.cartService.getSelectedItems(userId);
      }

      if (cartItems.length === 0) {
        throw new Error('购物车为空');
      }

      // 3. 验证库存并锁定
      const { validItems, totalAmount, skuIds } = await this.validateAndLockStock(
        client,
        cartItems
      );

      // 4. 计算优惠
      const { discountAmount, couponDiscount, pointsDiscount, finalAmount } =
        await this.calculateDiscounts(
          client,
          userId,
          totalAmount,
          couponId,
          pointsToUse
        );

      // 5. 生成订单号
      const orderNo = this.generateOrderNo();

      // 6. 创建订单
      const order = await this.createOrderRecord(
        client,
        userId,
        orderNo,
        finalAmount,
        totalAmount,
        discountAmount,
        couponDiscount,
        pointsDiscount,
        address,
        note
      );

      // 7. 创建订单项
      const orderItems = await this.createOrderItems(client, order.id, validItems);

      // 8. 扣减库存
      await this.decreaseStock(client, validItems);

      // 9. 使用优惠券
      if (couponId) {
        await this.useCoupon(client, userId, couponId, order.id);
      }

      // 10. 扣除积分
      if (pointsToUse && pointsToUse > 0) {
        await this.usePoints(client, userId, pointsToUse, order.id);
      }

      // 11. 清空已购买的购物车项
      await this.clearUsedCartItems(client, userId, validItems);

      // 12. 提交事务
      await client.query('COMMIT');

      // 发布订单创建事件
      this.emit('order.created', {
        orderId: order.id,
        orderNo,
        userId,
        amount: finalAmount,
        items: orderItems
      });

      logger.info(`订单创建成功: ${orderNo}`);

      return {
        ...order,
        items: orderItems
      };

    } catch (error) {
      await client.query('ROLLBACK');
      logger.error('订单创建失败:', error);
      throw error;
    } finally {
      client.release();
    }
  }

  // 获取订单详情
  async getOrderById(orderId: number, userId?: number): Promise<Order | null> {
    const query = `
      SELECT 
        o.*,
        u.username as user_username,
        u.phone as user_phone,
        addr.receiver as address_receiver,
        addr.phone as address_phone,
        addr.province as address_province,
        addr.city as address_city,
        addr.district as address_district,
        addr.address as address_detail
      FROM orders o
      LEFT JOIN users u ON o.user_id = u.id
      LEFT JOIN addresses addr ON o.address_id = addr.id
      WHERE o.id = $1
    `;

    const result = await this.db.query(query, [orderId]);

    if (result.rows.length === 0) {
      return null;
    }

    const row = result.rows[0];

    // 验证用户权限
    if (userId && row.user_id !== userId) {
      throw new Error('无权访问此订单');
    }

    // 获取订单项
    const items = await this.getOrderItems(orderId);

    return {
      id: row.id,
      orderNo: row.order_no,
      userId: row.user_id,
      status: row.status,
      totalAmount: parseFloat(row.total_amount),
      payAmount: parseFloat(row.pay_amount),
      freight: parseFloat(row.freight),
      discount: parseFloat(row.discount),
      couponDiscount: parseFloat(row.coupon_discount),
      pointsUsed: row.points_used,
      pointsAmount: parseFloat(row.points_amount),
      receiverName: row.address_receiver,
      receiverPhone: row.address_phone,
      receiverAddress: `${row.address_province}${row.address_city}${row.address_district}${row.address_detail}`,
      shippingMethod: row.shipping_method,
      shippingNo: row.shipping_no,
      buyerNote: row.buyer_note,
      sellerNote: row.seller_note,
      paidAt: row.paid_at,
      shippedAt: row.shipped_at,
      deliveredAt: row.delivered_at,
      completedAt: row.completed_at,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
      items
    };
  }

  // 获取用户订单列表
  async getUserOrders(
    userId: number,
    params: {
      status?: OrderStatus;
      page?: number;
      limit?: number;
      startDate?: Date;
      endDate?: Date;
    }
  ): Promise<{ orders: Order[]; total: number; pages: number }> {
    const { status, page = 1, limit = 10, startDate, endDate } = params;

    let whereClause = 'WHERE o.user_id = $1';
    const queryParams: any[] = [userId];
    let paramIndex = 2;

    if (status) {
      whereClause += ` AND o.status = $${paramIndex}`;
      queryParams.push(status);
      paramIndex++;
    }

    if (startDate) {
      whereClause += ` AND o.created_at >= $${paramIndex}`;
      queryParams.push(startDate);
      paramIndex++;
    }

    if (endDate) {
      whereClause += ` AND o.created_at <= $${paramIndex}`;
      queryParams.push(endDate);
      paramIndex++;
    }

    // 获取总数
    const countQuery = `
      SELECT COUNT(*) as total
      FROM orders o
      ${whereClause}
    `;

    const countResult = await this.db.query(countQuery, queryParams);
    const total = parseInt(countResult.rows[0].total);

    // 获取订单列表
    const offset = (page - 1) * limit;
    const ordersQuery = `
      SELECT 
        o.*,
        addr.receiver as address_receiver,
        addr.phone as address_phone,
        addr.province as address_province,
        addr.city as address_city,
        addr.district as address_district,
        addr.address as address_detail
      FROM orders o
      LEFT JOIN addresses addr ON o.address_id = addr.id
      ${whereClause}
      ORDER BY o.created_at DESC
      LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
    `;

    queryParams.push(limit, offset);
    const ordersResult = await this.db.query(ordersQuery, queryParams);

    const orders = await Promise.all(
      ordersResult.rows.map(async (row) => {
        const items = await this.getOrderItems(row.id);
        return {
          id: row.id,
          orderNo: row.order_no,
          userId: row.user_id,
          status: row.status,
          totalAmount: parseFloat(row.total_amount),
          payAmount: parseFloat(row.pay_amount),
          freight: parseFloat(row.freight),
          discount: parseFloat(row.discount),
          couponDiscount: parseFloat(row.coupon_discount),
          pointsUsed: row.points_used,
          pointsAmount: parseFloat(row.points_amount),
          receiverName: row.address_receiver,
          receiverPhone: row.address_phone,
          receiverAddress: `${row.address_province}${row.address_city}${row.address_district}${row.address_detail}`,
          shippingMethod: row.shipping_method,
          shippingNo: row.shipping_no,
          buyerNote: row.buyer_note,
          sellerNote: row.seller_note,
          paidAt: row.paid_at,
          shippedAt: row.shipped_at,
          deliveredAt: row.delivered_at,
          completedAt: row.completed_at,
          createdAt: row.created_at,
          updatedAt: row.updated_at,
          items
        };
      })
    );

    return {
      orders,
      total,
      pages: Math.ceil(total / limit)
    };
  }

  // 支付订单
  async payOrder(orderId: number, paymentInfo: PaymentInfo): Promise<Order> {
    const client = await this.db.connect();

    try {
      await client.query('BEGIN');

      // 1. 获取并锁定订单
      const order = await this.getOrderForUpdate(client, orderId);

      if (!order) {
        throw new Error('订单不存在');
      }

      if (order.status !== 'PENDING') {
        throw new Error('订单状态异常,无法支付');
      }

      // 2. 验证支付金额
      if (Math.abs(paymentInfo.amount - order.payAmount) > 0.01) {
        throw new Error('支付金额不匹配');
      }

      // 3. 创建支付记录
      const payment = await this.createPaymentRecord(
        client,
        orderId,
        paymentInfo
      );

      // 4. 更新订单状态
      const now = new Date();
      const updateResult = await client.query(
        `UPDATE orders 
         SET status = 'PAID', 
             paid_at = $1,
             updated_at = $1
         WHERE id = $2
         RETURNING *`,
        [now, orderId]
      );

      // 5. 更新商品销量
      await this.updateProductSales(client, orderId);

      // 6. 提交事务
      await client.query('COMMIT');

      const updatedOrder = await this.getOrderById(orderId);

      // 发布支付成功事件
      this.emit('order.paid', {
        orderId,
        orderNo: order.orderNo,
        amount: paymentInfo.amount,
        paymentMethod: paymentInfo.method,
        transactionId: paymentInfo.transactionId
      });

      logger.info(`订单支付成功: ${order.orderNo}`);

      return updatedOrder!;

    } catch (error) {
      await client.query('ROLLBACK');
      logger.error('订单支付失败:', error);
      throw error;
    } finally {
      client.release();
    }
  }

  // 取消订单
  async cancelOrder(orderId: number, userId: number, reason: string): Promise<Order> {
    const client = await this.db.connect();

    try {
      await client.query('BEGIN');

      // 1. 获取并锁定订单
      const order = await this.getOrderForUpdate(client, orderId);

      if (!order) {
        throw new Error('订单不存在');
      }

      if (order.user_id !== userId) {
        throw new Error('无权操作此订单');
      }

      if (!['PENDING', 'PAID'].includes(order.status)) {
        throw new Error('当前订单状态无法取消');
      }

      // 2. 如果已支付,需要退款
      if (order.status === 'PAID') {
        await this.initiateRefund(client, order);
      }

      // 3. 恢复库存
      await this.restoreStock(client, orderId);

      // 4. 返还优惠券
      if (order.coupon_id) {
        await this.returnCoupon(client, orderId);
      }

      // 5. 返还积分
      if (order.points_used > 0) {
        await this.returnPoints(client, orderId);
      }

      // 6. 更新订单状态
      const now = new Date();
      const updateResult = await client.query(
        `UPDATE orders 
         SET status = 'CANCELLED',
             cancel_reason = $1,
             cancelled_at = $2,
             updated_at = $2
         WHERE id = $3
         RETURNING *`,
        [reason, now, orderId]
      );

      // 7. 提交事务
      await client.query('COMMIT');

      const cancelledOrder = await this.getOrderById(orderId);

      // 发布订单取消事件
      this.emit('order.cancelled', {
        orderId,
        orderNo: order.orderNo,
        reason,
        refunded: order.status === 'PAID'
      });

      logger.info(`订单已取消: ${order.orderNo}`);

      return cancelledOrder!;

    } catch (error) {
      await client.query('ROLLBACK');
      logger.error('取消订单失败:', error);
      throw error;
    } finally {
      client.release();
    }
  }

  // 确认收货
  async confirmReceipt(orderId: number, userId: number): Promise<Order> {
    const order = await this.getOrderById(orderId, userId);

    if (!order) {
      throw new Error('订单不存在');
    }

    if (order.status !== 'SHIPPED') {
      throw new Error('订单状态异常,无法确认收货');
    }

    const now = new Date();
    const result = await this.db.query(
      `UPDATE orders 
       SET status = 'DELIVERED',
           delivered_at = $1,
           updated_at = $1
       WHERE id = $2
       RETURNING *`,
      [now, orderId]
    );

    const updatedOrder = await this.getOrderById(orderId);

    // 发布确认收货事件
    this.emit('order.delivered', {
      orderId,
      orderNo: order.orderNo,
      deliveredAt: now
    });

    return updatedOrder!;
  }

  // 申请退款
  async requestRefund(
    orderId: number,
    userId: number,
    reason: string,
    amount?: number
  ): Promise<Order> {
    const order = await this.getOrderById(orderId, userId);

    if (!order) {
      throw new Error('订单不存在');
    }

    if (!['PAID', 'SHIPPED', 'DELIVERED'].includes(order.status)) {
      throw new Error('当前订单状态无法申请退款');
    }

    const refundAmount = amount || order.payAmount;

    if (refundAmount > order.payAmount) {
      throw new Error('退款金额不能超过实付金额');
    }

    const result = await this.db.query(
      `UPDATE orders 
       SET status = 'REFUNDING',
           refund_reason = $1,
           refund_amount = $2,
           refund_requested_at = $3,
           updated_at = $3
       WHERE id = $4
       RETURNING *`,
      [reason, refundAmount, new Date(), orderId]
    );

    const updatedOrder = await this.getOrderById(orderId);

    // 发布退款申请事件
    this.emit('order.refund_requested', {
      orderId,
      orderNo: order.orderNo,
      reason,
      amount: refundAmount
    });

    return updatedOrder!;
  }

  // 获取订单统计
  async getOrderStatistics(userId: number): Promise<{
    totalOrders: number;
    totalSpent: number;
    pendingOrders: number;
    completedOrders: number;
  }> {
    const query = `
      SELECT 
        COUNT(*) as total_orders,
        COALESCE(SUM(pay_amount), 0) as total_spent,
        COUNT(CASE WHEN status = 'PENDING' THEN 1 END) as pending_orders,
        COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) as completed_orders
      FROM orders
      WHERE user_id = $1
    `;

    const result = await this.db.query(query, [userId]);
    const row = result.rows[0];

    return {
      totalOrders: parseInt(row.total_orders),
      totalSpent: parseFloat(row.total_spent),
      pendingOrders: parseInt(row.pending_orders),
      completedOrders: parseInt(row.completed_orders)
    };
  }

  // 私有方法:获取地址
  private async getAddress(client: any, userId: number, addressId: number) {
    const result = await client.query(
      'SELECT * FROM addresses WHERE id = $1 AND user_id = $2',
      [addressId, userId]
    );
    return result.rows[0] || null;
  }

  // 私有方法:根据ID获取购物车项
  private async getCartItemsByIds(
    client: any,
    userId: number,
    cartItemIds: number[]
  ): Promise<CartItem[]> {
    const placeholders = cartItemIds.map((_, i) => `$${i + 2}`).join(',');
    const query = `
      SELECT 
        c.*,
        p.id as product_id,
        p.name as product_name,
        p.images as product_images,
        p.price as product_price,
        p.status as product_status,
        sku.specs,
        sku.price as sku_price,
        sku.stock as sku_stock,
        sku.image as sku_image
      FROM carts c
      JOIN product_skus sku ON c.sku_id = sku.id
      JOIN products p ON sku.product_id = p.id
      WHERE c.id = ANY(ARRAY[${placeholders}]) AND c.user_id = $1
    `;

    const result = await client.query(query, [userId, ...cartItemIds]);
    return result.rows.map(row => ({
      id: row.id,
      userId: row.user_id,
      skuId: row.sku_id,
      quantity: row.quantity,
      selected: row.selected,
      addedAt: row.added_at,
      product: {
        id: row.product_id,
        name: row.product_name,
        images: row.product_images,
        price: parseFloat(row.product_price),
        status: row.product_status
      },
      sku: {
        specs: row.specs,
        price: parseFloat(row.sku_price),
        stock: row.sku_stock,
        image: row.sku_image
      }
    }));
  }

  // 私有方法:验证并锁定库存
  private async validateAndLockStock(
    client: any,
    cartItems: CartItem[]
  ): Promise<{
    validItems: CartItem[];
    totalAmount: number;
    skuIds: number[];
  }> {
    const validItems: CartItem[] = [];
    let totalAmount = 0;
    const skuIds: number[] = [];

    for (const item of cartItems) {
      // 检查库存
      const stockResult = await client.query(
        'SELECT stock, price FROM product_skus WHERE id = $1 FOR UPDATE',
        [item.skuId]
      );

      if (stockResult.rows.length === 0) {
        continue;
      }

      const sku = stockResult.rows[0];
      const currentStock = sku.stock;
      const currentPrice = parseFloat(sku.price);

      if (currentStock < item.quantity) {
        logger.warn(`库存不足: skuId=${item.skuId}, requested=${item.quantity}, available=${currentStock}`);
        continue;
      }

      // 锁定库存(减少可用库存)
      await client.query(
        'UPDATE product_skus SET stock = stock - $1 WHERE id = $2',
        [item.quantity, item.skuId]
      );

      validItems.push({
        ...item,
        sku: {
          ...item.sku,
          price: currentPrice,
          stock: currentStock
        }
      });

      totalAmount += currentPrice * item.quantity;
      skuIds.push(item.skuId);
    }

    if (validItems.length === 0) {
      throw new Error('所有商品库存不足');
    }

    return { validItems, totalAmount, skuIds };
  }

  // 私有方法:计算优惠
  private async calculateDiscounts(
    client: any,
    userId: number,
    totalAmount: number,
    couponId?: number,
    pointsToUse?: number
  ): Promise<{
    discountAmount: number;
    couponDiscount: number;
    pointsDiscount: number;
    finalAmount: number;
  }> {
    let couponDiscount = 0;
    let pointsDiscount = 0;

    // 计算优惠券折扣
    if (couponId) {
      const couponResult = await client.query(
        `SELECT * FROM coupons 
         WHERE id = $1 AND status = 'ACTIVE' 
         AND start_time <= NOW() AND end_time >= NOW()
         FOR UPDATE`,
        [couponId]
      );

      if (couponResult.rows.length > 0) {
        const coupon = couponResult.rows[0];
        
        if (totalAmount >= parseFloat(coupon.min_amount)) {
          if (coupon.type === 'FIXED') {
            couponDiscount = parseFloat(coupon.value);
          } else if (coupon.type === 'PERCENTAGE') {
            couponDiscount = totalAmount * parseFloat(coupon.value) / 100;
            if (coupon.max_discount && couponDiscount > parseFloat(coupon.max_discount)) {
              couponDiscount = parseFloat(coupon.max_discount);
            }
          }
        }
      }
    }

    // 计算积分折扣
    if (pointsToUse && pointsToUse > 0) {
      const pointsResult = await client.query(
        'SELECT points FROM users WHERE id = $1 FOR UPDATE',
        [userId]
      );

      const userPoints = parseInt(pointsResult.rows[0].points);

      if (userPoints >= pointsToUse) {
        // 假设100积分 = 1元
        pointsDiscount = Math.min(pointsToUse / 100, totalAmount - couponDiscount);
      }
    }

    const discountAmount = couponDiscount + pointsDiscount;
    const finalAmount = Math.max(0, totalAmount - discountAmount);

    return {
      discountAmount,
      couponDiscount,
      pointsDiscount,
      finalAmount
    };
  }

  // 私有方法:生成订单号
  private generateOrderNo(): string {
    const date = new Date();
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const random = uuidv4().replace(/-/g, '').substring(0, 12).toUpperCase();
    
    return `${year}${month}${day}${random}`;
  }

  // 私有方法:创建订单记录
  private async createOrderRecord(
    client: any,
    userId: number,
    orderNo: string,
    finalAmount: number,
    totalAmount: number,
    discountAmount: number,
    couponDiscount: number,
    pointsDiscount: number,
    address: any,
    note: string
  ): Promise<Order> {
    const result = await client.query(
      `INSERT INTO orders (
        order_no, user_id, status, total_amount, pay_amount,
        discount, coupon_discount, points_used, points_amount,
        receiver_name, receiver_phone, receiver_address,
        buyer_note, created_at, updated_at
      ) VALUES ($1, $2, 'PENDING', $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, NOW(), NOW())
      RETURNING *`,
      [
        orderNo,
        userId,
        totalAmount,
        finalAmount,
        discountAmount,
        couponDiscount,
        pointsDiscount > 0 ? Math.floor(pointsDiscount * 100) : 0,
        pointsDiscount,
        address.receiver,
        address.phone,
        `${address.province}${address.city}${address.district}${address.address}`,
        note
      ]
    );

    return result.rows[0];
  }

  // 私有方法:创建订单项
  private async createOrderItems(
    client: any,
    orderId: number,
    cartItems: CartItem[]
  ): Promise<OrderItem[]> {
    const orderItems: OrderItem[] = [];

    for (const item of cartItems) {
      const result = await client.query(
        `INSERT INTO order_items (
          order_id, product_id, sku_id, product_name, sku_specs,
          sku_image, price, original_price, quantity, subtotal, created_at
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())
        RETURNING *`,
        [
          orderId,
          item.product.id,
          item.skuId,
          item.product.name,
          item.sku.specs,
          item.sku.image,
          item.sku.price,
          item.product.price,
          item.quantity,
          item.sku.price * item.quantity
        ]
      );

      orderItems.push(result.rows[0]);
    }

    return orderItems;
  }

  // 私有方法:扣减库存
  private async decreaseStock(client: any, cartItems: CartItem[]): Promise<void> {
    for (const item of cartItems) {
      await client.query(
        'UPDATE product_skus SET stock = stock - $1 WHERE id = $2',
        [item.quantity, item.skuId]
      );
    }
  }

  // 私有方法:使用优惠券
  private async useCoupon(client: any, userId: number, couponId: number, orderId: number): Promise<void> {
    await client.query(
      `UPDATE user_coupons 
       SET status = 'USED', used_at = NOW(), order_id = $3
       WHERE user_id = $1 AND coupon_id = $2 AND status = 'UNUSED'`,
      [userId, couponId, orderId]
    );

    await client.query(
      'UPDATE coupons SET used_count = used_count + 1 WHERE id = $1',
      [couponId]
    );
  }

  // 私有方法:使用积分
  private async usePoints(client: any, userId: number, points: number, orderId: number): Promise<void> {
    await client.query(
      'UPDATE users SET points = points - $1 WHERE id = $2',
      [points, userId]
    );

    await client.query(
      'INSERT INTO points_transactions (user_id, points, type, reference_id, created_at) VALUES ($1, $2, $3, $4, NOW())',
      [userId, -points, 'ORDER_PAYMENT', orderId]
    );
  }

  // 私有方法:清空已使用的购物车项
  private async clearUsedCartItems(
    client: any,
    userId: number,
    cartItems: CartItem[]
  ): Promise<void> {
    const skuIds = cartItems.map(item => item.skuId);
    await client.query(
      'DELETE FROM carts WHERE user_id = $1 AND sku_id = ANY($2)',
      [userId, skuIds]
    );
  }

  // 私有方法:获取订单详情(带锁)
  private async getOrderForUpdate(client: any, orderId: number) {
    const result = await client.query(
      'SELECT * FROM orders WHERE id = $1 FOR UPDATE',
      [orderId]
    );
    return result.rows[0] || null;
  }

  // 私有方法:创建支付记录
  private async createPaymentRecord(
    client: any,
    orderId: number,
    paymentInfo: PaymentInfo
  ): Promise<any> {
    const paymentNo = `PAY${Date.now()}${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
    
    const result = await client.query(
      `INSERT INTO payments (
        order_id, payment_no, method, amount, currency,
        transaction_id, gateway_response, status, paid_at, created_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, 'SUCCESS', NOW(), NOW())
      RETURNING *`,
      [
        orderId,
        paymentNo,
        paymentInfo.method,
        paymentInfo.amount,
        paymentInfo.currency || 'CNY',
        paymentInfo.transactionId,
        paymentInfo.gatewayResponse ? JSON.stringify(paymentInfo.gatewayResponse) : null
      ]
    );

    return result.rows[0];
  }

  // 私有方法:更新商品销量
  private async updateProductSales(client: any, orderId: number): Promise<void> {
    await client.query(
      `UPDATE products p
       SET sales = sales + (
         SELECT SUM(quantity) FROM order_items WHERE order_id = $1 AND product_id = p.id
       )
       WHERE EXISTS (
         SELECT 1 FROM order_items WHERE order_id = $1 AND product_id = p.id
       )`,
      [orderId]
    );
  }

  // 私有方法:发起退款
  private async initiateRefund(client: any, order: Order): Promise<void> {
    await client.query(
      `INSERT INTO refund_records (
        order_id, refund_no, amount, reason, status, created_at
      ) VALUES ($1, $2, $3, '用户取消订单', 'PENDING', NOW())`,
      [order.id, `REF${Date.now()}`, order.payAmount]
    );
  }

  // 私有方法:恢复库存
  private async restoreStock(client: any, orderId: number): Promise<void> {
    await client.query(
      `UPDATE product_skus ps
       SET stock = stock + (
         SELECT quantity FROM order_items WHERE order_id = $1 AND sku_id = ps.id
       )
       WHERE EXISTS (
         SELECT 1 FROM order_items WHERE order_id = $1 AND sku_id = ps.id
       )`,
      [orderId]
    );
  }

  // 私有方法:返还优惠券
  private async returnCoupon(client: any, orderId: number): Promise<void> {
    await client.query(
      `UPDATE user_coupons uc
       SET status = 'UNUSED', used_at = NULL, order_id = NULL
       WHERE EXISTS (
         SELECT 1 FROM orders WHERE id = $1 AND coupon_id = uc.coupon_id
       )`,
      [orderId]
    );

    await client.query(
      `UPDATE coupons c
       SET used_count = GREATEST(0, used_count - 1)
       WHERE EXISTS (
         SELECT 1 FROM orders WHERE id = $1 AND coupon_id = c.id
       )`,
      [orderId]
    );
  }

  // 私有方法:返还积分
  private async returnPoints(client: any, orderId: number): Promise<void> {
    const orderResult = await client.query(
      'SELECT user_id, points_used FROM orders WHERE id = $1',
      [orderId]
    );

    if (orderResult.rows.length > 0) {
      const { user_id, points_used } = orderResult.rows[0];
      
      if (points_used > 0) {
        await client.query(
          'UPDATE users SET points = points + $1 WHERE id = $2',
          [points_used, user_id]
        );

        await client.query(
          'INSERT INTO points_transactions (user_id, points, type, reference_id, created_at) VALUES ($1, $2, $3, $4, NOW())',
          [user_id, points_used, 'ORDER_REFUND', orderId]
        );
      }
    }
  }

  // 私有方法:获取订单项
  private async getOrderItems(orderId: number): Promise<OrderItem[]> {
    const result = await this.db.query(
      'SELECT * FROM order_items WHERE order_id = $1',
      [orderId]
    );
    return result.rows;
  }
}

四、系统监控与运维

4.1 监控体系架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                           监控体系架构图                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        数据采集层                                     │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │ 应用指标   │  │ 系统指标   │  │ 业务指标   │  │ 日志数据   │  │   │
│  │  │ (Prometheus)│  │  (Node Exporter)│  │  (Custom)  │  │  (Fluentd)  │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        数据处理层                                     │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │  Prometheus │  │  Elasticsearch │  │  Kafka     │  │  Logstash  │  │   │
│  │  │  (时序数据)  │  │  (日志存储)  │  │  (消息队列)  │  │  (日志处理)  │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        存储层                                         │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │  Prometheus │  │  InfluxDB   │  │  Elasticsearch │  │  PostgreSQL │  │   │
│  │  │  TSDB      │  │  (历史数据)  │  │  (日志)    │  │  (业务数据)  │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        可视化层                                       │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │   Grafana   │  │   Kibana    │  │   Alertmanager │  │  Skywalking │  │   │
│  │  │  (仪表盘)   │  │  (日志分析)  │  │  (告警)    │  │  (链路追踪)  │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                        告警通知层                                     │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │   Email    │  │   SMS      │  │   Slack    │  │   DingTalk │  │   │
│  │  │  邮件通知   │  │  短信告警   │  │  即时通讯   │  │  钉钉机器人 │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

4.2 Prometheus配置

# monitoring/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: minijd-production
    env: prod

# 告警规则文件
rule_files:
  - /etc/prometheus/rules/*.yml

# 抓取配置
scrape_configs:
  # Prometheus自身监控
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter - 系统指标
  - job_name: 'node-exporter'
    static_configs:
      - targets:
        - 'minijd-monitoring:9100'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: minijd-monitoring:9100

  # 应用服务监控
  - job_name: 'minijd-services'
    static_configs:
      - targets:
        - 'user-service:3001'
        - 'product-service:3002'
        - 'order-service:3003'
        - 'payment-service:3004'
        - 'inventory-service:3005'
        - 'search-service:3006'
    metrics_path: /metrics
    scrape_interval: 10s
    basic_auth:
      username: prometheus
      password: ${PROMETHEUS_PASSWORD}

  # API Gateway监控
  - job_name: 'api-gateway'
    static_configs:
      - targets:
        - 'api-gateway:3000'
    metrics_path: /metrics

  # 数据库监控
  - job_name: 'postgres'
    static_configs:
      - targets:
        - 'postgres-exporter:9187'
    relabel_configs:
      - source_labels: [__meta_database]
        target_label: database

  # Redis监控
  - job_name: 'redis'
    static_configs:
      - targets:
        - 'redis-exporter:9121'
    metrics_path: /metrics

  # Elasticsearch监控
  - job_name: 'elasticsearch'
    static_configs:
      - targets:
        - 'elasticsearch-exporter:9114'
    metrics_path: /metrics

  # RabbitMQ监控
  - job_name: 'rabbitmq'
    static_configs:
      - targets:
        - 'rabbitmq-exporter:9419'
    metrics_path: /metrics

  # Docker容器监控
  - job_name: 'docker'
    static_configs:
      - targets:
        - 'cadvisor:8080'

  # 前端指标收集 (通过Pushgateway)
  - job_name: 'frontend-metrics'
    honor_labels: true
    static_configs:
      - targets:
        - 'pushgateway:9091'

# 远程写入配置 (可选,用于长期存储)
remote_write:
  - url: https://prometheus-write.example.com/api/v1/write
    basic_auth:
      username: ${REMOTE_WRITE_USERNAME}
      password: ${REMOTE_WRITE_PASSWORD}

# 告警管理器配置
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

4.3 告警规则配置

# monitoring/rules/application.yml
groups:
  - name: application_alerts
    rules:
      # 服务宕机告警
      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
          team: platform
        annotations:
          summary: "服务 {{ $labels.job }} 已宕机"
          description: "服务 {{ $labels.job }} 在实例 {{ $labels.instance }} 上不可达超过 1 分钟"

      # 高响应时间告警
      - alert: HighResponseTime
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
        for: 5m
        labels:
          severity: warning
          team: platform
        annotations:
          summary: "服务 {{ $labels.job }} 响应时间过长"
          description: "95% 的请求响应时间超过 2 秒"

      # 高错误率告警
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
          team: platform
        annotations:
          summary: "服务 {{ $labels.job }} 错误率过高"
          description: "5xx 错误率超过 5%"

      # 内存使用率告警
      - alert: HighMemoryUsage
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 85
        for: 10m
        labels:
          severity: warning
          team: platform
        annotations:
          summary: "节点 {{ $labels.instance }} 内存使用率过高"
          description: "内存使用率达到 {{ $value | humanizePercentage }}"

      # CPU使用率告警
      - alert: HighCPUUsage
        expr: 100 - (avg by(instance)(irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 10m
        labels:
          severity: warning
          team: platform
        annotations:
          summary: "节点 {{ $labels.instance }} CPU 使用率过高"
          description: "CPU 使用率达到 {{ $value | humanizePercentage }}"

  - name: business_alerts
    rules:
      # 订单量突降告警
      - alert: OrderVolumeDrop
        expr: |
          (
            rate(orders_created_total[1h] offset 1h) - 
            rate(orders_created_total[1h])
          ) / rate(orders_created_total[1h] offset 1h) < -0.3
        for: 15m
        labels:
          severity: critical
          team: business
        annotations:
          summary: "订单量突降"
          description: "订单创建量相比1小时前下降超过 30%"

      # 支付失败率告警
      - alert: HighPaymentFailureRate
        expr: rate(payment_failures_total[5m]) / rate(payment_attempts_total[5m]) > 0.1
        for: 5m
        labels:
          severity: critical
          team: business
        annotations:
          summary: "支付失败率过高"
          description: "支付失败率超过 10%"

      # 库存预警
      - alert: LowStockAlert
        expr: |
          (
            sum by (sku_id) (inventory_quantity{warehouse = "default"}) /
            sum by (sku_id) (inventory_max_quantity{warehouse = "default"})
          ) < 0.1
        for: 5m
        labels:
          severity: warning
          team: business
        annotations:
          summary: "商品库存不足"
          description: "SKU {{ $labels.sku_id }} 库存低于 10%"

      # 用户注册量突降
      - alert: UserRegistrationDrop
        expr: |
          (
            rate(user_registrations_total[1h] offset 1h) - 
            rate(user_registrations_total[1h])
          ) / rate(user_registrations_total[1h] offset 1h) < -0.2
        for: 15m
        labels:
          severity: warning
          team: business
        annotations:
          summary: "用户注册量突降"
          description: "用户注册量相比1小时前下降超过 20%"

4.4 日志收集与处理

# monitoring/fluentd/fluent.conf
# 日志源配置
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

# 从文件收集容器日志
<source>
  @type tail
  path /var/log/containers/*.log
  pos_file /var/log/fluentd-containers.log.pos
  tag kubernetes.*
  read_from_head true
  <parse>
    @type json
    time_key time
    time_format %Y-%m-%dT%H:%M:%S.%NZ
  </parse>
</source>

# 从系统收集日志
<source>
  @type systemd
  filters [{ "_SYSTEMD_UNIT": "docker.service" }]
  pos_file /var/log/fluentd-docker.pos
  read_from_head true
  tag docker
</source>

# 过滤和丰富日志
<filter kubernetes.**>
  @type kubernetes_metadata
  kube_url "https://kubernetes.default.svc"
  bearer_token_file /var/run/secrets/kubernetes.io/serviceaccount/token
  ca_file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  use_journal 'false'
  container_name_to_kubernetes_regexp '^(?<name>[^_]+)_(?<namespace>[^_]+)_[^_]+_[^_]+$'
</filter>

# 解析应用日志
<filter kubernetes.**>
  @type parser
  key_name log
  reserve_data true
  <parse>
    @type json
  </parse>
</filter>

# 错误日志单独处理
<match kubernetes.**>
  @type rewrite_tag_filter
  <rule>
    key level
    pattern "ERROR"
    tag "error.${tag}"
  </rule>
  <rule>
    key level
    pattern "WARN"
    tag "warn.${tag}"
  </rule>
  <rule>
    key level
    pattern "FATAL"
    tag "fatal.${tag}"
  </rule>
</match>

# 错误日志路由到Elasticsearch
<match error.**>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
  logstash_prefix minijd-logs-error
  type_name _doc
  include_tag_key true
  tag_key @log_name
  flush_interval 5s
  buffer_type file
  buffer_path /var/log/fluentd-buffers/error.*.buffer
  buffer_chunk_limit 1m
  buffer_queue_limit 32
  retry_max_interval 30
  num_threads 2
</match>

# 普通日志路由到Elasticsearch
<match kubernetes.**>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
  logstash_prefix minijd-logs
  type_name _doc
  include_tag_key true
  tag_key @log_name
  flush_interval 5s
  buffer_type file
  buffer_path /var/log/fluentd-buffers/normal.*.buffer
  buffer_chunk_limit 1m
  buffer_queue_limit 32
  retry_max_interval 30
  num_threads 2
</match>

# 日志告警
<match fatal.**>
  @type copy
  <store>
    @type elasticsearch
    host elasticsearch
    port 9200
    logstash_format true
    logstash_prefix minijd-logs-fatal
  </store>
  <store>
    @type webhdfs
    host hdfs-namenode
    port 50070
    path /logs/fatal/%Y%m%d/%H
  </store>
  <store>
    @type slack
    webhook_url "#{ENV['SLACK_WEBHOOK_URL']}"
    channel "#alerts"
    username "Fluentd"
    icon_emoji ":warning:"
    message "FATAL: %{message}"
  </store>
</match>

# 日志指标
<match kubernetes.**>
  @type prometheus
  <metric>
    name fluentd_input_num_records_total
    type counter
    desc The total number of input records
    <labels>
      tag ${tag}
    </labels>
  </metric>
  <metric>
    name fluentd_output_num_records_total
    type counter
    desc The total number of output records
    <labels>
      tag ${tag}
    </labels>
  </metric>
  <metric>
    name fluentd_retry_count
    type counter
    desc The total number of retries
    <labels>
      tag ${tag}
    </labels>
  </metric>
</match>

4.5 分布式链路追踪

// services/shared/tracing.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
import { ElasticsearchInstrumentation } from '@opentelemetry/instrumentation-elasticsearch';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { trace, context, SpanKind, SpanStatusCode } from '@opentelemetry/api';
import { W3CTraceContextPropagator } from '@opentelemetry/core';
import { B3Propagator, B3InjectEncoding } from '@opentelemetry/propagator-b3';
import { CompositePropagator } from '@opentelemetry/core';

// 初始化OpenTelemetry
export function initializeTracing(serviceName: string) {
  const sdk = new NodeSDK({
    resource: new Resource({
      [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
      [SemanticResourceAttributes.SERVICE_VERSION]: process.env.APP_VERSION || '1.0.0',
      [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'development',
    }),
    traceExporter: new OTLPTraceExporter({
      url: process.env.OTLP_ENDPOINT || 'http://jaeger:4318/v1/traces',
      headers: {
        Authorization: `Bearer ${process.env.OTLP_TOKEN || ''}`,
      },
    }),
    instrumentations: [
      new HttpInstrumentation({
        ignoreIncomingPaths: ['/health', '/metrics'],
        ignoreOutgoingUrls: [/localhost:9200/],
      }),
      new ExpressInstrumentation({
        ignoreLayersType: ['middleware'],
      }),
      new PgInstrumentation({
        requireParentSpan: true,
      }),
      new RedisInstrumentation(),
      new ElasticsearchInstrumentation(),
    ],
  });

  // 设置传播器
  const propagator = new CompositePropagator({
    propagators: [
      new W3CTraceContextPropagator(),
      new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }),
    ],
  });
  context.setGlobalPropagator(propagator);

  sdk.start();
  console.log(`Tracing initialized for service: ${serviceName}`);

  return sdk;
}

// 创建业务Span
export function createBusinessSpan(
  name: string,
  attributes: Record<string, any> = {}
) {
  const tracer = trace.getTracer('minijd-business');
  return tracer.startSpan(name, {
    kind: SpanKind.INTERNAL,
    attributes: {
      'minijd.business.operation': name,
      ...attributes,
    },
  });
}

// 追踪数据库操作
export function withDatabaseSpan<T>(
  operation: string,
  table: string,
  fn: () => Promise<T>
): Promise<T> {
  const tracer = trace.getTracer('minijd-database');
  return tracer.startActiveSpan(
    `db.${operation}`,
    { attributes: { 'db.table': table, 'db.operation': operation } },
    async (span) => {
      try {
        const result = await fn();
        span.setStatus({ code: SpanStatusCode.OK });
        return result;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: error instanceof Error ? error.message : 'Unknown error',
        });
        span.recordException(error as Error);
        throw error;
      } finally {
        span.end();
      }
    }
  );
}

// 追踪外部API调用
export function withExternalAPICall<T>(
  service: string,
  endpoint: string,
  fn: () => Promise<T>
): Promise<T> {
  const tracer = trace.getTracer('minijd-external-api');
  return tracer.startActiveSpan(
    `external.${service}.${endpoint}`,
    { attributes: { 'external.service': service, 'external.endpoint': endpoint } },
    async (span) => {
      try {
        const result = await fn();
        span.setStatus({ code: SpanStatusCode.OK });
        return result;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: error instanceof Error ? error.message : 'Unknown error',
        });
        span.recordException(error as Error);
        throw error;
      } finally {
        span.end();
      }
    }
  );
}

// 追踪用户操作
export function withUserActionSpan(
  userId: number,
  action: string,
  fn: () => Promise<any>
): Promise<any> {
  const tracer = trace.getTracer('minijd-user-action');
  return tracer.startActiveSpan(
    `user.${action}`,
    { attributes: { 'user.id': userId, 'user.action': action } },
    async (span) => {
      try {
        const result = await fn();
        span.setStatus({ code: SpanStatusCode.OK });
        return result;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: error instanceof Error ? error.message : 'Unknown error',
        });
        span.recordException(error as Error);
        throw error;
      } finally {
        span.end();
      }
    }
  );
}

// Express中间件
export function tracingMiddleware(serviceName: string) {
  return (req: any, res: any, next: any) => {
    const tracer = trace.getTracer(serviceName);
    const span = tracer.startSpan(`${req.method} ${req.path}`, {
      kind: SpanKind.SERVER,
      attributes: {
        'http.method': req.method,
        'http.url': req.url,
        'http.route': req.route?.path,
        'http.user_agent': req.get('User-Agent'),
        'http.client_ip': req.ip,
        'service.name': serviceName,
      },
    });

    // 将span存储到request中
    req.span = span;

    // 记录响应
    res.on('finish', () => {
      span.setAttributes({
        'http.status_code': res.statusCode,
        'http.response_content_length': res.get('Content-Length'),
      });

      if (res.statusCode >= 400) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: `HTTP ${res.statusCode}`,
        });
      } else {
        span.setStatus({ code: SpanStatusCode.OK });
      }

      span.end();
    });

    next();
  };
}

五、部署与CI/CD

5.1 Docker容器化

# services/user-service/Dockerfile
# 多阶段构建
FROM node:18-alpine AS builder

WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产阶段
FROM node:18-alpine AS production

# 设置工作目录
WORKDIR /app

# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# 设置环境变量
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# 复制必要文件
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/prisma ./prisma

# 设置权限
RUN chown -R nextjs:nodejs /app
USER nextjs

# 暴露端口
EXPOSE 3001

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# 启动应用
CMD ["node", "dist/index.js"]
# services/product-service/Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /app

# 设置Go代理
ENV GOPROXY=https://goproxy.cn,direct

# 安装依赖
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码
COPY . .

# 构建应用
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o product-service ./cmd/server

# 生产阶段
FROM alpine:3.18

RUN apk --no-cache add ca-certificates tzdata

WORKDIR /root/

# 复制二进制文件
COPY --from=builder /app/product-service .

# 设置时区
ENV TZ=Asia/Shanghai

# 创建非root用户
RUN addgroup -g 1001 -S app && adduser -S app -u 1001

USER app

EXPOSE 3002

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3002/health || exit 1

CMD ["./product-service"]

5.2 Kubernetes部署配置

# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
resources:
  - namespace.yaml
  - configmap.yaml
  - secrets.yaml
  - ingress.yaml
  - services/
  - deployments/
  - hpa.yaml
  - pdb.yaml

namespace: minijd-prod
# k8s/base/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: minijd-prod
  labels:
    name: minijd-prod
    istio-injection: enabled
# k8s/base/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: minijd-config
  namespace: minijd-prod
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  DB_HOST: "postgres.minijd-prod.svc.cluster.local"
  DB_PORT: "5432"
  DB_NAME: "minijd"
  REDIS_HOST: "redis.minijd-prod.svc.cluster.local"
  REDIS_PORT: "6379"
  ELASTICSEARCH_URL: "http://elasticsearch.minijd-prod.svc.cluster.local:9200"
  RABBITMQ_URL: "amqp://admin:password@rabbitmq.minijd-prod.svc.cluster.local:5672"
  JWT_SECRET: "your-jwt-secret-key"
  JWT_EXPIRES_IN: "7d"
  CORS_ORIGINS: "https://minijd.com,https://www.minijd.com"
  RATE_LIMIT_WINDOW_MS: "900000"
  RATE_LIMIT_MAX_REQUESTS: "100"
# k8s/base/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: minijd-secrets
  namespace: minijd-prod
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQ=  # base64 encoded
  REDIS_PASSWORD: cGFzc3dvcmQ=
  JWT_SECRET: eW91ci1qd3Qtc2VjcmV0LWtleQ==
  STRIPE_SECRET_KEY: c3RyaXBlLXNlY3JldC1rZXk=
  EMAIL_PASSWORD: eWFob28xMjM=
  SLACK_WEBHOOK_URL: aHR0cHM6Ly9ob29rcy5zbGFjay5jb20v...
# k8s/base/services/user-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: minijd-prod
  labels:
    app: user-service
    version: v1
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
        version: v1
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "3001"
        prometheus.io/path: "/metrics"
    spec:
      serviceAccountName: minijd-service-account
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        runAsGroup: 1001
        fsGroup: 1001
      containers:
        - name: user-service
          image: minijd/user-service:v1.0.0
          imagePullPolicy: Always
          ports:
            - name: http
              containerPort: 3001
              protocol: TCP
          envFrom:
            - configMapRef:
                name: minijd-config
            - secretRef:
                name: minijd-secrets
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 3001
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: 3001
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 3
          startupProbe:
            httpGet:
              path: /health
              port: 3001
            initialDelaySeconds: 60
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 10
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir: {}
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - user-service
                topologyKey: kubernetes.io/hostname
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: minijd-prod
  labels:
    app: user-service
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 3001
      targetPort: 3001
      protocol: TCP
  selector:
    app: user-service
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: user-service-ingress
  namespace: minijd-prod
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/rate-limit-window: "1m"
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://minijd.com"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
  tls:
    - hosts:
        - api.minijd.com
      secretName: minijd-tls
  rules:
    - host: api.minijd.com
      http:
        paths:
          - path: /api/users
            pathType: Prefix
            backend:
              service:
                name: user-service
                port:
                  number: 3001
# k8s/base/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
  namespace: minijd-prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: user-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "1000"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

5.3 CI/CD流水线

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_PREFIX: ${{ github.repository_owner }}/minijd

jobs:
  # 代码质量检查
  lint-and-test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: [user-service, product-service, order-service, frontend]
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        if: matrix.service == 'frontend' || matrix.service == 'user-service'
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
          cache-dependency-path: services/${{ matrix.service }}/package-lock.json

      - name: Setup Go
        if: matrix.service != 'frontend' && matrix.service != 'user-service'
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'
          cache: true

      - name: Install dependencies
        working-directory: services/${{ matrix.service }}
        run: |
          if [ "${{ matrix.service }}" == "frontend" ]; then
            npm ci
          elif [ "${{ matrix.service }}" == "user-service" ]; then
            npm ci
          else
            go mod download
          fi

      - name: Run linting
        working-directory: services/${{ matrix.service }}
        run: |
          if [ "${{ matrix.service }}" == "frontend" ]; then
            npm run lint
          elif [ "${{ matrix.service }}" == "user-service" ]; then
            npm run lint
          else
            go vet ./...
          fi

      - name: Run tests
        working-directory: services/${{ matrix.service }}
        run: |
          if [ "${{ matrix.service }}" == "frontend" ]; then
            npm run test:unit
          elif [ "${{ matrix.service }}" == "user-service" ]; then
            npm run test
          else
            go test -v -race -coverprofile=coverage.out ./...
          fi

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: services/${{ matrix.service }}/coverage.out
          flags: ${{ matrix.service }}
          fail_ci_if_error: false

  # 构建镜像
  build-images:
    needs: lint-and-test
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    
    strategy:
      matrix:
        service: [user-service, product-service, order-service, frontend]
    
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: ./services/${{ matrix.service }}
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # 部署到测试环境
  deploy-staging:
    needs: build-images
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment:
      name: staging
      url: https://staging.minijd.com

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2

      - name: Update kubeconfig
        run: aws eks update-kubeconfig --name minijd-staging-cluster --region us-west-2

      - name: Deploy to staging
        run: |
          cd k8s/staging
          kubectl apply -k .
          
          # Wait for rollout
          kubectl rollout status deployment/user-service -n minijd-staging --timeout=300s
          kubectl rollout status deployment/product-service -n minijd-staging --timeout=300s
          kubectl rollout status deployment/order-service -n minijd-staging --timeout=300s

      - name: Run smoke tests
        run: |
          npm install -g newman
          newman run tests/postman/minijd-smoke-tests.json \
            --environment tests/postman/staging-environment.json \
            --reporters cli,htmlextra

  # 部署到生产环境
  deploy-production:
    needs: build-images
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://minijd.com

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2

      - name: Update kubeconfig
        run: aws eks update-kubeconfig --name minijd-production-cluster --region us-west-2

      - name: Create deployment backup
        run: |
          kubectl get all -n minijd-prod -o yaml > backup-$(date +%Y%m%d-%H%M%S).yaml

      - name: Deploy to production
        run: |
          cd k8s/production
          kubectl apply -k .
          
          # Wait for rollout with canary
          echo "Deploying user-service..."
          kubectl rollout restart deployment/user-service -n minijd-prod
          kubectl rollout status deployment/user-service -n minijd-prod --timeout=600s
          
          echo "Deploying product-service..."
          kubectl rollout restart deployment/product-service -n minijd-prod
          kubectl rollout status deployment/product-service -n minijd-prod --timeout=600s
          
          echo "Deploying order-service..."
          kubectl rollout restart deployment/order-service -n minijd-prod
          kubectl rollout status deployment/order-service -n minijd-prod --timeout=600s

      - name: Verify deployment
        run: |
          # Health checks
          curl -f https://api.minijd.com/health
          curl -f https://minijd.com/health

      - name: Run integration tests
        run: |
          npm install -g newman
          newman run tests/postman/minijd-integration-tests.json \
            --environment tests/postman/production-environment.json \
            --reporters cli,htmlextra

      - name: Notify deployment success
        uses: 8398a7/action-slack@v3
        with:
          status: success
          text: '🚀 Production deployment successful!'
          webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}

5.4 Helm Charts部署

# helm/minijd/Chart.yaml
apiVersion: v2
name: minijd
description: Mini JD E-commerce Platform
type: application
version: 1.0.0
appVersion: "1.0.0"

dependencies:
  - name: postgresql
    version: "12.1.2"
    repository: "https://charts.bitnami.com/bitnami"
  - name: redis
    version: "17.3.7"
    repository: "https://charts.bitnami.com/bitnami"
  - name: elasticsearch
    version: "19.0.0"
    repository: "https://helm.elastic.co"
  - name: rabbitmq
    version: "11.2.3"
    repository: "https://charts.bitnami.com/bitnami"
  - name: minio
    version: "13.0.2"
    repository: "https://charts.min.io/"
# helm/minijd/values.yaml
# 全局配置
global:
  environment: production
  region: us-west-2
  domain: minijd.com

# 命名空间
namespace: minijd-prod

# 镜像配置
image:
  registry: ghcr.io
  repository: your-org/minijd
  tag: latest
  pullPolicy: IfNotPresent

# 服务配置
services:
  user:
    replicaCount: 3
    port: 3001
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
    autoscaling:
      enabled: true
      minReplicas: 3
      maxReplicas: 20
      targetCPUUtilization: 70
      targetMemoryUtilization: 80

  product:
    replicaCount: 3
    port: 3002
    resources:
      requests:
        memory: "512Mi"
        cpu: "500m"
      limits:
        memory: "1Gi"
        cpu: "1000m"
    autoscaling:
      enabled: true
      minReplicas: 3
      maxReplicas: 15
      targetCPUUtilization: 70
      targetMemoryUtilization: 80

  order:
    replicaCount: 3
    port: 3003
    resources:
      requests:
        memory: "512Mi"
        cpu: "500m"
      limits:
        memory: "1Gi"
        cpu: "1000m"
    autoscaling:
      enabled: true
      minReplicas: 3
      maxReplicas: 15
      targetCPUUtilization: 70
      targetMemoryUtilization: 80

# 数据库配置
postgresql:
  enabled: true
  auth:
    postgresPassword: "password"
    database: "minijd"
  primary:
    persistence:
      size: 100Gi
      storageClass: "gp3"
  readReplicas:
    replicaCount: 2
  metrics:
    enabled: true

# Redis配置
redis:
  enabled: true
  architecture: replication
  auth:
    password: "password"
  master:
    persistence:
      size: 20Gi
      storageClass: "gp3"
  replica:
    replicaCount: 2
  metrics:
    enabled: true

# Elasticsearch配置
elasticsearch:
  enabled: true
  auth:
    fileRealm:
    - name: admin
      password: "password"
  master:
    replicaCount: 3
    persistence:
      size: 100Gi
      storageClass: "gp3"
  data:
    replicaCount: 3
    persistence:
      size: 200Gi
      storageClass: "gp3"
  ml:
    enabled: false
  metrics:
    enabled: true

# RabbitMQ配置
rabbitmq:
  enabled: true
  auth:
    username: "admin"
    password: "password"
  persistence:
    enabled: true
    size: 50Gi
  replicaCount: 3
  metrics:
    enabled: true

# MinIO配置
minio:
  enabled: true
  auth:
    rootUser: "minijd"
    rootPassword: "minijd123"
  defaultBuckets:
    - minijd-images
    - minijd-documents
  persistence:
    size: 500Gi
    storageClass: "gp3"
  metrics:
    enabled: true

# Ingress配置
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rate-limit: "100"
  hosts:
    - host: minijd.com
      paths:
        - path: /
          pathType: Prefix
          service: frontend
    - host: api.minijd.com
      paths:
        - path: /api
          pathType: Prefix
          service: api-gateway
  tls:
    - secretName: minijd-tls
      hosts:
        - minijd.com
        - api.minijd.com

# 监控配置
monitoring:
  prometheus:
    enabled: true
  grafana:
    enabled: true
    adminPassword: "admin"
  jaeger:
    enabled: true
  loki:
    enabled: true
  alertmanager:
    enabled: true
    receivers:
      - name: "slack"
        slackConfigs:
          - channel: "#alerts"
            sendResolved: true
            title: "{{ template \"slack.minijd.title\" . }}"
            text: "{{ template \"slack.minijd.text\" . }}"

六、性能优化与最佳实践

6.1 数据库性能优化

-- 数据库性能优化脚本
-- 1. 创建必要的索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_orders_user_status_created 
ON orders(user_id, status, created_at DESC);
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_orders_status_created 
ON orders(status, created_at DESC);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_order_items_order_product 
ON order_items(order_id, product_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_category_status_sales 
ON products(category_id, status, sales DESC);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_brand_status 
ON products(brand, status) WHERE status = 'ACTIVE';

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_price_range 
ON products(price) WHERE status = 'ACTIVE';

-- 2. 创建全文搜索索引
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE EXTENSION IF NOT EXISTS pg_trgm;

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_name_gin 
ON products USING gin(to_tsvector('simple', name));

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_name_trgm 
ON products USING gin(name gin_trgm_ops);

-- 3. 分区表配置
-- 按时间分区的订单表
CREATE TABLE orders_partitioned (
    id BIGSERIAL,
    order_no VARCHAR(32) NOT NULL,
    user_id BIGINT NOT NULL,
    status VARCHAR(20) NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    pay_amount DECIMAL(12,2) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL,
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

-- 创建分区
CREATE TABLE orders_y2024 PARTITION OF orders_partitioned
    FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

CREATE TABLE orders_y2025 PARTITION OF orders_partitioned
    FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');

-- 4. 物化视图优化报表查询
CREATE MATERIALIZED VIEW mv_daily_sales AS
SELECT 
    DATE(created_at) as sale_date,
    COUNT(*) as order_count,
    SUM(total_amount) as total_sales,
    SUM(pay_amount) as total_revenue,
    AVG(pay_amount) as avg_order_value,
    COUNT(DISTINCT user_id) as unique_customers
FROM orders
WHERE status IN ('PAID', 'SHIPPED', 'DELIVERED', 'COMPLETED')
GROUP BY DATE(created_at)
ORDER BY sale_date DESC;

-- 创建唯一索引以支持并发刷新
CREATE UNIQUE INDEX idx_mv_daily_sales_date ON mv_daily_sales(sale_date);

-- 定时刷新物化视图
CREATE OR REPLACE FUNCTION refresh_daily_sales()
RETURNS TRIGGER AS $$
BEGIN
    REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_sales;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_refresh_daily_sales
AFTER INSERT OR UPDATE ON orders
FOR EACH STATEMENT
EXECUTE FUNCTION refresh_daily_sales();

-- 5. 查询优化示例
-- 优化前
-- SELECT * FROM products WHERE category_id = 1 AND status = 'ACTIVE' ORDER BY sales DESC LIMIT 20;

-- 优化后 - 使用覆盖索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_covering 
ON products(category_id, status, sales DESC, id, name, price, images);

-- 优化查询
SELECT 
    id, name, price, images, sales
FROM products
WHERE category_id = 1 AND status = 'ACTIVE'
ORDER BY sales DESC
LIMIT 20;

-- 6. 连接池配置优化
-- postgresql.conf 优化
-- shared_buffers = 256MB                    # 系统内存的 25%
-- effective_cache_size = 1GB                # 系统内存的 50%
-- work_mem = 4MB                           # 排序和连接操作的内存
-- maintenance_work_mem = 64MB               # 维护操作的内存
-- max_connections = 200                     # 最大连接数
-- shared_preload_libraries = 'pg_stat_statements'
-- pg_stat_statements.track = all

-- 7. 查询计划分析
EXPLAIN ANALYZE 
SELECT p.*, COUNT(oi.id) as order_count
FROM products p
LEFT JOIN order_items oi ON p.id = oi.product_id
WHERE p.category_id = 1 AND p.status = 'ACTIVE'
GROUP BY p.id
ORDER BY order_count DESC
LIMIT 10;

-- 8. 慢查询监控
CREATE TABLE slow_queries (
    id BIGSERIAL PRIMARY KEY,
    query_text TEXT NOT NULL,
    query_plan TEXT,
    execution_time_ms BIGINT,
    rows_examined BIGINT,
    rows_returned BIGINT,
    user_id BIGINT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_slow_queries_created ON slow_queries(created_at DESC);

-- 自动记录慢查询的触发器函数
CREATE OR REPLACE FUNCTION log_slow_query()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.execution_time > 1000 THEN  -- 超过1秒的查询
        INSERT INTO slow_queries (query_text, query_plan, execution_time_ms, rows_examined, rows_returned)
        VALUES (NEW.query, NEW.query_plan, NEW.execution_time, NEW.rows_examined, NEW.rows_returned);
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

6.2 缓存策略优化

// services/shared/cache/CacheManager.ts
import Redis from 'ioredis';
import { logger } from '../../utils/logger';

interface CacheConfig {
  ttl: number;           // 默认过期时间(秒)
  prefix: string;        // 键前缀
  serialize: boolean;    // 是否序列化
  compress: boolean;     // 是否压缩
}

interface CacheEntry<T> {
  value: T;
  createdAt: number;
  ttl: number;
  version: string;
}

export class CacheManager {
  private redis: Redis;
  private defaultConfig: CacheConfig = {
    ttl: 3600,           // 1小时
    prefix: 'minijd:',
    serialize: true,
    compress: false
  };

  constructor(redisClient: Redis, config?: Partial<CacheConfig>) {
    this.redis = redisClient;
    this.defaultConfig = { ...this.defaultConfig, ...config };
  }

  // 生成缓存键
  private buildKey(key: string): string {
    return `${this.defaultConfig.prefix}${key}`;
  }

  // 序列化数据
  private serialize<T>(value: T): string {
    if (!this.defaultConfig.serialize) {
      return String(value);
    }
    return JSON.stringify(value);
  }

  // 反序列化数据
  private deserialize<T>(data: string): T | null {
    if (!this.defaultConfig.serialize) {
      return data as unknown as T;
    }
    try {
      return JSON.parse(data) as T;
    } catch {
      return null;
    }
  }

  // 设置缓存
  async set<T>(
    key: string,
    value: T,
    ttl?: number,
    options?: {
      nx?: boolean;      // 只在键不存在时设置
      xx?: boolean;      // 只在键存在时设置
      tags?: string[];   // 标签用于批量失效
    }
  ): Promise<boolean> {
    const cacheKey = this.buildKey(key);
    const entry: CacheEntry<T> = {
      value,
      createdAt: Date.now(),
      ttl: ttl || this.defaultConfig.ttl,
      version: '1.0'
    };

    const serialized = this.serialize(entry);

    try {
      const args: any[] = [cacheKey, serialized];
      
      if (ttl) {
        args.push('EX', ttl);
      }

      if (options?.nx) {
        args.push('NX');
      }

      if (options?.xx) {
        args.push('XX');
      }

      const result = await this.redis.set(...args);

      // 处理标签
      if (options?.tags && result === 'OK') {
        await this.addToTags(key, options.tags);
      }

      return result === 'OK';
    } catch (error) {
      logger.error('缓存设置失败:', error);
      return false;
    }
  }

  // 获取缓存
  async get<T>(key: string): Promise<T | null> {
    const cacheKey = this.buildKey(key);

    try {
      const data = await this.redis.get(cacheKey);
      
      if (!data) {
        return null;
      }

      const entry = this.deserialize<CacheEntry<T>>(data);
      
      if (!entry) {
        return null;
      }

      // 检查是否过期
      if (Date.now() - entry.createdAt > entry.ttl * 1000) {
        await this.delete(key);
        return null;
      }

      return entry.value;
    } catch (error) {
      logger.error('缓存获取失败:', error);
      return null;
    }
  }

  // 删除缓存
  async delete(key: string): Promise<boolean> {
    const cacheKey = this.buildKey(key);

    try {
      const result = await this.redis.del(cacheKey);
      return result > 0;
    } catch (error) {
      logger.error('缓存删除失败:', error);
      return false;
    }
  }

  // 批量删除
  async deleteMany(keys: string[]): Promise<number> {
    const cacheKeys = keys.map(k => this.buildKey(k));

    try {
      const result = await this.redis.del(...cacheKeys);
      return result;
    } catch (error) {
      logger.error('批量删除缓存失败:', error);
      return 0;
    }
  }

  // 添加标签
  private async addToTags(key: string, tags: string[]): Promise<void> {
    const pipeline = this.redis.pipeline();

    for (const tag of tags) {
      const tagKey = this.buildKey(`tag:${tag}`);
      pipeline.sadd(tagKey, key);
      pipeline.expire(tagKey, 86400 * 7); // 标签7天过期
    }

    await pipeline.exec();
  }

  // 按标签失效
  async invalidateByTag(tag: string): Promise<number> {
    const tagKey = this.buildKey(`tag:${tag}`);

    try {
      // 获取所有相关的键
      const keys = await this.redis.smembers(tagKey);
      
      if (keys.length === 0) {
        return 0;
      }

      // 删除所有相关键
      const fullKeys = keys.map(k => this.buildKey(k));
      const pipeline = this.redis.pipeline();
      fullKeys.forEach(k => pipeline.del(k));
      pipeline.del(tagKey);

      const results = await pipeline.exec();
      return results?.filter(r => r[1] > 0).length || 0;
    } catch (error) {
      logger.error('按标签失效缓存失败:', error);
      return 0;
    }
  }

  // 设置带自动过期的缓存
  async setWithAutoExtend<T>(
    key: string,
    value: T,
    ttl: number,
    extendOnAccess: boolean = true
  ): Promise<boolean> {
    // 使用Lua脚本实现原子操作
    const luaScript = `
      local key = KEYS[1]
      local value = ARGV[1]
      local ttl = tonumber(ARGV[2])
      local extend = ARGV[3] == "true"
      local currentTtl = redis.call('TTL', key)
      
      if currentTtl > 0 then
        if extend then
          redis.call('EXPIRE', key, ttl)
        end
        return 0
      end
      
      redis.call('SET', key, value, 'EX', ttl)
      return 1
    `;

    const entry: CacheEntry<T> = {
      value,
      createdAt: Date.now(),
      ttl,
      version: '1.0'
    };

    const serialized = this.serialize(entry);

    try {
      const result = await this.redis.eval(
        luaScript,
        1,
        this.buildKey(key),
        serialized,
        ttl.toString(),
        extendOnAccess.toString()
      );

      return result === 1;
    } catch (error) {
      logger.error('设置自动扩展缓存失败:', error);
      return false;
    }
  }

  // 缓存预热
  async warmUp(
    loader: () => Promise<Record<string, any>>,
    keyGenerator: (item: any) => string,
    ttl?: number
  ): Promise<number> {
    try {
      const data = await loader();
      const pipeline = this.redis.pipeline();

      let count = 0;
      for (const [id, value] of Object.entries(data)) {
        const key = keyGenerator(value);
        const entry: CacheEntry<typeof value> = {
          value,
          createdAt: Date.now(),
          ttl: ttl || this.defaultConfig.ttl,
          version: '1.0'
        };
        pipeline.set(this.buildKey(key), this.serialize(entry), 'EX', ttl || this.defaultConfig.ttl);
        count++;
      }

      await pipeline.exec();
      logger.info(`缓存预热完成: ${count} 条记录`);
      return count;
    } catch (error) {
      logger.error('缓存预热失败:', error);
      return 0;
    }
  }

  // 获取或设置缓存(缓存穿透保护)
  async getOrSet<T>(
    key: string,
    loader: () => Promise<T>,
    ttl?: number,
    options?: {
      lockTimeout?: number;    // 锁超时时间
      lockRetryInterval?: number; // 锁重试间隔
      tags?: string[];
    }
  ): Promise<T | null> {
    // 先尝试获取缓存
    const cached = await this.get<T>(key);
    if (cached !== null) {
      return cached;
    }

    // 缓存穿透保护:使用互斥锁
    const lockKey = this.buildKey(`lock:${key}`);
    const lockValue = uuidv4();
    const lockTimeout = options?.lockTimeout || 10; // 10秒锁超时

    try {
      // 尝试获取锁
      const locked = await this.redis.set(
        lockKey,
        lockValue,
        'EX',
        lockTimeout,
        'NX'
      );

      if (!locked) {
        // 获取锁失败,等待一段时间后重试
        await new Promise(resolve => setTimeout(resolve, options?.lockRetryInterval || 100));
        return this.get<T>(key); // 再次尝试获取缓存
      }

      // 双重检查缓存
      const doubleChecked = await this.get<T>(key);
      if (doubleChecked !== null) {
        await this.releaseLock(lockKey, lockValue);
        return doubleChecked;
      }

      // 加载数据
      const value = await loader();

      // 设置缓存
      await this.set(key, value, ttl, { tags: options?.tags });

      // 释放锁
      await this.releaseLock(lockKey, lockValue);

      return value;
    } catch (error) {
      logger.error('获取或设置缓存失败:', error);
      // 发生错误时直接加载数据,保证业务可用性
      return loader();
    }
  }

  // 释放锁
  private async releaseLock(lockKey: string, lockValue: string): Promise<void> {
    const luaScript = `
      if redis.call('GET', KEYS[1]) == ARGV[1] then
        return redis.call('DEL', KEYS[1])
      else
        return 0
      end
    `;

    await this.redis.eval(luaScript, 1, lockKey, lockValue);
  }

  // 批量获取缓存
  async mget<T>(keys: string[]): Promise<Map<string, T | null>> {
    const cacheKeys = keys.map(k => this.buildKey(k));

    try {
      const results = await this.redis.mget(...cacheKeys);
      const map = new Map<string, T | null>();

      results.forEach((data, index) => {
        if (data) {
          const entry = this.deserialize<CacheEntry<T>>(data);
          if (entry && Date.now() - entry.createdAt <= entry.ttl * 1000) {
            map.set(keys[index], entry.value);
          } else {
            map.set(keys[index], null);
          }
        } else {
          map.set(keys[index], null);
        }
      });

      return map;
    } catch (error) {
      logger.error('批量获取缓存失败:', error);
      return new Map();
    }
  }

  // 批量设置缓存
  async mset<T>(
    entries: Record<string, T>,
    ttl?: number,
    tags?: string[]
  ): Promise<boolean> {
    const pipeline = this.redis.pipeline();

    for (const [key, value] of Object.entries(entries)) {
      const cacheKey = this.buildKey(key);
      const entry: CacheEntry<T> = {
        value,
        createdAt: Date.now(),
        ttl: ttl || this.defaultConfig.ttl,
        version: '1.0'
      };
      pipeline.set(cacheKey, this.serialize(entry), 'EX', ttl || this.defaultConfig.ttl);
    }

    try {
      await pipeline.exec();

      // 处理标签
      if (tags) {
        for (const key of Object.keys(entries)) {
          await this.addToTags(key, tags);
        }
      }

      return true;
    } catch (error) {
      logger.error('批量设置缓存失败:', error);
      return false;
    }
  }

  // 递增计数器
  async increment(key: string, delta: number = 1): Promise<number> {
    const cacheKey = this.buildKey(key);

    try {
      const result = await this.redis.incrby(cacheKey, delta);
      return result;
    } catch (error) {
      logger.error('递增计数器失败:', error);
      return 0;
    }
  }

  // 获取缓存统计
  async getStats(): Promise<{
    totalKeys: number;
    memoryUsage: number;
    hitRate: number;
  }> {
    try {
      const info = await this.redis.info('memory');
      const keyspace = await this.redis.info('keyspace');
      
      // 解析内存使用
      const memoryMatch = info.match(/used_memory:(\d+)/);
      const memoryUsage = memoryMatch ? parseInt(memoryMatch[1]) : 0;

      // 解析键数量
      const keysMatch = keyspace.match(/db0:keys=(\d+)/);
      const totalKeys = keysMatch ? parseInt(keysMatch[1]) : 0;

      return {
        totalKeys,
        memoryUsage,
        hitRate: 0 // 需要额外的统计
      };
    } catch (error) {
      logger.error('获取缓存统计失败:', error);
      return { totalKeys: 0, memoryUsage: 0, hitRate: 0 };
    }
  }
}

6.3 前端性能优化

// frontend/lib/performance/ImageOptimizer.ts
import { useEffect, useState, useCallback, useRef } from 'react';

interface ImageOptimizationConfig {
  width?: number;
  height?: number;
  quality?: number;
  format?: 'auto' | 'webp' | 'avif' | 'jpg' | 'png';
  fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
  priority?: 'high' | 'low' | 'auto';
  placeholder?: 'blur' | 'empty' | 'skeleton';
}

interface OptimizedImageProps {
  src: string;
  alt: string;
  config?: ImageOptimizationConfig;
  className?: string;
  onLoad?: () => void;
  onError?: (error: Error) => void;
  lazy?: boolean;
  threshold?: number;
}

// 检测浏览器支持的图片格式
const detectSupportedFormats = (): Promise<{
  webp: boolean;
  avif: boolean;
  jxl: boolean;
}> => {
  return new Promise((resolve) => {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    if (!ctx) {
      resolve({ webp: false, avif: false, jxl: false });
      return;
    }

    const checkFormat = (format: string): Promise<boolean> => {
      return new Promise((res) => {
        const dataUrl = canvas.toDataURL(`image/${format}`);
        res(dataUrl.indexOf(`data:image/${format}`) === 0);
      });
    };

    Promise.all([
      checkFormat('webp'),
      checkFormat('avif'),
      checkFormat('jxl')
    ]).then(([webp, avif, jxl]) => {
      resolve({ webp, avif, jxl });
    });
  });
};

// 生成优化的图片URL
const generateOptimizedUrl = (
  originalUrl: string,
  config: ImageOptimizationConfig
): string => {
  const { width, height, quality = 80, format = 'auto', fit = 'cover' } = config;

  // 如果已经是CDN URL,添加优化参数
  if (originalUrl.includes('cdn.minijd.com') || originalUrl.includes('image-process')) {
    const params = new URLSearchParams();
    params.set('url', encodeURIComponent(originalUrl));
    
    if (width) params.set('w', width.toString());
    if (height) params.set('h', height.toString());
    params.set('q', quality.toString());
    params.set('fmt', format);
    params.set('fit', fit);

    return `https://cdn.minijd.com/image-process?${params.toString()}`;
  }

  // 否则返回原始URL
  return originalUrl;
};

// 预加载关键图片
const preloadCriticalImages = (urls: string[]): void => {
  urls.slice(0, 6).forEach((url) => {
    const link = document.createElement('link');
    link.rel = 'preload';
    link.as = 'image';
    link.href = url;
    link.setAttribute('fetchpriority', 'high');
    document.head.appendChild(link);
  });
};

// 图片优化Hook
export const useImageOptimization = (props: OptimizedImageProps) => {
  const {
    src,
    alt,
    config = {},
    lazy = true,
    threshold = 0.1,
    onLoad,
    onError
  } = props;

  const [loadedSrc, setLoadedSrc] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isInView, setIsInView] = useState(!lazy);
  const [supportedFormats, setSupportedFormats] = useState({
    webp: false,
    avif: false,
    jxl: false
  });

  const imgRef = useRef<HTMLDivElement>(null);
  const observerRef = useRef<IntersectionObserver | null>(null);

  // 检测支持的格式
  useEffect(() => {
    detectSupportedFormats().then(setSupportedFormats);
  }, []);

  // 确定最佳格式
  const getBestFormat = useCallback(() => {
    if (config.format === 'auto') {
      if (supportedFormats.avif) return 'avif';
      if (supportedFormats.webp) return 'webp';
      return 'jpg';
    }
    return config.format;
  }, [config.format, supportedFormats]);

  // 生成优化后的URL
  const optimizedSrc = useMemo(() => {
    const format = getBestFormat();
    return generateOptimizedUrl(src, { ...config, format });
  }, [src, config, getBestFormat]);

  // 懒加载观察器
  useEffect(() => {
    if (!lazy || isInView) return;

    observerRef.current = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observerRef.current?.disconnect();
        }
      },
      { threshold, rootMargin: '50px' }
    );

    if (imgRef.current) {
      observerRef.current.observe(imgRef.current);
    }

    return () => {
      observerRef.current?.disconnect();
    };
  }, [lazy, threshold, isInView]);

  // 加载图片
  useEffect(() => {
    if (!isInView || loadedSrc) return;

    setIsLoading(true);
    setError(null);

    const img = new Image();
    img.onload = () => {
      setLoadedSrc(optimizedSrc);
      setIsLoading(false);
      onLoad?.();
    };

    img.onerror = () => {
      setError(new Error(`Failed to load image: ${optimizedSrc}`));
      setIsLoading(false);
      onError?.(new Error(`Failed to load image: ${optimizedSrc}`));
    };

    img.src = optimizedSrc;

    return () => {
      img.onload = null;
      img.onerror = null;
    };
  }, [isInView, optimizedSrc, loadedSrc, onLoad, onError]);

  return {
    loadedSrc,
    isLoading,
    error,
    isInView,
    imgRef,
    supportedFormats
  };
};

// 优化的图片组件
export const OptimizedImage: React.FC<OptimizedImageProps> = (props) => {
  const {
    src,
    alt,
    config = {},
    className = '',
    lazy = true,
    threshold = 0.1,
    placeholder = 'blur'
  } = props;

  const { loadedSrc, isLoading, error, isInView, imgRef } = useImageOptimization({
    src,
    alt,
    config,
    lazy,
    threshold,
    onLoad: props.onLoad,
    onError: props.onError
  });

  // 占位符组件
  const Placeholder = () => {
    if (placeholder === 'skeleton') {
      return (
        <div 
          className={`animate-pulse bg-gray-200 rounded ${className}`}
          style={{ 
            width: config.width || '100%', 
            height: config.height || '200px' 
          }}
        />
      );
    }
    
    if (placeholder === 'blur' && config.width && config.height) {
      return (
        <div 
          className={`bg-gray-100 ${className}`}
          style={{ 
            width: config.width, 
            height: config.height,
            backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${config.width} ${config.height}'%3E%3Crect fill='%23f0f0f0' width='100%25' height='100%25'/%3E%3C/svg%3E")`,
            backgroundSize: 'cover'
          }}
        />
      );
    }

    return <div className={`bg-transparent ${className}`} />;
  };

  return (
    <div ref={imgRef} className="relative overflow-hidden">
      {!isInView ? (
        <Placeholder />
      ) : isLoading ? (
        <Placeholder />
      ) : error ? (
        <div className={`flex items-center justify-center bg-gray-100 text-gray-400 ${className}`}>
          <span>图片加载失败</span>
        </div>
      ) : (
        <img
          src={loadedSrc || src}
          alt={alt}
          className={`w-full h-full object-cover transition-opacity duration-300 ${className}`}
          loading={lazy ? 'lazy' : 'eager'}
          decoding="async"
        />
      )}
    </div>
  );
};

// 响应式图片组件
export const ResponsiveImage: React.FC<OptimizedImageProps & {
  sizes?: string;
  breakpoints?: { width: number; quality: number }[];
}> = (props) => {
  const {
    src,
    alt,
    config = {},
    breakpoints = [
      { width: 320, quality: 75 },
      { width: 640, quality: 80 },
      { width: 960, quality: 85 },
      { width: 1280, quality: 90 }
    ],
    sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw'
  } = props;

  const srcSet = breakpoints
    .map(({ width, quality }) => {
      const optimizedUrl = generateOptimizedUrl(src, {
        ...config,
        width,
        quality
      });
      return `${optimizedUrl} ${width}w`;
    })
    .join(', ');

  return (
    <OptimizedImage
      {...props}
      src={src}
      srcSet={srcSet}
      sizes={sizes}
      loading="lazy"
    />
  );
};

// 图片预加载Hook
export const useImagePreloader = (imageUrls: string[]) => {
  useEffect(() => {
    preloadCriticalImages(imageUrls);
  }, [imageUrls]);
};

// 导出工具函数
export { generateOptimizedUrl, detectSupportedFormats, preloadCriticalImages };

6.4 微服务通信优化

// services/shared/messaging/MessageQueue.ts
import amqp from 'amqplib';
import { logger } from '../../utils/logger';
import { EventEmitter } from 'events';

interface QueueConfig {
  url: string;
  exchange: string;
  queue: string;
  routingKey: string;
  prefetch: number;
  durable: boolean;
  autoDelete: boolean;
}

interface MessageHandler {
  (message: any, ack: () => void, nack: () => void): Promise<void>;
}

export class MessageQueue extends EventEmitter {
  private connection: amqp.Connection | null = null;
  private channel: amqp.Channel | null = null;
  private config: QueueConfig;
  private handlers: Map<string, MessageHandler[]> = new Map();
  private isConnected = false;

  constructor(config: QueueConfig) {
    super();
    this.config = config;
  }

  // 连接到RabbitMQ
  async connect(): Promise<void> {
    try {
      this.connection = await amqp.connect(this.config.url);
      this.channel = await this.connection.createChannel();

      // 设置预取数量
      await this.channel.prefetch(this.config.prefetch);

      // 声明交换机
      await this.channel.assertExchange(this.config.exchange, 'topic', {
        durable: true
      });

      // 声明队列
      await this.channel.assertQueue(this.config.queue, {
        durable: this.config.durable,
        autoDelete: this.config.autoDelete
      });

      // 绑定路由键
      await this.channel.bindQueue(
        this.config.queue,
        this.config.exchange,
        this.config.routingKey
      );

      this.isConnected = true;
      logger.info('Message queue connected successfully');

      // 监听连接关闭
      this.connection.on('close', () => {
        this.isConnected = false;
        logger.warn('Message queue connection closed');
        this.emit('disconnected');
      });

      // 监听错误
      this.connection.on('error', (error) => {
        logger.error('Message queue connection error:', error);
        this.emit('error', error);
      });

    } catch (error) {
      logger.error('Failed to connect to message queue:', error);
      throw error;
    }
  }

  // 发布消息
  async publish(
    routingKey: string,
    message: any,
    options: {
      persistent?: boolean;
      priority?: number;
      ttl?: number;
      correlationId?: string;
      replyTo?: string;
    } = {}
  ): Promise<boolean> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    try {
      const messageBuffer = Buffer.from(JSON.stringify(message));

      const publishOptions: amqp.Options.Publish = {
        persistent: options.persistent ?? true,
        priority: options.priority,
        expiration: options.ttl?.toString(),
        correlationId: options.correlationId,
        replyTo: options.replyTo,
        timestamp: Date.now(),
        appId: 'minijd-service'
      };

      const result = this.channel.publish(
        this.config.exchange,
        routingKey,
        messageBuffer,
        publishOptions
      );

      if (!result) {
        logger.warn('Message was not published (backpressure)');
      }

      return result;
    } catch (error) {
      logger.error('Failed to publish message:', error);
      throw error;
    }
  }

  // 订阅消息
  async subscribe(
    routingKey: string,
    handler: MessageHandler,
    options: {
      queue?: string;
      exclusive?: boolean;
      noAck?: boolean;
    } = {}
  ): Promise<void> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    const queue = options.queue || this.config.queue;

    // 声明消费者队列
    await this.channel.assertQueue(queue, {
      durable: true,
      exclusive: options.exclusive
    });

    // 绑定路由键
    await this.channel.bindQueue(queue, this.config.exchange, routingKey);

    // 消费消息
    await this.channel.consume(
      queue,
      async (msg) => {
        if (!msg) return;

        try {
          const message = JSON.parse(msg.content.toString());
          const correlationId = msg.properties.correlationId;
          const replyTo = msg.properties.replyTo;

          logger.debug(`Received message: ${routingKey}`, { correlationId });

          await handler(
            { ...message, correlationId, replyTo },
            () => {
              this.channel?.ack(msg);
            },
            () => {
              this.channel?.nack(msg, false, false);
            }
          );
        } catch (error) {
          logger.error('Error processing message:', error);
          this.channel?.nack(msg, false, false);
        }
      },
      { noAck: options.noAck ?? false }
    );

    // 存储处理器
    if (!this.handlers.has(routingKey)) {
      this.handlers.set(routingKey, []);
    }
    this.handlers.get(routingKey)?.push(handler);

    logger.info(`Subscribed to: ${routingKey}`);
  }

  // 请求-响应模式
  async request<T>(
    routingKey: string,
    message: any,
    timeout: number = 30000
  ): Promise<T> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    const correlationId = uuidv4();
    const replyQueue = `reply.${correlationId}`;

    return new Promise((resolve, reject) => {
      // 创建临时回复队列
      this.channel!.assertQueue(replyQueue, {
        exclusive: true,
        autoDelete: true
      }).then(() => {
        // 设置超时
        const timeoutId = setTimeout(() => {
          this.channel?.cancel(consumerTag!);
          this.channel?.deleteQueue(replyQueue);
          reject(new Error('Request timeout'));
        }, timeout);

        // 消费回复
        this.channel!.consume(
          replyQueue,
          (msg) => {
            if (!msg) return;

            if (msg.properties.correlationId === correlationId) {
              clearTimeout(timeoutId);
              this.channel?.cancel(consumerTag!);
              this.channel?.deleteQueue(replyQueue);

              const response = JSON.parse(msg.content.toString());
              if (response.error) {
                reject(new Error(response.error));
              } else {
                resolve(response.data);
              }
            }
          },
          { noAck: true }
        ).then((ok) => {
          consumerTag = ok.consumerTag;
        });

        // 发送请求
        this.publish(routingKey, message, {
          correlationId,
          replyTo: replyQueue,
          persistent: true
        });
      });
    });
  }

  // RPC模式
  async rpc<T>(
    routingKey: string,
    message: any,
    timeout: number = 30000
  ): Promise<T> {
    return this.request<T>(routingKey, message, timeout);
  }

  // 批量发布
  async publishBatch(
    messages: Array<{
      routingKey: string;
      message: any;
      options?: any;
    }>
  ): Promise<void> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    const batchSize = 100;
    const batches = [];

    for (let i = 0; i < messages.length; i += batchSize) {
      batches.push(messages.slice(i, i + batchSize));
    }

    for (const batch of batches) {
      const operations = batch.map(({ routingKey, message, options }) => {
        const messageBuffer = Buffer.from(JSON.stringify(message));
        return this.channel!.publish(
          this.config.exchange,
          routingKey,
          messageBuffer,
          { ...options, persistent: true }
        );
      });

      await Promise.all(operations);
    }

    logger.info(`Published ${messages.length} messages in ${batches.length} batches`);
  }

  // 确认模式
  async enableConfirmMode(): Promise<void> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    await this.channel.confirmSelect();
    logger.info('Publisher confirms enabled');
  }

  // 等待确认
  async waitForConfirms(timeout: number = 10000): Promise<boolean> {
    if (!this.channel) {
      throw new Error('Message queue not connected');
    }

    try {
      await this.channel.waitForConfirms(timeout);
      return true;
    } catch (error) {
      logger.error('Confirmation timeout:', error);
      return false;
    }
  }

  // 断开连接
  async disconnect(): Promise<void> {
    try {
      if (this.channel) {
        await this.channel.close();
      }
      if (this.connection) {
        await this.connection.close();
      }
      this.isConnected = false;
      logger.info('Message queue disconnected');
    } catch (error) {
      logger.error('Error disconnecting from message queue:', error);
    }
  }

  // 获取连接状态
  getConnectionStatus(): boolean {
    return this.isConnected;
  }

  // 健康检查
  async healthCheck(): Promise<{
    connected: boolean;
    uptime: number;
    pendingMessages: number;
  }> {
    const startTime = Date.now();

    try {
      if (!this.connection || !this.channel) {
        return {
          connected: false,
          uptime: 0,
          pendingMessages: 0
        };
      }

      // 检查通道状态
      await this.channel.checkQueue(this.config.queue);

      return {
        connected: true,
        uptime: Date.now() - startTime,
        pendingMessages: 0 // 可以添加更详细的统计
      };
    } catch (error) {
      return {
        connected: false,
        uptime: 0,
        pendingMessages: 0
      };
    }
  }
}

// 导出单例实例
let messageQueueInstance: MessageQueue | null = null;

export const getMessageQueue = (config?: QueueConfig): MessageQueue => {
  if (!messageQueueInstance && config) {
    messageQueueInstance = new MessageQueue(config);
  }
  return messageQueueInstance!;
};

// UUID生成工具
function uuidv4(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

七、安全最佳实践

7.1 身份认证与授权

// services/shared/security/AuthService.ts
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import { v4 as uuidv4 } from 'uuid';
import { Redis } from 'ioredis';
import { logger } from '../../utils/logger';

interface TokenPayload {
  userId: number;
  email: string;
  role: string;
  sessionId: string;
  type: 'access' | 'refresh';
}

interface AuthConfig {
  jwtSecret: string;
  jwtRefreshSecret: string;
  accessTokenExpiry: string;
  refreshTokenExpiry: string;
  redisUrl: string;
}

export class AuthService {
  private redis: Redis;
  private config: AuthConfig;

  constructor(config: AuthConfig) {
    this.config = config;
    this.redis = new Redis(config.redisUrl);
  }

  // 哈希密码
  async hashPassword(password: string): Promise<string> {
    const saltRounds = 12;
    return bcrypt.hash(password, saltRounds);
  }

  // 验证密码
  async verifyPassword(password: string, hash: string): Promise<boolean> {
    return bcrypt.compare(password, hash);
  }

  // 生成访问令牌
  generateAccessToken(payload: Omit<TokenPayload, 'type'>): string {
    return jwt.sign(
      { ...payload, type: 'access' },
      this.config.jwtSecret,
      { expiresIn: this.config.accessTokenExpiry }
    );
  }

  // 生成刷新令牌
  generateRefreshToken(payload: Omit<TokenPayload, 'type'>): string {
    return jwt.sign(
      { ...payload, type: 'refresh' },
      this.config.jwtRefreshSecret,
      { expiresIn: this.config.refreshTokenExpiry }
    );
  }

  // 验证访问令牌
  verifyAccessToken(token: string): TokenPayload | null {
    try {
      const decoded = jwt.verify(token, this.config.jwtSecret) as TokenPayload;
      
      if (decoded.type !== 'access') {
        throw new Error('Invalid token type');
      }

      return decoded;
    } catch (error) {
      logger.warn('Access token verification failed:', error);
      return null;
    }
  }

  // 验证刷新令牌
  verifyRefreshToken(token: string): TokenPayload | null {
    try {
      const decoded = jwt.verify(token, this.config.jwtRefreshSecret) as TokenPayload;
      
      if (decoded.type !== 'refresh') {
        throw new Error('Invalid token type');
      }

      return decoded;
    } catch (error) {
      logger.warn('Refresh token verification failed:', error);
      return null;
    }
  }

  // 创建会话
  async createSession(
    userId: number,
    metadata: {
      ip: string;
      userAgent: string;
      deviceId?: string;
    }
  ): Promise<{
    sessionId: string;
    accessToken: string;
    refreshToken: string;
  }> {
    const sessionId = uuidv4();
    const payload = {
      userId,
      email: '', // 需要从数据库获取
      role: '', // 需要从数据库获取
      sessionId
    };

    const accessToken = this.generateAccessToken(payload);
    const refreshToken = this.generateRefreshToken(payload);

    // 存储会话信息到Redis
    const sessionData = {
      userId,
      refreshToken,
      ip: metadata.ip,
      userAgent: metadata.userAgent,
      deviceId: metadata.deviceId,
      createdAt: new Date().toISOString(),
      lastActivityAt: new Date().toISOString()
    };

    await this.redis.setex(
      `session:${sessionId}`,
      7 * 24 * 60 * 60, // 7天
      JSON.stringify(sessionData)
    );

    // 添加到用户会话列表
    await this.redis.sadd(`user:sessions:${userId}`, sessionId);

    // 存储令牌黑名单检查用的密钥
    await this.redis.setex(
      `token:blacklist:${accessToken}`,
      15 * 60, // 15分钟(访问令牌有效期)
      '1'
    );

    return {
      sessionId,
      accessToken,
      refreshToken
    };
  }

  // 验证会话
  async validateSession(sessionId: string): Promise<boolean> {
    try {
      const sessionData = await this.redis.get(`session:${sessionId}`);
      return !!sessionData;
    } catch (error) {
      logger.error('Session validation failed:', error);
      return false;
    }
  }

  // 刷新令牌
  async refreshTokens(
    refreshToken: string
  ): Promise<{
    accessToken: string;
    refreshToken: string;
    sessionId: string;
  } | null> {
    const decoded = this.verifyRefreshToken(refreshToken);
    
    if (!decoded) {
      return null;
    }

    // 验证会话是否存在且有效
    const sessionValid = await this.validateSession(decoded.sessionId);
    if (!sessionValid) {
      logger.warn(`Invalid session during token refresh: ${decoded.sessionId}`);
      return null;
    }

    // 获取最新的用户信息
    const userInfo = await this.getUserInfo(decoded.userId);
    if (!userInfo) {
      return null;
    }

    // 撤销旧的刷新令牌
    await this.revokeRefreshToken(refreshToken);

    // 创建新的令牌
    const newPayload = {
      userId: userInfo.id,
      email: userInfo.email,
      role: userInfo.role,
      sessionId: decoded.sessionId
    };

    const newAccessToken = this.generateAccessToken(newPayload);
    const newRefreshToken = this.generateRefreshToken(newPayload);

    // 更新会话中的刷新令牌
    await this.updateSessionToken(decoded.sessionId, newRefreshToken);

    // 添加到黑名单
    await this.redis.setex(
      `token:blacklist:${newAccessToken}`,
      15 * 60,
      '1'
    );

    return {
      accessToken: newAccessToken,
      refreshToken: newRefreshToken,
      sessionId: decoded.sessionId
    };
  }

  // 撤销会话
  async revokeSession(sessionId: string): Promise<void> {
    try {
      const sessionData = await this.redis.get(`session:${sessionId}`);
      
      if (sessionData) {
        const session = JSON.parse(sessionData);
        
        // 从用户会话列表中移除
        await this.redis.srem(`user:sessions:${session.userId}`, sessionId);
      }

      // 删除会话
      await this.redis.del(`session:${sessionId}`);
      
      logger.info(`Session revoked: ${sessionId}`);
    } catch (error) {
      logger.error('Error revoking session:', error);
    }
  }

  // 撤销所有用户会话
  async revokeAllUserSessions(userId: number): Promise<void> {
    try {
      const sessionIds = await this.redis.smembers(`user:sessions:${userId}`);
      
      const pipeline = this.redis.pipeline();
      sessionIds.forEach(sessionId => {
        pipeline.del(`session:${sessionId}`);
      });
      pipeline.del(`user:sessions:${userId}`);

      await pipeline.exec();
      logger.info(`All sessions revoked for user: ${userId}`);
    } catch (error) {
      logger.error('Error revoking all user sessions:', error);
    }
  }

  // 撤销刷新令牌
  private async revokeRefreshToken(refreshToken: string): Promise<void> {
    const decoded = this.verifyRefreshToken(refreshToken);
    if (decoded) {
      await this.redis.del(`session:${decoded.sessionId}`);
    }
  }

  // 更新会话令牌
  private async updateSessionToken(sessionId: string, newRefreshToken: string): Promise<void> {
    const sessionData = await this.redis.get(`session:${sessionId}`);
    if (sessionData) {
      const session = JSON.parse(sessionData);
      session.refreshToken = newRefreshToken;
      session.lastActivityAt = new Date().toISOString();
      
      await this.redis.setex(
        `session:${sessionId}`,
        7 * 24 * 60 * 60,
        JSON.stringify(session)
      );
    }
  }

  // 获取用户信息(模拟)
  private async getUserInfo(userId: number): Promise<{ id: number; email: string; role: string } | null> {
    // 实际应该从数据库获取
    return { id: userId, email: 'user@example.com', role: 'USER' };
  }

  // 检查令牌是否在黑名单中
  async isTokenBlacklisted(token: string): Promise<boolean> {
    const result = await this.redis.get(`token:blacklist:${token}`);
    return result !== null;
  }

  // 将令牌加入黑名单
  async blacklistToken(token: string, expirySeconds: number): Promise<void> {
    await this.redis.setex(
      `token:blacklist:${token}`,
      expirySeconds,
      '1'
    );
  }

  // 清理过期会话
  async cleanupExpiredSessions(): Promise<number> {
    // 实际实现需要更复杂的逻辑
    return 0;
  }

  // 获取用户活动会话
  async getUserActiveSessions(userId: number): Promise<any[]> {
    const sessionIds = await this.redis.smembers(`user:sessions:${userId}`);
    const sessions = [];

    for (const sessionId of sessionIds) {
      const sessionData = await this.redis.get(`session:${sessionId}`);
      if (sessionData) {
        sessions.push(JSON.parse(sessionData));
      }
    }

    return sessions;
  }

  // 关闭连接
  async close(): Promise<void> {
    await this.redis.quit();
  }
}

// RBAC权限管理
export class RBACService {
  private roles: Map<string, Set<string>> = new Map();
  private permissions: Map<string, Set<string>> = new Map();

  constructor() {
    this.initializeRoles();
  }

  private initializeRoles(): void {
    // 定义角色权限
    this.permissions.set('VIEW_PRODUCTS', new Set(['USER', 'SELLER', 'ADMIN']));
    this.permissions.set('CREATE_PRODUCT', new Set(['SELLER', 'ADMIN']));
    this.permissions.set('EDIT_PRODUCT', new Set(['SELLER', 'ADMIN']));
    this.permissions.set('DELETE_PRODUCT', new Set(['ADMIN']));
    this.permissions.set('VIEW_ORDERS', new Set(['USER', 'SELLER', 'ADMIN']));
    this.permissions.set('CREATE_ORDER', new Set(['USER', 'ADMIN']));
    this.permissions.set('MANAGE_ORDER', new Set(['SELLER', 'ADMIN']));
    this.permissions.set('VIEW_USERS', new Set(['ADMIN']));
    this.permissions.set('MANAGE_USERS', new Set(['ADMIN']));
    this.permissions.set('MANAGE_SYSTEM', new Set(['ADMIN']));
  }

  // 检查权限
  hasPermission(role: string, permission: string): boolean {
    const allowedRoles = this.permissions.get(permission);
    return allowedRoles ? allowedRoles.has(role) : false;
  }

  // 检查多个权限(任一)
  hasAnyPermission(role: string, permissions: string[]): boolean {
    return permissions.some(permission => this.hasPermission(role, permission));
  }

  // 检查多个权限(全部)
  hasAllPermissions(role: string, permissions: string[]): boolean {
    return permissions.every(permission => this.hasPermission(role, permission));
  }

  // 添加角色权限
  addRolePermission(role: string, permission: string): void {
    if (!this.permissions.has(permission)) {
      this.permissions.set(permission, new Set());
    }
    this.permissions.get(permission)!.add(role);
  }

  // 移除角色权限
  removeRolePermission(role: string, permission: string): void {
    const allowedRoles = this.permissions.get(permission);
    if (allowedRoles) {
      allowedRoles.delete(role);
    }
  }

  // 获取角色所有权限
  getRolePermissions(role: string): string[] {
    const rolePermissions: string[] = [];
    
    for (const [permission, roles] of this.permissions) {
      if (roles.has(role)) {
        rolePermissions.push(permission);
      }
    }

    return rolePermissions;
  }
}

7.2 输入验证与安全

// services/shared/security/ValidationService.ts
import { z } from 'zod';
import { logger } from '../../utils/logger';

// 用户注册验证
export const registerSchema = z.object({
  email: z
    .string()
    .email('邮箱格式不正确')
    .max(100, '邮箱长度不能超过100字符')
    .toLowerCase()
    .refine(
      (email) => !email.endsWith('.exe') && !email.endsWith('.sh'),
      '邮箱格式不安全'
    ),
  password: z
    .string()
    .min(8, '密码至少8位')
    .max(128, '密码长度不能超过128位')
    .regex(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
      '密码必须包含大小写字母、数字和特殊字符'
    )
    .refine(
      (password) => !/(.)\1{2,}/.test(password),
      '密码不能包含连续重复的字符'
    ),
  username: z
    .string()
    .min(2, '用户名至少2个字符')
    .max(20, '用户名最多20个字符')
    .regex(
      /^[a-zA-Z0-9_\u4e00-\u9fa5]+$/,
      '用户名只能包含字母、数字、下划线和中文'
    )
    .refine(
      (username) => !/(admin|root|system|administrator)/i.test(username),
      '用户名不能使用敏感词汇'
    ),
  phone: z
    .string()
    .regex(/^1[3-9]\d{9}$/, '手机号格式不正确')
    .optional()
    .or(z.literal(''))
});

// 登录验证
export const loginSchema = z.object({
  email: z.string().email('邮箱格式不正确'),
  password: z.string().min(1, '密码不能为空'),
  captcha: z.string().optional()
});

// 商品创建验证
export const productCreateSchema = z.object({
  name: z
    .string()
    .min(1, '商品名称不能为空')
    .max(200, '商品名称不能超过200字符')
    .refine(
      (name) => !/<script|javascript:|on\w+\s*=/i.test(name),
      '商品名称包含不安全内容'
    ),
  description: z
    .string()
    .max(5000, '商品描述不能超过5000字符')
    .refine(
      (desc) => !/<script|javascript:|on\w+\s*=/i.test(desc),
      '商品描述包含不安全内容'
    )
    .optional(),
  price: z
    .number()
    .positive('价格必须大于0')
    .max(99999999.99, '价格超出范围')
    .refine(
      (price) => Number.isFinite(price) && !Number.isNaN(price),
      '价格格式无效'
    ),
  originalPrice: z
    .number()
    .positive('原价必须大于0')
    .max(99999999.99, '原价超出范围')
    .optional(),
  categoryId: z.number().int().positive('请选择商品分类'),
  brand: z.string().max(100, '品牌名称过长').optional(),
  stock: z
    .number()
    .int()
    .min(0, '库存不能为负数')
    .max(999999, '库存超出范围'),
  images: z
    .array(z.string().url('图片URL格式不正确'))
    .max(20, '最多上传20张图片')
    .optional()
});

// 订单创建验证
export const orderCreateSchema = z.object({
  addressId: z.number().int().positive('请选择收货地址'),
  cartItemIds: z
    .array(z.number().int().positive())
    .min(1, '请选择要购买的商品')
    .max(50, '一次最多购买50件商品'),
  note: z
    .string()
    .max(500, '备注不能超过500字符')
    .optional(),
  couponId: z.number().int().positive().optional(),
  pointsToUse: z
    .number()
    .int()
    .min(0, '使用积分数不能为负')
    .max(100000, '使用积分数超出限制')
    .optional()
});

// SQL注入防护
export class SQLInjectionGuard {
  private static dangerousPatterns = [
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE|EXEC|UNION|OR|AND)\b)/gi,
    /(--|\/\*|\*\/|;)/g,
    /(\b(WAITFOR|DELAY|BENCHMARK|SLEEP)\b)/gi,
    /(\b(LOAD_FILE|INTO\s+OUTFILE|DUMPFILE)\b)/gi,
    /(0x[0-9a-fA-F]+)/g,
    /(\%27|\%22|\%3D|\%3B)/g,
    /(\'\s*or\s*\'|\"\s*or\s*\"|\'\s*and\s*\'|\"\s*and\s*\")/gi
  ];

  static sanitize(input: string): string {
    if (typeof input !== 'string') {
      return input;
    }

    let sanitized = input;
    
    for (const pattern of this.dangerousPatterns) {
      if (pattern.test(sanitized)) {
        logger.warn('Potential SQL injection detected:', { input: sanitized });
        throw new Error('输入包含不安全内容');
      }
    }

    return sanitized;
  }

  static validateIdentifier(identifier: string): boolean {
    return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier);
  }
}

// XSS防护
export class XSSGuard {
  private static dangerousTags = [
    'script', 'iframe', 'object', 'embed', 'form', 'input',
    'button', 'select', 'textarea', 'link', 'meta', 'style'
  ];

  private static dangerousAttributes = [
    'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover',
    'onmousemove', 'onmouseout', 'onkeypress', 'onkeydown', 'onkeyup',
    'onload', 'onunload', 'onfocus', 'onblur', 'onsubmit', 'onreset',
    'onselect', 'onchange', 'onerror', 'onabort', 'onresize', 'onscroll'
  ];

  static sanitizeHtml(input: string): string {
    if (typeof input !== 'string') {
      return input;
    }

    // 移除危险标签
    let sanitized = input;
    for (const tag of this.dangerousTags) {
      const regex = new RegExp(`<${tag}\\b[^>]*>`, 'gi');
      sanitized = sanitized.replace(regex, '&lt;' + tag + '&gt;');
    }

    // 移除危险属性
    for (const attr of this.dangerousAttributes) {
      const regex = new RegExp(`${attr}\\s*=`, 'gi');
      sanitized = sanitized.replace(regex, '_' + attr + '=');
    }

    // 移除javascript:协议
    sanitized = sanitized.replace(/javascript:/gi, 'removed:');

    // 移除data:协议(可能导致XSS)
    sanitized = sanitized.replace(/data:(?!image\/)/gi, 'removed:');

    return sanitized;
  }

  static escapeHtml(input: string): string {
    const htmlEntities: Record<string, string> = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#x27;',
      '/': '&#x2F;',
      '`': '&#x60;',
      '=': '&#x3D;'
    };

    return input.replace(/[&<>"'`=\/]/g, char => htmlEntities[char] || char);
  }
}

// 请求限流
export class RateLimiter {
  private redis: Redis;
  private config: {
    windowMs: number;
    maxRequests: number;
    keyPrefix: string;
  };

  constructor(redis: Redis, config?: Partial<typeof RateLimiter.prototype.config>) {
    this.redis = redis;
    this.config = {
      windowMs: 15 * 60 * 1000, // 15分钟
      maxRequests: 100,
      keyPrefix: 'ratelimit:',
      ...config
    };
  }

  async isAllowed(identifier: string): Promise<{
    allowed: boolean;
    remaining: number;
    resetTime: number;
  }> {
    const key = `${this.config.keyPrefix}${identifier}`;
    const now = Date.now();
    const windowStart = Math.floor(now / this.config.windowMs) * this.config.windowMs;
    const windowKey = `${key}:${windowStart}`;

    const pipeline = this.redis.pipeline();
    pipeline.incr(windowKey);
    pipeline.pexpire(windowKey, this.config.windowMs);

    const results = await pipeline.exec();
    
    if (!results) {
      return { allowed: true, remaining: this.config.maxRequests, resetTime: windowStart + this.config.windowMs };
    }

    const requestCount = results[0][1] as number;
    const allowed = requestCount <= this.config.maxRequests;
    const remaining = Math.max(0, this.config.maxRequests - requestCount);
    const resetTime = windowStart + this.config.windowMs;

    return { allowed, remaining, resetTime };
  }

  // 滑动窗口限流
  async isAllowedSlidingWindow(identifier: string): Promise<{
    allowed: boolean;
    remaining: number;
    resetTime: number;
  }> {
    const key = `${this.config.keyPrefix}sw:${identifier}`;
    const now = Date.now();
    const windowStart = now - this.config.windowMs;

    const pipeline = this.redis.pipeline();
    pipeline.zremrangebyscore(key, '-inf', windowStart);
    pipeline.zcard(key);
    pipeline.zadd(key, now, `${now}-${Math.random()}`);
    pipeline.pexpire(key, this.config.windowMs);

    const results = await pipeline.exec();
    
    if (!results) {
      return { allowed: true, remaining: this.config.maxRequests, resetTime: now + this.config.windowMs };
    }

    const currentCount = results[1][1] as number;
    const allowed = currentCount < this.config.maxRequests;
    const remaining = Math.max(0, this.config.maxRequests - currentCount - 1);
    const resetTime = now + this.config.windowMs;

    return { allowed, remaining, resetTime };
  }
}

// 安全头设置
export const securityHeaders = {
  'Content-Security-Policy': [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.minijd.com",
    "style-src 'self' 'unsafe-inline' https://cdn.minijd.com",
    "img-src 'self' data: https: blob:",
    "font-src 'self' https://cdn.minijd.com",
    "connect-src 'self' https://api.minijd.com wss://ws.minijd.com",
    "frame-ancestors 'none'",
    "base-uri 'self'",
    "form-action 'self'"
  ].join('; '),
  'X-Content-Type-Options': 'nosniff',
  'X-Frame-Options': 'DENY',
  'X-XSS-Protection': '1; mode=block',
  'Referrer-Policy': 'strict-origin-when-cross-origin',
  'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
  'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
  'Cross-Origin-Opener-Policy': 'same-origin',
  'Cross-Origin-Embedder-Policy': 'require-corp',
  'Cross-Origin-Resource-Policy': 'same-origin'
};

八、项目总结与展望

8.1 项目成果

通过"迷你京东"全栈项目的设计与实现,我们成功构建了一个现代化、高性能、可扩展的电商平台。主要成果包括:

8.1.1 技术架构成果

  • 微服务架构: 实现了6个核心微服务的独立部署和扩展

  • 全栈技术栈: 整合了Next.js 14、Go、Node.js、PostgreSQL、Redis、Elasticsearch等主流技术

  • 云原生部署: 基于Docker和Kubernetes的容器化部署方案

  • 监控体系: 完整的Prometheus + Grafana + ELK监控体系

8.1.2 性能优化成果

  • 首屏加载: 从4.8s优化到1.2s,提升75%

  • 包体积: 从4.4MB优化到1.1MB,减少75%

  • 构建时间: 从240s优化到45s,提升81%

  • 并发能力: 支持10K QPS,99.9%可用性

8.1.3 业务功能成果

  • 完整购物流程: 用户注册、商品浏览、搜索、购物车、订单、支付

  • 智能推荐: 基于用户行为的个性化商品推荐

  • 搜索优化: Elasticsearch驱动的快速精准搜索

  • 库存管理: 实时库存扣减和预警

8.2 技术债务与改进方向

8.2.1 当前技术债务

  1. 数据一致性: 跨服务事务需要更完善的Saga模式

  2. 测试覆盖率: 部分服务单元测试覆盖率需提升

  3. 文档完善: API文档和架构文档需要持续更新

  4. 性能调优: 部分复杂查询需要进一步优化

8.2.2 未来改进方向

  1. 服务网格: 引入Istio实现更细粒度的流量管理

  2. AI集成: 智能客服、商品图像识别、需求预测

  3. 多租户支持: 为B2B业务扩展多租户架构

  4. 边缘计算: 在CDN边缘节点处理部分业务逻辑

  5. 区块链: 商品溯源和供应链透明化

8.3 经验教训

8.3.1 架构设计经验

  • 渐进式架构: 从单体到微服务的平滑过渡至关重要

  • 领域驱动设计: 合理的领域划分是微服务成功的基础

  • 容错设计: 熔断、降级、限流是分布式系统的生命线

  • 数据建模: 早期的数据模型设计影响深远,需要慎重考虑

8.3.2 开发实践经验

  • 自动化测试: 测试金字塔的坚持能显著提升质量

  • CI/CD流水线: 自动化的构建部署是快速迭代的保障

  • 代码规范: 统一的代码规范和审查机制提升可维护性

  • 监控先行: 可观测性建设应该与功能开发同步进行

8.3.3 团队协作经验

  • 文档驱动: 技术文档和API文档的及时更新很重要

  • 技术分享: 定期的技术分享促进团队成长

  • 故障复盘: 每个线上问题的复盘都是宝贵的学习机会

  • 持续学习: 技术栈的快速演进要求团队保持学习热情

8.4 项目价值

"迷你京东"项目不仅是一个技术实践,更是一个全栈开发能力的综合体现。它展示了:
  1. 技术深度: 从底层数据库优化到前端性能调优的完整技术栈掌握

  2. 工程能力: 从需求分析到生产部署的全流程工程化能力

  3. 架构思维: 从单体应用到微服务的架构演进思维

  4. 产品意识: 技术方案与业务价值的平衡考量

这个项目为类似规模的电商平台提供了可复用的技术参考,也为个人技术成长提供了宝贵的实战经验。

结语: 通过"迷你京东"全栈项目的实践,我们深刻体会到现代软件开发的复杂性和挑战性。只有持续学习、不断实践、及时总结,才能在快速变化的技术浪潮中保持竞争力。希望这份技术文档能为读者提供有价值的参考,也期待与更多开发者交流学习,共同推动技术进步。
需要我进一步解释微服务间通信的最佳实践,或者如何设计更完善的容错机制吗?


群贤毕至

访客