重构 Controller 的 7 个黄金法则

技术小馆专注AI与Java领域的前沿技术知识库 点击进入

重构 Controller 的 7 个黄金法则

你是否曾经面对过一个臃肿不堪的 Controller?方法堆积如山,业务逻辑混乱,测试困难,维护噩梦。当新需求来临时,你不得不在这个"巨无霸"中继续添加代码,让问题雪上加霜。一个优雅的 Controller 应该是轻量级的、职责单一的、易于测试和维护的。它就像一位优雅的舞者,动作简洁而有力,每个转身都恰到好处。

1. Controller 设计的核心原则

单一职责原则(SRP)

一个 Controller 应该只负责处理 HTTP 请求和响应,而不是承载业务逻辑。让我们看一个反例:

less 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @PostMapping("/register")
    public ResponseEntity<?> registerUser(@RequestBody UserRegistrationDto dto) {
        // 验证邮箱格式
        if (!isValidEmail(dto.getEmail())) {
            return ResponseEntity.badRequest().body("Invalid email format");
        }
        
        // 检查密码强度
        if (!isPasswordStrong(dto.getPassword())) {
            return ResponseEntity.badRequest().body("Password too weak");
        }
        
        // 检查用户是否已存在
        if (userRepository.existsByEmail(dto.getEmail())) {
            return ResponseEntity.badRequest().body("User already exists");
        }
        
        // 加密密码
        String encryptedPassword = passwordEncoder.encode(dto.getPassword());
        
        // 创建用户
        User user = new User();
        user.setEmail(dto.getEmail());
        user.setPassword(encryptedPassword);
        user.setName(dto.getName());
        
        // 保存用户
        User savedUser = userRepository.save(user);
        
        // 发送欢迎邮件
        emailService.sendWelcomeEmail(savedUser.getEmail());
        
        return ResponseEntity.ok(savedUser);
    }
    
    // 其他验证方法...
}

这个 Controller 违反了单一职责原则,包含了太多业务逻辑。重构后应该是这样:

less 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @PostMapping("/register")
    public ResponseEntity<?> registerUser(@RequestBody UserRegistrationDto dto) {
        try {
            User user = userService.registerUser(dto);
            return ResponseEntity.ok(user);
        } catch (UserRegistrationException e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }
}

依赖注入与解耦

通过构造函数注入依赖,而不是直接实例化,这样便于测试和替换实现:

kotlin 复制代码
@Service
public class UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final EmailService emailService;
    private final UserValidator userValidator;
    
    public UserService(UserRepository userRepository, 
                      PasswordEncoder passwordEncoder,
                      EmailService emailService,
                      UserValidator userValidator) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.emailService = emailService;
        this.userValidator = userValidator;
    }
}

接口隔离原则

为不同的功能定义专门的接口,避免一个接口承担过多职责:

csharp 复制代码
public interface UserRegistrationService {
    User registerUser(UserRegistrationDto dto);
}

public interface UserQueryService {
    User findById(Long id);
    List<User> findAll();
    User findByEmail(String email);
}

public interface UserUpdateService {
    User updateUser(Long id, UserUpdateDto dto);
    void deleteUser(Long id);
}

2. 代码结构优化策略

方法提取与重构

将长方法拆分为多个小方法,每个方法只做一件事:

scss 复制代码
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @PostMapping
    public ResponseEntity<?> createOrder(@RequestBody CreateOrderDto dto) {
        // 验证请求
        validateCreateOrderRequest(dto);
        
        // 检查库存
        checkInventoryAvailability(dto);
        
        // 创建订单
        Order order = createOrderFromDto(dto);
        
        // 保存订单
        Order savedOrder = orderService.save(order);
        
        // 发送确认邮件
        sendOrderConfirmation(savedOrder);
        
        return ResponseEntity.ok(savedOrder);
    }
    
    private void validateCreateOrderRequest(CreateOrderDto dto) {
        if (dto.getItems() == null || dto.getItems().isEmpty()) {
            throw new InvalidOrderException("Order must contain at least one item");
        }
        // 其他验证逻辑...
    }
    
    private void checkInventoryAvailability(CreateOrderDto dto) {
        for (OrderItemDto item : dto.getItems()) {
            if (!inventoryService.isAvailable(item.getProductId(), item.getQuantity())) {
                throw new InsufficientInventoryException("Insufficient inventory for product: " + item.getProductId());
            }
        }
    }
    
    private Order createOrderFromDto(CreateOrderDto dto) {
        Order order = new Order();
        order.setCustomerId(dto.getCustomerId());
        order.setItems(dto.getItems().stream()
                .map(this::createOrderItem)
                .collect(Collectors.toList()));
        order.setTotalAmount(calculateTotal(dto.getItems()));
        return order;
    }
}

