Codex + 后端框架实战:Spring/Express/Django 业务逻辑全自动生成

目标:同一业务需求(电商订单系统),对比三大框架的 AI 生成策略、代码质量和工程化差异。


一、测试基准:统一业务需求

# 需求规格(ARD 格式)
业务: 电商订单核心流程
功能模块:
  1. 订单创建: 购物车结算 → 库存预占 → 价格计算 → 订单持久化
  2. 订单支付: 支付回调 → 状态机推进 → 库存扣减 → 通知发货
  3. 订单查询: 多维度筛选 → 分页 → 关联数据聚合

非功能需求:
  - 并发: 库存扣减需乐观锁/分布式锁
  - 事务: 创建订单 + 预占库存原子性
  - 性能: P99 < 200ms,支持 1万 QPS
  - 安全: 防超卖、防重复支付、SQL注入防护

技术约束:
  - Java: Spring Boot 3.2 + JPA + Redis
  - Node: Express 4 + TypeScript + Prisma
  - Python: Django 4.2 + DRF + Celery

二、Spring Boot 实战:企业级 Java 方案

2.1 架构约束定义(人工)

// 架构基类(强制 Codex 遵循)
// domain/aggregate/AggregateRoot.java
public abstract class AggregateRoot<ID> {
    private final ID id;
    private final List<DomainEvent> domainEvents = new ArrayList<>();
    
    protected void registerEvent(DomainEvent event) {
        domainEvents.add(event);
    }
    
    public List<DomainEvent> popEvents() {
        List<DomainEvent> events = new ArrayList<>(domainEvents);
        domainEvents.clear();
        return events;
    }
}

// application/UseCase.java
public interface UseCase<I, O> {
    O execute(I input);
}

// infrastructure/UnitOfWork.java
public interface UnitOfWork {
    void commit();
    void rollback();
}

2.2 Codex 生成 Prompt

【Spring Boot 业务生成】

已实现:领域基类(AggregateRoot, UseCase, UnitOfWork)
参考范例:src/user/ 模块(已固定上下文)

需求:实现订单创建用例(CreateOrderUseCase)

约束:
1. 必须实现 UseCase<CreateOrderInput, CreateOrderOutput>
2. 领域逻辑在 Order 聚合根内(继承 AggregateRoot)
3. 库存预占使用乐观锁(@Version 或 CAS)
4. 事务边界在 UseCase 层(@Transactional)
5. 发布 OrderCreatedEvent(领域事件)
6. 禁止:Repository 直接返回 Entity,必须转 Domain 对象

验证:
- 代码必须通过 Checkstyle(Google Java Style)
- 必须通过 ArchUnit 架构测试(分层依赖检查)
- 单元测试:正常流程、库存不足、并发冲突

输出:
1. Order 聚合根(实体 + 值对象)
2. CreateOrderUseCase(应用服务)
3. OrderRepository 接口(领域层)+ JPA 实现(基础设施)
4. 单元测试(JUnit 5 + Mockito)
5. 架构合规说明文档

2.3 Codex 生成结果

// ============== Domain Layer ==============

// domain/vo/Money.java
public record Money(
    @NotNull BigDecimal amount,
    @NotNull Currency currency
) implements Serializable {
    public Money {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new DomainException("金额不能为负");
        }
    }
    
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new DomainException("币种不匹配");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
    
    public static Money zero(Currency currency) {
        return new Money(BigDecimal.ZERO, currency);
    }
}

// domain/entity/OrderItem.java
@Entity
@Table(name = "order_items")
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;
    
    @Column(name = "product_id", nullable = false)
    private Long productId;
    
    @Embedded
    private Money unitPrice;
    
    @Column(name = "quantity", nullable = false)
    private Integer quantity;
    
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "amount", column = @Column(name = "subtotal_amount")),
        @AttributeOverride(name = "currency", column = @Column(name = "subtotal_currency"))
    })
    private Money subtotal;
    
    protected OrderItem() {} // JPA 需要
    
    public OrderItem(Long productId, Money unitPrice, Integer quantity) {
        this.productId = productId;
        this.unitPrice = unitPrice;
        this.quantity = quantity;
        this.subtotal = unitPrice.amount()
            .multiply(BigDecimal.valueOf(quantity))
            .pipe(a -> new Money(a, unitPrice.currency()));
    }
    
    // Getters...
}

// domain/aggregate/Order.java
@Entity
@Table(name = "orders")
public class Order extends AggregateRoot<Long> {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "user_id", nullable = false)
    private Long userId;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
    
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "amount", column = @Column(name = "total_amount")),
        @AttributeOverride(name = "currency", column = @Column(name = "total_currency"))
    })
    private Money totalAmount;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", nullable = false)
    private OrderStatus status;
    
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @Version
    private Long version; // 乐观锁
    
    protected Order() {
        super(null);
    }
    
    // 工厂方法:创建订单(领域逻辑核心)
    public static Order create(Long userId, List<OrderItem> items) {
        if (items == null || items.isEmpty()) {
            throw new DomainException("订单不能为空");
        }
        
        Order order = new Order();
        order.userId = userId;
        order.items = items;
        order.status = OrderStatus.CREATED;
        order.createdAt = LocalDateTime.now();
        
        // 计算总价
        order.totalAmount = items.stream()
            .map(OrderItem::getSubtotal)
            .reduce(Money.zero(Currency.CNY), Money::add);
        
        // 关联双向关系
        items.forEach(item -> item.setOrder(order));
        
        // 注册领域事件
        order.registerEvent(new OrderCreatedEvent(
            order.id,
            order.userId,
            order.totalAmount,
            order.createdAt
        ));
        
        return order;
    }
    
    // 支付成功:状态机推进
    public void pay(Money paidAmount) {
        if (this.status != OrderStatus.CREATED) {
            throw new DomainException("订单状态不正确,当前: " + this.status);
        }
        
        if (!this.totalAmount.amount().equals(paidAmount.amount())) {
            throw new DomainException("支付金额不匹配");
        }
        
        this.status = OrderStatus.PAID;
        registerEvent(new OrderPaidEvent(this.id, paidAmount, LocalDateTime.now()));
    }
    
    // Getters...
}

// ============== Application Layer ==============

// application/dto/CreateOrderInput.java
public record CreateOrderInput(
    @NotNull Long userId,
    @NotEmpty List<CartItem> cartItems
) {
    public record CartItem(
        @NotNull Long productId,
        @NotNull @Positive Integer quantity
    ) {}
}

// application/dto/CreateOrderOutput.java
public record CreateOrderOutput(
    Long orderId,
    Money totalAmount,
    OrderStatus status,
    LocalDateTime createdAt
) {}

// application/usecase/CreateOrderUseCase.java
@Service
@RequiredArgsConstructor
@Transactional(isolation = Isolation.READ_COMMITTED)
public class CreateOrderUseCase implements UseCase<CreateOrderInput, CreateOrderOutput> {
    
    private final OrderRepository orderRepository;
    private final InventoryService inventoryService; // 防腐层接口
    private final PricingService pricingService;
    private final DomainEventPublisher eventPublisher;
    
    @Override
    public CreateOrderOutput execute(CreateOrderInput input) {
        // 1. 库存预占(带乐观锁)
        List<InventoryReservation> reservations = new ArrayList<>();
        for (var cartItem : input.cartItems()) {
            var reservation = inventoryService.reserve(
                cartItem.productId(),
                cartItem.quantity()
            );
            reservations.add(reservation);
        }
        
        try {
            // 2. 获取商品价格
            List<OrderItem> orderItems = new ArrayList<>();
            for (int i = 0; i < input.cartItems().size(); i++) {
                var cartItem = input.cartItems().get(i);
                var price = pricingService.getPrice(cartItem.productId());
                
                orderItems.add(new OrderItem(
                    cartItem.productId(),
                    price,
                    cartItem.quantity()
                ));
            }
            
            // 3. 创建订单(领域逻辑)
            Order order = Order.create(input.userId(), orderItems);
            
            // 4. 持久化
            Order savedOrder = orderRepository.save(order);
            
            // 5. 发布领域事件
            order.popEvents().forEach(eventPublisher::publish);
            
            // 6. 确认库存预占
            reservations.forEach(r -> inventoryService.confirm(r.id()));
            
            return new CreateOrderOutput(
                savedOrder.getId(),
                savedOrder.getTotalAmount(),
                savedOrder.getStatus(),
                savedOrder.getCreatedAt()
            );
            
        } catch (Exception e) {
            // 回滚库存预占
            reservations.forEach(r -> inventoryService.cancel(r.id()));
            throw e;
        }
    }
}

// ============== Infrastructure Layer ==============

// infrastructure/persistence/JpaOrderRepository.java
@Repository
@RequiredArgsConstructor
public class JpaOrderRepository implements OrderRepository {
    
    private final SpringDataOrderRepository jpaRepository;
    private final OrderJpaMapper mapper;
    
    @Override
    public Optional<Order> findById(Long id) {
        return jpaRepository.findById(id)
            .map(mapper::toDomain);
    }
    
    @Override
    public Order save(Order order) {
        OrderJpaEntity entity = mapper.toEntity(order);
        OrderJpaEntity saved = jpaRepository.save(entity);
        return mapper.toDomain(saved);
    }
}

// infrastructure/messaging/SpringEventPublisher.java
@Component
@RequiredArgsConstructor
public class SpringEventPublisher implements DomainEventPublisher {
    
    private final ApplicationEventPublisher publisher;
    
    @Override
    public void publish(DomainEvent event) {
        publisher.publishEvent(event);
    }
}

// ============== Test ==============

@SpringBootTest
@Transactional
class CreateOrderUseCaseTest {
    
    @Autowired
    private CreateOrderUseCase useCase;
    
    @MockBean
    private InventoryService inventoryService;
    
    @MockBean
    private PricingService pricingService;
    
    @Test
    @DisplayName("正常创建订单")
    void execute_Success() {
        // Given
        when(inventoryService.reserve(any(), any()))
            .thenReturn(new InventoryReservation(1L, 100L, 2));
        when(pricingService.getPrice(100L))
            .thenReturn(new Money(new BigDecimal("99.99"), Currency.CNY));
        
        var input = new CreateOrderInput(
            1L,
            List.of(new CreateOrderInput.CartItem(100L, 2))
        );
        
        // When
        var output = useCase.execute(input);
        
        // Then
        assertThat(output.orderId()).isNotNull();
        assertThat(output.totalAmount().amount())
            .isEqualByComparingTo(new BigDecimal("199.98"));
        assertThat(output.status()).isEqualTo(OrderStatus.CREATED);
        
        verify(inventoryService).confirm(1L);
    }
    