参数验证与异常处理

使用统一的异常处理机制和参数验证:

less 复制代码
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @PostMapping
    public ResponseEntity<?> createProduct(@Valid @RequestBody CreateProductDto dto) {
        Product product = productService.createProduct(dto);
        return ResponseEntity.ok(product);
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<?> updateProduct(@PathVariable Long id, 
                                        @Valid @RequestBody UpdateProductDto dto) {
        try {
            Product product = productService.updateProduct(id, dto);
            return ResponseEntity.ok(product);
        } catch (ProductNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

// 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleValidationErrors(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());
        
        return ResponseEntity.badRequest()
                .body(new ErrorResponse("Validation failed", errors));
    }
    
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<?> handleBusinessException(BusinessException ex) {
        return ResponseEntity.badRequest()
                .body(new ErrorResponse(ex.getMessage()));
    }
}

响应格式标准化

定义统一的响应格式,提高 API 的一致性:

typescript 复制代码
public class ApiResponse<T> {
    private boolean success;
    private String message;
    private T data;
    private long timestamp;
    
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(true);
        response.setData(data);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
    
    public static <T> ApiResponse<T> error(String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(false);
        response.setMessage(message);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
}

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<User>> getUser(@PathVariable Long id) {
        try {
            User user = userService.findById(id);
            return ResponseEntity.ok(ApiResponse.success(user));
        } catch (UserNotFoundException e) {
            return ResponseEntity.ok(ApiResponse.error("User not found"));
        }
    }
}

3. 业务逻辑分离技巧

Service 层职责划分

将业务逻辑从 Controller 中提取到 Service 层:

scss 复制代码
@Service
@Transactional
public class OrderService {
    
    private final OrderRepository orderRepository;
    private final ProductService productService;
    private final CustomerService customerService;
    private final PaymentService paymentService;
    
    public Order createOrder(CreateOrderDto dto) {
        // 验证客户
        Customer customer = customerService.findById(dto.getCustomerId());
        
        // 验证产品
        List<Product> products = validateProducts(dto.getItems());
        
        // 计算价格
        BigDecimal totalAmount = calculateTotalAmount(dto.getItems());
        
        // 创建订单
        Order order = new Order();
        order.setCustomer(customer);
        order.setItems(createOrderItems(dto.getItems()));
        order.setTotalAmount(totalAmount);
        order.setStatus(OrderStatus.PENDING);
        
        // 保存订单
        Order savedOrder = orderRepository.save(order);
        
        // 处理支付
        paymentService.processPayment(savedOrder);
        
        return savedOrder;
    }
    
    private List<Product> validateProducts(List<OrderItemDto> items) {
        return items.stream()
                .map(item -> productService.findById(item.getProductId()))
                .collect(Collectors.toList());
    }
    
    private BigDecimal calculateTotalAmount(List<OrderItemDto> items) {
        return items.stream()
                .map(item -> {
                    Product product = productService.findById(item.getProductId());
                    return product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()));
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

数据转换与映射

使用 DTO 和 Mapper 来处理数据传输:

scss 复制代码
public class UserMapper {
    
    public static UserDto toDto(User user) {
        UserDto dto = new UserDto();
        dto.setId(user.getId());
        dto.setEmail(user.getEmail());
        dto.setName(user.getName());
        dto.setCreatedAt(user.getCreatedAt());
        return dto;
    }
    
    public static User toEntity(CreateUserDto dto) {
        User user = new User();
        user.setEmail(dto.getEmail());
        user.setName(dto.getName());
        user.setCreatedAt(LocalDateTime.now());
        return user;
    }
    
    public static List<UserDto> toDtoList(List<User> users) {
        return users.stream()
                .map(UserMapper::toDto)
                .collect(Collectors.toList());
    }
}

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping
    public ResponseEntity<ApiResponse<List<UserDto>>> getAllUsers() {
        List<User> users = userService.findAll();
        List<UserDto> userDtos = UserMapper.toDtoList(users);
        return ResponseEntity.ok(ApiResponse.success(userDtos));
    }
}

事务管理策略

在 Service 层使用事务注解,确保数据一致性:

scss 复制代码
@Service
@Transactional
public class OrderService {
    
    @Transactional(readOnly = true)
    public Order findById(Long id) {
        return orderRepository.findById(id)
                .orElseThrow(() -> new OrderNotFoundException("Order not found: " + id));
    }
    
    @Transactional
    public Order createOrder(CreateOrderDto dto) {
        // 创建订单逻辑
        Order order = buildOrder(dto);
        
        // 保存订单
        Order savedOrder = orderRepository.save(order);
        
        // 更新库存
        updateInventory(dto.getItems());
        
        // 发送通知
        notificationService.sendOrderCreatedNotification(savedOrder);
        
        return savedOrder;
    }
    
    @Transactional
    public void cancelOrder(Long orderId) {
        Order order = findById(orderId);
        
        if (order.getStatus() != OrderStatus.PENDING) {
            throw new InvalidOrderStatusException("Cannot cancel order in status: " + order.getStatus());
        }
        
        order.setStatus(OrderStatus.CANCELLED);
        orderRepository.save(order);
        
        // 恢复库存
        restoreInventory(order.getItems());
        
        // 发送取消通知
        notificationService.sendOrderCancelledNotification(order);
    }
}

4. 测试友好性设计

单元测试编写

为 Controller 编写单元测试,确保每个方法都能正确工作:

less 复制代码
@ExtendWith(MockitoExtension.class)
class UserControllerTest {
    
    @Mock
    private UserService userService;
    
    @Mock
    private UserValidator userValidator;
    
    @InjectMocks
    private UserController userController;
    
    @Test
    void createUser_WithValidData_ReturnsCreatedUser() {
        // Arrange
        CreateUserDto dto = new CreateUserDto();
        dto.setEmail("test@example.com");
        dto.setName("Test User");
        
        User user = new User();
        user.setId(1L);
        user.setEmail(dto.getEmail());
        user.setName(dto.getName());
        
        when(userService.createUser(dto)).thenReturn(user);
        
        // Act
        ResponseEntity<ApiResponse<User>> response = userController.createUser(dto);
        
        // Assert
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody().isSuccess()).isTrue();
        assertThat(response.getBody().getData().getEmail()).isEqualTo(dto.getEmail());
        
        verify(userService).createUser(dto);
    }
    
    @Test
    void createUser_WithInvalidData_ReturnsBadRequest() {
        // Arrange
        CreateUserDto dto = new CreateUserDto();
        dto.setEmail("invalid-email");
        
        when(userService.createUser(dto))
                .thenThrow(new ValidationException("Invalid email format"));
        
        // Act
        ResponseEntity<ApiResponse<User>> response = userController.createUser(dto);
        
        // Assert
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody().isSuccess()).isFalse();
        assertThat(response.getBody().getMessage()).contains("Invalid email format");
    }
}