    @Test
    @DisplayName("库存不足,抛出异常并回滚预占")
    void execute_InsufficientInventory() {
        when(inventoryService.reserve(any(), any()))
            .thenThrow(new InsufficientInventoryException("库存不足"));
        
        assertThatThrownBy(() -> useCase.execute(any()))
            .isInstanceOf(InsufficientInventoryException.class);
    }
}

2.4 Spring 生成质量评估

维度 评分 说明
架构合规 9/10 严格遵循 DDD 分层,依赖方向正确
类型安全 10/10 泛型、Record、@NotNull 完整
事务处理 8/10 手动回滚库存需优化为 Saga 模式
并发控制 7/10 乐观锁在 Order,但库存需分布式锁
测试覆盖 9/10 正常+异常路径覆盖,需补充并发测试
代码量 300+ 行 偏冗长,但符合 Java 企业级习惯

三、Express + TypeScript 实战:轻量全栈方案

3.1 架构约束定义(人工)

// 架构基类
// core/UseCase.ts
export interface UseCase<Input, Output> {
  execute(input: Input): Promise<Output>;
}

// core/Result.ts(函数式错误处理)
export type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };

// core/DomainEvent.ts
export interface DomainEvent {
  readonly occurredOn: Date;
  readonly eventType: string;
}

3.2 Codex 生成 Prompt

【Express + TypeScript 业务生成】

已实现:核心基类(UseCase, Result, DomainEvent)
参考范例:src/modules/user/(函数式风格,禁止 class 过多)

需求:实现订单创建用例

约束:
1. 使用函数式编程风格(优先函数/接口,class 仅用于聚合根)
2. 错误处理使用 Result 类型,禁止 throw
3. 异步使用 async/await,禁止回调
4. 数据库使用 Prisma,禁止原生 SQL
5. 库存预占使用 Redis 分布式锁(Redlock)
6. 发布事件到 Redis Pub/Sub(解耦)

验证:
- ESLint(@typescript-eslint/recommended)
- 类型检查(strict 模式零错误)
- 测试:Jest + 内存数据库(sqlite)

输出:
1. 领域层(函数 + 类型定义)
2. 应用层(UseCase 实现)
3. 基础设施(Prisma schema + Repository 实现)
4. API 层(Express Route + Controller)
5. 完整测试(集成测试覆盖数据库操作)

3.3 Codex 生成结果

// ============== Domain Layer ==============

// domain/order/types.ts
export type OrderId = string & { readonly __brand: 'OrderId' };
export type UserId = string & { readonly __brand: 'UserId' };
export type ProductId = string & { readonly __brand: 'ProductId' };

export interface Money {
  readonly amount: number;
  readonly currency: 'CNY' | 'USD';
}

export const Money = {
  create(amount: number, currency: Money['currency']): Result<Money> {
    if (amount < 0) {
      return { success: false, error: new Error('金额不能为负') };
    }
    return { success: true, data: { amount, currency } };
  },
  
  add(a: Money, b: Money): Result<Money> {
    if (a.currency !== b.currency) {
      return { success: false, error: new Error('币种不匹配') };
    }
    return Money.create(a.amount + b.amount, a.currency);
  },
  
  zero(currency: Money['currency']): Money {
    return { amount: 0, currency };
  }
};

export enum OrderStatus {
  CREATED = 'CREATED',
  PAID = 'PAID',
  SHIPPED = 'SHIPPED',
  COMPLETED = 'COMPLETED',
  CANCELLED = 'CANCELLED'
}

export interface OrderItem {
  readonly productId: ProductId;
  readonly unitPrice: Money;
  readonly quantity: number;
  readonly subtotal: Money;
}

export const OrderItem = {
  create(
    productId: ProductId,
    unitPrice: Money,
    quantity: number
  ): Result<OrderItem> {
    if (quantity <= 0) {
      return { success: false, error: new Error('数量必须为正') };
    }
    
    const subtotalResult = Money.create(
      unitPrice.amount * quantity,
      unitPrice.currency
    );
    
    if (!subtotalResult.success) {
      return subtotalResult;
    }
    
    return {
      success: true,
      data: {
        productId,
        unitPrice,
        quantity,
        subtotal: subtotalResult.data
      }
    };
  }
};

export interface Order {
  readonly id: OrderId;
  readonly userId: UserId;
  readonly items: readonly OrderItem[];
  readonly totalAmount: Money;
  readonly status: OrderStatus;
  readonly createdAt: Date;
  readonly domainEvents: readonly DomainEvent[];
}

// 领域事件
export interface OrderCreatedEvent extends DomainEvent {
  readonly type: 'OrderCreated';
  readonly orderId: OrderId;
  readonly userId: UserId;
  readonly totalAmount: Money;
}

// ============== Domain Functions ==============

// domain/order/order.ts
import { v4 as uuidv4 } from 'uuid';

export const Order = {
  create(
    userId: UserId,
    items: OrderItem[]
  ): Result<Order & { popEvents(): DomainEvent[] }> {
    if (items.length === 0) {
      return { success: false, error: new Error('订单不能为空') };
    }
    
    // 计算总价
    const totalResult = items.reduce<Result<Money>>(
      (acc, item) => {
        if (!acc.success) return acc;
        return Money.add(acc.data, item.subtotal);
      },
      { success: true, data: Money.zero('CNY') }
    );
    
    if (!totalResult.success) {
      return totalResult;
    }
    
    const id = uuidv4() as OrderId;
    const event: OrderCreatedEvent = {
      type: 'OrderCreated',
      occurredOn: new Date(),
      orderId: id,
      userId,
      totalAmount: totalResult.data
    };
    
    let events: DomainEvent[] = [event];
    
    const order: Order & { popEvents(): DomainEvent[] } = {
      id,
      userId,
      items: Object.freeze([...items]),
      totalAmount: totalResult.data,
      status: OrderStatus.CREATED,
      createdAt: new Date(),
      get domainEvents() { return Object.freeze([...events]); },
      popEvents() {
        const popped = [...events];
        events = [];
        return popped;
      }
    };
    
    return { success: true, data: order };
  }
};

// ============== Application Layer ==============

// application/order/CreateOrderUseCase.ts
import { UseCase } from '../../core/UseCase';
import { Result } from '../../core/Result';
import { DomainEventPublisher } from '../../core/DomainEventPublisher';
import { OrderRepository } from '../../domain/order/OrderRepository';
import { InventoryService } from '../../domain/inventory/InventoryService';
import { PricingService } from '../../domain/pricing/PricingService';
import { Order, OrderItem, Money } from '../../domain/order';
import { LockService } from '../../infrastructure/lock/LockService';

export interface CreateOrderInput {
  readonly userId: string;
  readonly cartItems: ReadonlyArray<{
    readonly productId: string;
    readonly quantity: number;
  }>;
}

export interface CreateOrderOutput {
  readonly orderId: string;
  readonly totalAmount: number;
  readonly currency: string;
  readonly status: string;
}

export class CreateOrderUseCase implements UseCase<CreateOrderInput, CreateOrderOutput> {
  constructor(
    private readonly orderRepo: OrderRepository,
    private readonly inventoryService: InventoryService,
    private readonly pricingService: PricingService,
    private readonly eventPublisher: DomainEventPublisher,
    private readonly lockService: LockService
  ) {}
  
  async execute(input: CreateOrderInput): Promise<Result<CreateOrderOutput>> {
    const lockKeys = input.cartItems.map(item => `inventory:${item.productId}`);
    
    // 获取分布式锁(防止超卖)
    const lockResult = await this.lockService.acquire(lockKeys, 5000);
    if (!lockResult.success) {
      return { success: false, error: new Error('系统繁忙,请重试') };
    }
    
    const reservations: string[] = [];
    
    try {
      // 1. 库存预占
      for (const item of input.cartItems) {
        const reserveResult = await this.inventoryService.reserve(
          item.productId as ProductId,
          item.quantity
        );
        
        if (!reserveResult.success) {
          return reserveResult;
        }
        
        reservations.push(reserveResult.data.reservationId);
      }
      
      // 2. 构建订单项
      const orderItems: OrderItem[] = [];
      for (const item of input.cartItems) {
        const priceResult = await this.pricingService.getPrice(item.productId as ProductId);
        if (!priceResult.success) {
          return priceResult;
        }
        
        const itemResult = OrderItem.create(
          item.productId as ProductId,
          priceResult.data,
          item.quantity
        );
        
        if (!itemResult.success) {
          return itemResult;
        }
        
        orderItems.push(itemResult.data);
      }
      
      // 3. 创建订单
      const orderResult = Order.create(input.userId as UserId, orderItems);
      if (!orderResult.success) {
        return orderResult;
      }
      
      const order = orderResult.data;
      
      // 4. 持久化
      const saveResult = await this.orderRepo.save(order);
      if (!saveResult.success) {
        return saveResult;
      }
      
      // 5. 发布事件
      const events = order.popEvents();
      for (const event of events) {
        await this.eventPublisher.publish(event);
      }
      
      // 6. 确认库存
      for (const reservationId of reservations) {
        await this.inventoryService.confirm(reservationId);
      }
      
      return {
        success: true,
        data: {
          orderId: order.id,
          totalAmount: order.totalAmount.amount,
          currency: order.totalAmount.currency,
          status: order.status
        }
      };
      
    } catch (error) {
      // 回滚库存
      for (const reservationId of reservations) {
        await this.inventoryService.cancel(reservationId).catch(() => {});
      }
      
      return {
        success: false,
        error: error instanceof Error ? error : new Error(String(error))
      };
    } finally {
      await this.lockService.release(lockResult.data);
    }
  }
}

// ============== Infrastructure Layer ==============

// infrastructure/persistence/prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Order {
  id            String   @id @default(uuid())
  userId        String
  status        String
  totalAmount   Decimal  @db.Decimal(19, 4)
  currency      String
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  
  items         OrderItem[]
  
  @@index([userId])
  @@index([status, createdAt])
}

model OrderItem {
  id          String  @id @default(uuid())
  orderId     String
  productId   String
  unitPrice   Decimal @db.Decimal(19, 4)
  quantity    Int
  subtotal    Decimal @db.Decimal(19, 4)
  
  order       Order   @relation(fields: [orderId], references: [id])
  
  @@index([productId])
}

// infrastructure/persistence/PrismaOrderRepository.ts
import { PrismaClient } from '@prisma/client';
import { OrderRepository } from '../../domain/order/OrderRepository';
import { Order, OrderItem, OrderStatus, Money } from '../../domain/order';
import { Result } from '../../core/Result';

export class PrismaOrderRepository implements OrderRepository {
  constructor(private readonly prisma: PrismaClient) {}
  