Mock 策略选择

选择合适的 Mock 策略来隔离依赖:

less 复制代码
@WebMvcTest(UserController.class)
class UserControllerIntegrationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void getUser_WithValidId_ReturnsUser() throws Exception {
        // Arrange
        User user = new User();
        user.setId(1L);
        user.setEmail("test@example.com");
        user.setName("Test User");
        
        when(userService.findById(1L)).thenReturn(user);
        
        // Act & Assert
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.email").value("test@example.com"))
                .andExpect(jsonPath("$.data.name").value("Test User"));
    }
    
    @Test
    void getUser_WithInvalidId_ReturnsNotFound() throws Exception {
        // Arrange
        when(userService.findById(999L))
                .thenThrow(new UserNotFoundException("User not found"));
        
        // Act & Assert
        mockMvc.perform(get("/api/users/999"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(false))
                .andExpect(jsonPath("$.message").value("User not found"));
    }
}

集成测试设计

设计端到端的集成测试,验证整个流程:

less 复制代码
@SpringBootTest
@AutoConfigureTestDatabase
@Transactional
class UserControllerIntegrationTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void createAndRetrieveUser_CompleteFlow_Success() {
        // Arrange
        CreateUserDto createDto = new CreateUserDto();
        createDto.setEmail("integration@example.com");
        createDto.setName("Integration User");
        