  async save(order: Order): Promise<Result<Order>> {
    try {
      const created = await this.prisma.order.create({
        data: {
          id: order.id,
          userId: order.userId,
          status: order.status,
          totalAmount: order.totalAmount.amount,
          currency: order.totalAmount.currency,
          items: {
            create: order.items.map(item => ({
              productId: item.productId,
              unitPrice: item.unitPrice.amount,
              quantity: item.quantity,
              subtotal: item.subtotal.amount
            }))
          }
        },
        include: { items: true }
      });
      
      return { success: true, data: order };
    } catch (error) {
      return {
        success: false,
        error: new Error(`保存订单失败: ${error}`)
      };
    }
  }
  
  // ... findById 等
}

// ============== API Layer ==============

// api/routes/orderRoutes.ts
import { Router } from 'express';
import { CreateOrderUseCase } from '../../application/order/CreateOrderUseCase';
import { body, validationResult } from 'express-validator';

export const createOrderRouter = (
  createOrderUseCase: CreateOrderUseCase
): Router => {
  const router = Router();
  
  router.post(
    '/orders',
    [
      body('userId').isUUID(),
      body('cartItems').isArray({ min: 1 }),
      body('cartItems.*.productId').isUUID(),
      body('cartItems.*.quantity').isInt({ min: 1 })
    ],
    async (req, res) => {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
      }
      
      const result = await createOrderUseCase.execute(req.body);
      
      if (!result.success) {
        return res.status(400).json({ error: result.error.message });
      }
      
      res.status(201).json(result.data);
    }
  );
  
  return router;
};

// ============== Test ==============

// tests/integration/CreateOrder.test.ts
import { CreateOrderUseCase } from '../../application/order/CreateOrderUseCase';
import { PrismaOrderRepository } from '../../infrastructure/persistence/PrismaOrderRepository';
import { MockInventoryService } from '../mocks/MockInventoryService';
import { MockPricingService } from '../mocks/MockPricingService';
import { MockEventPublisher } from '../mocks/MockEventPublisher';
import { MockLockService } from '../mocks/MockLockService';
import { PrismaClient } from '@prisma/client';

describe('CreateOrderUseCase Integration', () => {
  let prisma: PrismaClient;
  let useCase: CreateOrderUseCase;
  
  beforeAll(async () => {
    prisma = new PrismaClient({
      datasources: { db: { url: 'file:./test.db' } } // SQLite 内存
    });
    await prisma.$connect();
    
    const repo = new PrismaOrderRepository(prisma);
    useCase = new CreateOrderUseCase(
      repo,
      new MockInventoryService(), // 总是成功
      new MockPricingService({ 'prod-1': 99.99 }),
      new MockEventPublisher(),
      new MockLockService() // 总是获取锁
    );
  });
  
  afterAll(async () => {
    await prisma.$disconnect();
  });
  
  it('should create order successfully', async () => {
    const result = await useCase.execute({
      userId: 'user-1',
      cartItems: [{ productId: 'prod-1', quantity: 2 }]
    });
    
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.totalAmount).toBe(199.98);
      expect(result.data.status).toBe('CREATED');
    }
  });
  
  it('should handle insufficient inventory', async () => {
    // 模拟库存不足...
  });
});

3.4 Express 生成质量评估

维度 评分 说明
架构合规 9/10 函数式风格,依赖注入清晰
类型安全 10/10 branded type 防 ID 混用,Result 强制错误处理
代码简洁 9/10 200+ 行,比 Spring 少 30%
异步处理 9/10 async/await 贯穿,无回调地狱
测试友好 8/10 依赖注入便于 mock,但需手动管理 Prisma 生命周期
运行时安全 7/10 无编译期保证,依赖运行时检查

四、Django + DRF 实战:快速交付方案

4.1 架构约束定义(人工)

# 架构规范(Django 约定优于配置)
# core/models.py
from django.db import models

class TimestampedModel(models.Model):
    """抽象基类:自动添加创建/更新时间"""
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        abstract = True

class DomainEventMixin(models.Model):
    """领域事件支持"""
    class Meta:
        abstract = True
    
    def save(self, *args, **kwargs):
        is_new = self.pk is None
        super().save(*args, **kwargs)
        if is_new:
            self._publish_created_event()
    
    def _publish_created_event(self):
        raise NotImplementedError

4.2 Codex 生成 Prompt

【Django + DRF 业务生成】

已实现:TimestampedModel, DomainEventMixin
参考范例:apps/users/(CBV 风格,DRF Serializer)

需求:实现订单创建 API

约束:
1. 模型继承 TimestampedModel,使用 Django ORM(禁止原生 SQL)
2. API 使用 DRF ModelSerializer + ModelViewSet
3. 业务逻辑在 Serializer 或 Service 层,禁止在 View 中写逻辑
4. 库存预占使用 select_for_update()(数据库锁)
5. 异步任务使用 Celery(发送通知、更新统计)
6. 单元测试使用 Django TestCase + factory_boy

验证:
- pylint-django 零警告
- 测试覆盖率 >85%
- API 文档自动生成(drf-spectacular)

输出:
1. models.py(Order, OrderItem)
2. serializers.py(创建 + 查询)
3. views.py(ModelViewSet + 自定义 action)
4. services.py(复杂业务逻辑)
5. tasks.py(Celery 异步任务)
6. tests.py(完整测试)

4.3 Codex 生成结果

# ============== Models ==============

# apps/orders/models.py
from django.db import models, transaction
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
from apps.core.models import TimestampedModel, DomainEventMixin
from apps.products.models import Product  # 假设已存在

class OrderStatus(models.TextChoices):
    CREATED = 'created', '待支付'
    PAID = 'paid', '已支付'
    SHIPPED = 'shipped', '已发货'
    COMPLETED = 'completed', '已完成'
    CANCELLED = 'cancelled', '已取消'

class Order(TimestampedModel, DomainEventMixin):
    """订单聚合根"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(
        'users.User',
        on_delete=models.PROTECT,
        related_name='orders',
        verbose_name='用户'
    )
    status = models.CharField(
        max_length=20,
        choices=OrderStatus.choices,
        default=OrderStatus.CREATED,
        verbose_name='状态'
    )
    total_amount = models.DecimalField(
        max_digits=19,
        decimal_places=2,
        default=0,
        verbose_name='总金额'
    )
    currency = models.CharField(max_length=3, default='CNY', verbose_name='币种')
    paid_at = models.DateTimeField(null=True, blank=True, verbose_name='支付时间')
    
    class Meta:
        db_table = 'orders'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['user', '-created_at']),
            models.Index(fields=['status', 'created_at']),
        ]
    
    def clean(self):
        if self.paid_at and self.status != OrderStatus.PAID:
            raise ValidationError('支付时间仅在已支付状态允许')
    
    def calculate_total(self):
        """计算订单总价"""
        self.total_amount = sum(
            item.subtotal for item in self.items.all()
        )
        self.save(update_fields=['total_amount', 'updated_at'])
    
    def _publish_created_event(self):
        """发布订单创建事件(Celery 异步处理)"""
        from .tasks import notify_order_created
        notify_order_created.delay(str(self.id), str(self.user_id))
    
    def __str__(self):
        return f'订单 {self.id} ({self.get_status_display()})'


class OrderItem(TimestampedModel):
    """订单项(值对象)"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    order = models.ForeignKey(
        Order,
        on_delete=models.CASCADE,
        related_name='items',
        verbose_name='订单'
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        verbose_name='商品'
    )
    product_name = models.CharField(max_length=255, verbose_name='商品名称快照')
    unit_price = models.DecimalField(
        max_digits=19,
        decimal_places=2,
        verbose_name='单价'
    )
    quantity = models.PositiveIntegerField(
        validators=[MinValueValidator(1)],
        verbose_name='数量'
    )
    subtotal = models.DecimalField(
        max_digits=19,
        decimal_places=2,
        verbose_name='小计'
    )
    
    class Meta:
        db_table = 'order_items'
    
    def save(self, *args, **kwargs):
        # 自动计算小计
        self.subtotal = self.unit_price * self.quantity
        super().save(*args, **kwargs)
    
    def clean(self):
        # 库存检查(悲观锁)
        with transaction.atomic():
            product = Product.objects.select_for_update().get(pk=self.product_id)
            if product.stock < self.quantity:
                raise ValidationError(
                    f'商品 {product.name} 库存不足,剩余 {product.stock}'
                )


# ============== Services ==============

# apps/orders/services.py
from django.db import transaction
from django.core.exceptions import ValidationError
from .models import Order, OrderItem, OrderStatus
from apps.inventory.services import InventoryService  # 防腐层

class OrderService:
    """订单应用服务"""
    
    def __init__(self):
        self.inventory_service = InventoryService()
    
    @transaction.atomic
    def create_order(self, user, cart_items: list[dict]) -> Order:
        """
        创建订单(核心事务)
        
        Args:
            user: 用户实例
            cart_items: [{'product_id': 'uuid', 'quantity': 2}, ...]
        
        Returns:
            Order: 创建的订单
        
        Raises:
            ValidationError: 库存不足或参数错误
        """
        if not cart_items:
            raise ValidationError('购物车不能为空')
        
        # 1. 创建订单(状态:待支付)
        order = Order.objects.create(user=user, status=OrderStatus.CREATED)
        
        try:
            # 2. 创建订单项(包含库存检查)
            for item_data in cart_items:
                product_id = item_data['product_id']
                quantity = item_data['quantity']
                
                # 预占库存(使用数据库锁防止超卖)
                reserved = self.inventory_service.reserve(
                    product_id=product_id,
                    quantity=quantity,
                    order_id=str(order.id)
                )
                
                if not reserved:
                    raise ValidationError(f'商品 {product_id} 库存不足')
                
                # 创建订单项
                product = reserved['product']  # 库存服务返回商品信息
                OrderItem.objects.create(
                    order=order,
                    product=product,
                    product_name=product.name,
                    unit_price=product.price,
                    quantity=quantity
                )
            
            # 3. 计算总价
            order.calculate_total()
            
            return order
            
        except Exception as e:
            # 回滚库存(Celery 异步确保最终一致性)
            self.inventory_service.cancel_reservation(order_id=str(order.id))
            order.delete()
            raise
    
    @transaction.atomic
    def pay_order(self, order: Order, payment_amount: float) -> Order:
        """支付订单"""
        if order.status != OrderStatus.CREATED:
            raise ValidationError('订单状态不允许支付')
        
        if float(order.total_amount) != payment_amount:
            raise ValidationError('支付金额不匹配')
        
        # 更新状态
        order.status = OrderStatus.PAID
        order.paid_at = timezone.now()
        order.save()
        
        # 确认库存扣减(异步)
        self.inventory_service.confirm_reservation.delay(
            order_id=str(order.id)
        )
        
        # 触发发货流程(异步)
        from .tasks import initiate_shipment
        initiate_shipment.delay(str(order.id))
        
        return order