        // Act - Create user
        ResponseEntity<ApiResponse> createResponse = restTemplate.postForEntity(
                "/api/users", createDto, ApiResponse.class);
        
        // Assert - User created
        assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(createResponse.getBody().isSuccess()).isTrue();
        
        // Act - Retrieve user
        ResponseEntity<ApiResponse> getResponse = restTemplate.getForEntity(
                "/api/users/1", ApiResponse.class);
        
        // Assert - User retrieved
        assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(getResponse.getBody().isSuccess()).isTrue();
        
        // Verify database
        User savedUser = userRepository.findById(1L).orElse(null);
        assertThat(savedUser).isNotNull();
        assertThat(savedUser.getEmail()).isEqualTo("integration@example.com");
    }
}

5. 性能优化与缓存

响应时间优化

通过异步处理和缓存来提升响应速度:

less 复制代码
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @GetMapping("/{id}")
    @Cacheable(value = "products", key = "#id")
    public ResponseEntity<ApiResponse<Product>> getProduct(@PathVariable Long id) {
        Product product = productService.findById(id);
        return ResponseEntity.ok(ApiResponse.success(product));
    }
    
    @GetMapping("/search")
    public ResponseEntity<ApiResponse<List<Product>>> searchProducts(
            @RequestParam String keyword,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        
        Pageable pageable = PageRequest.of(page, size);
        Page<Product> products = productService.searchByKeyword(keyword, pageable);
        
        return ResponseEntity.ok(ApiResponse.success(products.getContent()));
    }
    
    @PostMapping("/{id}/view")
    @Async
    public void incrementViewCount(@PathVariable Long id) {
        productService.incrementViewCount(id);
    }
}

@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        return productRepository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException("Product not found: " + id));
    }
    
    @CacheEvict(value = "products", key = "#product.id")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
    
    @Async
    public void incrementViewCount(Long productId) {
        productRepository.incrementViewCount(productId);
    }
}

缓存策略实施

实现多层次的缓存策略:

typescript 复制代码
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        
        ConcurrentMapCache productsCache = new ConcurrentMapCache("products");
        ConcurrentMapCache usersCache = new ConcurrentMapCache("users");
        ConcurrentMapCache ordersCache = new ConcurrentMapCache("orders");
        
        cacheManager.setCaches(Arrays.asList(productsCache, usersCache, ordersCache));
        return cacheManager;
    }
}

@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#id", unless = "#result == null")
    public Product findById(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    @Cacheable(value = "products", key = "'search_' + #keyword + '_' + #pageable.pageNumber + '_' + #pageable.pageSize")
    public Page<Product> searchByKeyword(String keyword, Pageable pageable) {
        return productRepository.findByNameContainingIgnoreCase(keyword, pageable);
    }
    
    @CacheEvict(value = "products", allEntries = true)
    public void clearAllCaches() {
        // 清除所有产品相关缓存
    }
}

异步处理机制

使用异步处理来处理耗时操作:

scss 复制代码
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @PostMapping("/{id}/process")
    public ResponseEntity<ApiResponse<String>> processOrder(@PathVariable Long id) {
        // 立即返回处理中的状态
        orderService.processOrderAsync(id);
        
        return ResponseEntity.ok(ApiResponse.success("Order processing started"));
    }
    
    @GetMapping("/{id}/status")
    public ResponseEntity<ApiResponse<OrderStatus>> getOrderStatus(@PathVariable Long id) {
        OrderStatus status = orderService.getOrderStatus(id);
        return ResponseEntity.ok(ApiResponse.success(status));
    }
}

@Service
public class OrderService {
    
    @Async
    public void processOrderAsync(Long orderId) {
        try {
            // 模拟耗时处理
            Thread.sleep(5000);
            
            // 更新订单状态
            Order order = findById(orderId);
            order.setStatus(OrderStatus.PROCESSING);
            orderRepository.save(order);
            
            // 继续处理...
            processPayment(order);
            updateInventory(order);
            sendNotifications(order);
            
            order.setStatus(OrderStatus.COMPLETED);
            orderRepository.save(order);
            
        } catch (Exception e) {
            // 处理异常,更新订单状态为失败
            Order order = findById(orderId);
            order.setStatus(OrderStatus.FAILED);
            orderRepository.save(order);
            
            log.error("Failed to process order: " + orderId, e);
        }
    }
}

6. 代码规范与文档

命名规范制定

建立清晰的命名约定:

less 复制代码
// Controller 命名:以 Controller 结尾,使用复数形式
@RestController
@RequestMapping("/api/users")
public class UserController { }

@RestController
@RequestMapping("/api/orders")
public class OrderController { }

// 方法命名:使用动词开头,清晰表达意图
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUserById(@PathVariable Long id) { }

@PostMapping
public ResponseEntity<ApiResponse<User>> createNewUser(@RequestBody CreateUserDto dto) { }

@PutMapping("/{id}")
public ResponseEntity<ApiResponse<User>> updateExistingUser(@PathVariable Long id, 
                                                         @RequestBody UpdateUserDto dto) { }

@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> removeUser(@PathVariable Long id) { }

// DTO 命名:以 Dto 结尾,使用描述性名称
public class CreateUserDto { }
public class UpdateUserDto { }
public class UserResponseDto { }

// 异常命名:以 Exception 结尾
public class UserNotFoundException extends RuntimeException { }
public class InvalidUserDataException extends RuntimeException { }

API 文档生成

使用 Swagger 或 OpenAPI 生成 API 文档:

less 复制代码
@RestController
@RequestMapping("/api/users")
@Tag(name = "User Management", description = "APIs for managing users")
public class UserController {
    
    @Operation(summary = "Create a new user", description = "Creates a new user with the provided information")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "User created successfully",
                    content = @Content(schema = @Schema(implementation = User.class))),
        @ApiResponse(responseCode = "400", description = "Invalid input data"),
        @ApiResponse(responseCode = "409", description = "User already exists")
    })
    @PostMapping
    public ResponseEntity<ApiResponse<User>> createUser(
            @Parameter(description = "User information", required = true)
            @Valid @RequestBody CreateUserDto dto) {
        
        User user = userService.createUser(dto);
        return ResponseEntity.ok(ApiResponse.success(user));
    }
    
    @Operation(summary = "Get user by ID", description = "Retrieves a user by their unique identifier")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "User found successfully"),
        @ApiResponse(responseCode = "404", description = "User not found")
    })
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<User>> getUserById(
            @Parameter(description = "User ID", required = true)
            @PathVariable Long id) {
        
        User user = userService.findById(id);
        return ResponseEntity.ok(ApiResponse.success(user));
    }
}

代码审查要点

建立代码审查检查清单:

less 复制代码
// 代码审查检查清单示例

/*
□ Controller 是否只负责 HTTP 请求处理?
□ 业务逻辑是否已提取到 Service 层?
□ 是否使用了适当的 HTTP 状态码?
□ 异常处理是否统一?
□ 参数验证是否完整?
□ 响应格式是否一致?
□ 是否添加了适当的日志?
□ 方法是否过于复杂(超过 20 行)?
□ 是否遵循了命名约定?
□ 是否添加了必要的注释?
□ 是否考虑了性能问题?
□ 是否添加了单元测试?
*/