# ============== Serializers ==============

# apps/orders/serializers.py
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_field
from .models import Order, OrderItem

class OrderItemSerializer(serializers.ModelSerializer):
    """订单项序列化器"""
    product_id = serializers.UUIDField(source='product.id', read_only=True)
    
    class Meta:
        model = OrderItem
        fields = ['id', 'product_id', 'product_name', 'unit_price', 'quantity', 'subtotal']
        read_only_fields = fields


class CartItemSerializer(serializers.Serializer):
    """购物车项(创建订单输入)"""
    product_id = serializers.UUIDField()
    quantity = serializers.IntegerField(min_value=1)


class OrderCreateSerializer(serializers.ModelSerializer):
    """创建订单序列化器"""
    cart_items = CartItemSerializer(many=True, write_only=True)
    
    class Meta:
        model = Order
        fields = ['id', 'cart_items', 'total_amount', 'status', 'created_at']
        read_only_fields = ['id', 'total_amount', 'status', 'created_at']
    
    def validate_cart_items(self, value):
        """验证购物车非空"""
        if not value:
            raise serializers.ValidationError('购物车不能为空')
        return value
    
    def create(self, validated_data):
        """委托给 Service 层"""
        from .services import OrderService
        service = OrderService()
        
        cart_items = validated_data.pop('cart_items')
        user = self.context['request'].user
        
        return service.create_order(user, cart_items)


class OrderListSerializer(serializers.ModelSerializer):
    """订单列表(精简)"""
    item_count = serializers.SerializerMethodField()
    
    class Meta:
        model = Order
        fields = ['id', 'status', 'total_amount', 'item_count', 'created_at']
    
    @extend_schema_field(serializers.IntegerField())
    def get_item_count(self, obj: Order) -> int:
        return obj.items.count()


class OrderDetailSerializer(serializers.ModelSerializer):
    """订单详情(完整)"""
    items = OrderItemSerializer(many=True, read_only=True)
    user_email = serializers.EmailField(source='user.email', read_only=True)
    
    class Meta:
        model = Order
        fields = [
            'id', 'user_email', 'status', 'total_amount', 'currency',
            'items', 'created_at', 'updated_at', 'paid_at'
        ]


# ============== Views ==============

# apps/orders/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from drf_spectacular.utils import extend_schema, OpenApiParameter
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from .models import Order, OrderStatus
from .serializers import (
    OrderCreateSerializer,
    OrderListSerializer,
    OrderDetailSerializer
)
from .services import OrderService