@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
    
    @PostMapping
    public ResponseEntity<ApiResponse<User>> createUser(@Valid @RequestBody CreateUserDto dto) {
        log.info("Creating new user with email: {}", dto.getEmail());
        
        try {
            User user = userService.createUser(dto);
            log.info("User created successfully with ID: {}", user.getId());
            
            return ResponseEntity.ok(ApiResponse.success(user));
            
        } catch (UserAlreadyExistsException e) {
            log.warn("Failed to create user: {}", e.getMessage());
            return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
            
        } catch (Exception e) {
            log.error("Unexpected error while creating user", e);
            return ResponseEntity.ok(ApiResponse.error("Internal server error"));
        }
    }
}

7. 重构实战案例

遗留代码分析

分析一个典型的"巨无霸" Controller:

scss 复制代码
// 重构前的混乱 Controller
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private EmailService emailService;
    
    @Autowired
    private PaymentService paymentService;
    
    @PostMapping
    public ResponseEntity<?> createOrder(@RequestBody Map<String, Object> request) {
        try {
            // 验证用户
            Long userId = Long.valueOf(request.get("userId").toString());
            User user = userRepository.findById(userId).orElse(null);
            if (user == null) {
                return ResponseEntity.badRequest().body("User not found");
            }
            
            // 验证产品
            List<Map<String, Object>> items = (List<Map<String, Object>>) request.get("items");
            List<OrderItem> orderItems = new ArrayList<>();
            BigDecimal totalAmount = BigDecimal.ZERO;
            
            for (Map<String, Object> item : items) {
                Long productId = Long.valueOf(item.get("productId").toString());
                Integer quantity = Integer.valueOf(item.get("quantity").toString());
                
                Product product = productRepository.findById(productId).orElse(null);
                if (product == null) {
                    return ResponseEntity.badRequest().body("Product not found: " + productId);
                }
                
                if (product.getStock() < quantity) {
                    return ResponseEntity.badRequest().body("Insufficient stock for product: " + productId);
                }
                
                OrderItem orderItem = new OrderItem();
                orderItem.setProduct(product);
                orderItem.setQuantity(quantity);
                orderItem.setPrice(product.getPrice());
                orderItems.add(orderItem);
                
                totalAmount = totalAmount.add(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
                
                // 更新库存
                product.setStock(product.getStock() - quantity);
                productRepository.save(product);
            }
            
            // 创建订单
            Order order = new Order();
            order.setUser(user);
            order.setItems(orderItems);
            order.setTotalAmount(totalAmount);
            order.setStatus("PENDING");
            order.setCreatedAt(new Date());
            
            Order savedOrder = orderRepository.save(order);
            
            // 处理支付
            String paymentResult = paymentService.processPayment(savedOrder);
            if (!"SUCCESS".equals(paymentResult)) {
                // 回滚库存
                for (OrderItem item : orderItems) {
                    Product product = item.getProduct();
                    product.setStock(product.getStock() + item.getQuantity());
                    productRepository.save(product);
                }
                orderRepository.delete(savedOrder);
                return ResponseEntity.badRequest().body("Payment failed");
            }
            
            // 发送确认邮件
            emailService.sendOrderConfirmation(user.getEmail(), savedOrder);
            
            return ResponseEntity.ok(savedOrder);
            
        } catch (Exception e) {
            return ResponseEntity.status(500).body("Internal server error: " + e.getMessage());
        }
    }
    
    // 其他混乱的方法...
}

重构计划制定

制定分步骤的重构计划:

less 复制代码
// 第一步:提取 DTO 和验证
public class CreateOrderDto {
    @NotNull
    private Long userId;
    
    @NotEmpty
    @Valid
    private List<OrderItemDto> items;
    
    // getters and setters
}

public class OrderItemDto {
    @NotNull
    private Long productId;
    
    @Min(1)
    private Integer quantity;
    
    // getters and setters
}

// 第二步:创建专门的 Service
@Service
@Transactional
public class OrderService {
    
    private final OrderRepository orderRepository;
    private final ProductService productService;
    private final UserService userService;
    private final PaymentService paymentService;
    private final EmailService emailService;
    
    public Order createOrder(CreateOrderDto dto) {
        // 验证用户
        User user = userService.findById(dto.getUserId());
        
        // 验证产品并创建订单项
        List<OrderItem> orderItems = createOrderItems(dto.getItems());
        
        // 计算总金额
        BigDecimal totalAmount = calculateTotalAmount(orderItems);
        
        // 创建订单
        Order order = buildOrder(user, orderItems, totalAmount);
        
        // 保存订单
        Order savedOrder = orderRepository.save(order);
        
        // 处理支付
        processPayment(savedOrder);
        
        // 发送确认邮件
        emailService.sendOrderConfirmation(user.getEmail(), savedOrder);
        
        return savedOrder;
    }
    
    private List<OrderItem> createOrderItems(List<OrderItemDto> itemDtos) {
        return itemDtos.stream()
                .map(this::createOrderItem)
                .collect(Collectors.toList());
    }
    
    private OrderItem createOrderItem(OrderItemDto dto) {
        Product product = productService.findById(dto.getProductId());
        productService.checkStockAvailability(product, dto.getQuantity());
        
        OrderItem item = new OrderItem();
        item.setProduct(product);
        item.setQuantity(dto.getQuantity());
        item.setPrice(product.getPrice());
        
        return item;
    }
}

// 第三步:重构 Controller
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    private final OrderService orderService;
    
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
    
    @PostMapping
    public ResponseEntity<ApiResponse<Order>> createOrder(@Valid @RequestBody CreateOrderDto dto) {
        try {
            Order order = orderService.createOrder(dto);
            return ResponseEntity.ok(ApiResponse.success(order));
        } catch (BusinessException e) {
            return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
        }
    }
}

重构后效果评估

评估重构的效果和改进:

scss 复制代码
// 重构后的效果对比

/*
重构前的问题:
- Controller 方法超过 80 行
- 混合了业务逻辑、数据访问、验证等职责
- 异常处理不统一
- 难以测试和维护
- 代码重复严重

重构后的改进:
- Controller 方法只有 10 行左右
- 职责分离清晰
- 统一的异常处理
- 易于测试和维护
- 代码复用性高
- 符合 SOLID 原则
*/

// 重构后的测试覆盖
@ExtendWith(MockitoExtension.class)
class OrderControllerTest {
    
    @Mock
    private OrderService orderService;
    
    @InjectMocks
    private OrderController orderController;
    
    @Test
    void createOrder_WithValidData_ReturnsSuccess() {
        // Arrange
        CreateOrderDto dto = new CreateOrderDto();
        dto.setUserId(1L);
        dto.setItems(Arrays.asList(new OrderItemDto(1L, 2)));
        
        Order order = new Order();
        order.setId(1L);
        
        when(orderService.createOrder(dto)).thenReturn(order);
        
        // Act
        ResponseEntity<ApiResponse<Order>> response = orderController.createOrder(dto);
        
        // Assert
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody().isSuccess()).isTrue();
        assertThat(response.getBody().getData().getId()).isEqualTo(1L);
    }
}

// 重构后的性能指标
/*
- 响应时间:从平均 500ms 降低到 200ms
- 代码行数:从 200+ 行减少到 50 行
- 测试覆盖率:从 30% 提升到 85%
- 维护成本:降低 60%
- 代码质量评分:从 C 提升到 A
*/
相关推荐
渣哥4 分钟前
Kafka消息丢失的3种场景,生产环境千万要注意
java
渣哥4 分钟前
ElasticSearch深度分页的致命缺陷,千万数据查询秒变蜗牛
java
Olrookie5 分钟前
XXL-JOB GLUE模式动态数据源实践:Spring AOP + MyBatis 解耦多库查询
java·数据库·spring boot
用户48221371677510 分钟前
C++——访问控制
后端
语落心生19 分钟前
数控技术:数控系统刀补功能的软件实现及其仿真
后端
柯南二号22 分钟前
【Java后端】MyBatis-Plus 原理解析
java·开发语言·mybatis
又是努力搬砖的一年30 分钟前
SpringBoot中,接口加解密
java·spring boot·后端
:-)33 分钟前
idea配置maven国内镜像
java·ide·maven·intellij-idea
0wioiw040 分钟前
Python基础(Flask①)
后端·python·flask
张元清1 小时前
电商 Feeds 流缓存策略:Temu vs 拼多多的技术选择
前端·javascript·面试