class OrderViewSet(viewsets.ModelViewSet):
    """
    订单管理 API
    
    提供订单的创建、查询、支付等操作
    """
    permission_classes = [IsAuthenticated]
    lookup_field = 'pk'
    
    def get_queryset(self):
        """用户只能查看自己的订单"""
        queryset = Order.objects.filter(user=self.request.user)
        
        # 筛选
        status = self.request.query_params.get('status')
        if status:
            queryset = queryset.filter(status=status)
        
        # 日期范围
        date_from = self.request.query_params.get('from')
        date_to = self.request.query_params.get('to')
        if date_from:
            queryset = queryset.filter(created_at__gte=date_from)
        if date_to:
            queryset = queryset.filter(created_at__lte=date_to)
        
        return queryset.select_related('user').prefetch_related('items')
    
    def get_serializer_class(self):
        """根据动作选择序列化器"""
        if self.action == 'create':
            return OrderCreateSerializer
        elif self.action == 'retrieve':
            return OrderDetailSerializer
        return OrderListSerializer
    
    @extend_schema(
        summary='创建订单',
        description='从购物车创建订单,自动检查库存',
        request=OrderCreateSerializer,
        responses={201: OrderDetailSerializer}
    )
    def create(self, request, *args, **kwargs):
        """创建订单(重写以使用自定义 Service)"""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        order = serializer.save()
        
        # 使用详情序列化器返回
        detail_serializer = OrderDetailSerializer(order)
        return Response(detail_serializer.data, status=status.HTTP_201_CREATED)
    
    @extend_schema(
        summary='支付订单',
        description='模拟支付回调,推进订单状态',
        request={'application/json': {'payment_amount': 'number'}},
        responses={200: OrderDetailSerializer}
    )
    @action(detail=True, methods=['post'])
    def pay(self, request, pk=None):
        """支付订单"""
        order = self.get_object()
        payment_amount = request.data.get('payment_amount')
        
        if payment_amount is None:
            return Response(
                {'error': '缺少 payment_amount'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        service = OrderService()
        try:
            paid_order = service.pay_order(order, float(payment_amount))
            serializer = OrderDetailSerializer(paid_order)
            return Response(serializer.data)
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
    
    @method_decorator(cache_page(60))  # 缓存1分钟
    @extend_schema(
        summary='订单统计',
        description='获取用户订单统计数据'
    )
    @action(detail=False, methods=['get'])
    def statistics(self, request):
        """订单统计"""
        stats = Order.objects.filter(user=request.user).aggregate(
            total_orders=models.Count('id'),
            total_spent=models.Sum('total_amount'),
            pending_payment=models.Count('id', filter=models.Q(status=OrderStatus.CREATED))
        )
        return Response(stats)


# ============== Celery Tasks ==============

# apps/orders/tasks.py
from celery import shared_task
from django.core.mail import send_mail
from .models import Order

@shared_task(bind=True, max_retries=3)
def notify_order_created(self, order_id: str, user_id: str):
    """异步通知订单创建"""
    try:
        order = Order.objects.get(id=order_id)
        # 发送邮件/短信/推送...
        print(f'通知用户 {user_id} 订单 {order_id} 已创建')
    except Order.DoesNotExist:
        # 重试
        raise self.retry(countdown=5)


@shared_task
def initiate_shipment(order_id: str):
    """触发发货流程"""
    # 调用物流系统 API...
    print(f'开始处理订单 {order_id} 发货')


# ============== Tests ==============

# apps/orders/tests.py
import pytest
from django.test import TestCase
from django.core.exceptions import ValidationError
from rest_framework.test import APIClient
from rest_framework import status
import factory
from .models import Order, OrderItem, OrderStatus
from apps.products.models import Product
from apps.users.models import User

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User
    email = factory.Sequence(lambda n: f'user{n}@test.com')

class ProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Product
    name = factory.Sequence(lambda n: f'Product {n}')
    price = 99.99
    stock = 100

class OrderTestCase(TestCase):
    """订单业务测试"""
    
    def setUp(self):
        self.user = UserFactory()
        self.product = ProductFactory(stock=10)
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)
    
    def test_create_order_success(self):
        """正常创建订单"""
        response = self.client.post('/api/orders/', {
            'cart_items': [{
                'product_id': str(self.product.id),
                'quantity': 2
            }]
        }, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['status'], 'created')
        self.assertEqual(float(response.data['total_amount']), 199.98)
        
        # 验证库存预占
        self.product.refresh_from_db()
        self.assertEqual(self.product.stock, 8)  # 10 - 2
    
    def test_create_order_insufficient_stock(self):
        """库存不足"""
        response = self.client.post('/api/orders/', {
            'cart_items': [{
                'product_id': str(self.product.id),
                'quantity': 999
            }]
        }, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    
    def test_pay_order_success(self):
        """正常支付"""
        # 先创建订单
        order_response = self.client.post('/api/orders/', {
            'cart_items': [{'product_id': str(self.product.id), 'quantity': 1}]
        }, format='json')
        order_id = order_response.data['id']
        
        # 支付
        pay_response = self.client.post(
            f'/api/orders/{order_id}/pay/',
            {'payment_amount': 99.99},
            format='json'
        )
        
        self.assertEqual(pay_response.status_code, status.HTTP_200_OK)
        self.assertEqual(pay_response.data['status'], 'paid')

4.4 Django 生成质量评估

维度 评分 说明
开发速度 10/10 约定优于配置,代码量最少(150+ 行)
功能完整 9/10 ORM + Admin + 缓存 + 异步一站式
可维护性 8/10 强约定降低自由度,但团队协作友好
性能优化 7/10 ORM 隐藏查询细节,需手动优化 N+1
类型安全 6/10 Python 动态类型,依赖运行时检查
扩展性 7/10 单体友好,微服务拆分成本较高

五、三大框架横评总结

维度 Spring Boot Express/TS Django
代码量 300+ 行 200+ 行 150+ 行
生成速度 慢(需处理大量样板) 快(约定驱动)
类型安全 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐☆☆
架构灵活 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐☆☆
企业级功能 ⭐⭐⭐⭐⭐ ⭐⭐⭐☆☆ ⭐⭐⭐⭐☆
团队约束 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐☆ ⭐⭐⭐⭐⭐
AI 可控性 ⭐⭐⭐⭐☆ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

选择策略

企业级复杂系统(金融、电信)→ Spring Boot
    └── 严格分层、事务、监控、合规

现代全栈应用(SaaS、API 服务)→ Express + TypeScript
    └── 快速迭代、类型安全、前后端统一语言

快速 MVP、内容驱动产品 → Django
    └── 极速交付、内置功能丰富、维护成本低

六、通用最佳实践:跨框架的 AI 生成策略

1. 分层生成(降低单次复杂度)

Step 1: 生成领域模型(实体 + 值对象 + 领域事件)
    ↓ 人工审查:业务规则是否正确
Step 2: 生成应用服务(用例编排)
    ↓ 人工审查:事务边界、依赖方向
Step 3: 生成基础设施(仓储、外部服务)
    ↓ 人工审查:技术选型、性能陷阱
Step 4: 生成接口层(API + 测试)
    ↓ 人工审查:安全、文档、边界测试

2. 约束即代码(强制规范)

# 架构测试(ArchUnit / 自定义脚本)
def test_layer_dependencies():
    """禁止跨层调用"""
    # 领域层不依赖其他层
    assert not imports_from("domain", "application")
    assert not imports_from("domain", "infrastructure")
    
    # 应用层仅依赖领域层
    assert not imports_from("application", "infrastructure")

3. 契约驱动(API 优先)

# OpenAPI 规范(人工定义)
openapi: 3.0.0
paths:
  /orders:
    post:
      summary: 创建订单
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        201:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'

# Codex 任务:实现此契约,禁止偏离

七、一句话总结

框架选择决定下限,AI 约束决定上限。Spring 适合"规范重"的企业,Express 适合"灵活快"的初创,Django 适合"交付快"的 MVP——但无论选哪个,给 Codex 的约束越清晰,生成的代码越生产级。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