从模糊需求到清晰代码的完整路径
“这个需求你来负责——用户下单后30分钟未支付要自动关闭订单,释放库存。”
当我还是初级程序员时,面对这样的需求总是直接开写代码。结果要么是不断返工,要么是代码臃肿难以维护。直到经历过几个大项目后,我才明白:比写代码更重要的,是如何思考代码。
第一步:需求澄清——5W1H分析法
1.1 不要急着写代码,先问对问题
面对“订单超时关闭”需求,我会先问:
What(做什么)
- 具体要关闭什么?只关闭订单状态,还是需要连带操作?
- “关闭”是逻辑删除还是物理删除?
Why(为什么做)
- 为什么是30分钟?业务依据是什么?
- 为什么需要自动关闭?解决什么问题?
When(何时触发)
- 精确的时间点要求?30分钟是创建时间还是更新时间?
- 是否有例外情况?(如客服手动干预)
Who(涉及角色)
- 系统自动执行,还是人工触发?
- 关闭后需要通知哪些人?
Where(影响范围)
- 只影响订单模块,还是涉及库存、优惠券等?
- 数据一致性要求级别?
How(如何实现)
- 实时性要求?秒级还是分钟级可接受?
- 预计订单量?性能要求?
1.2 需求澄清的输出
经过沟通,明确需求细节:
- 触发条件:订单创建30分钟后,状态仍为“待支付”
- 关闭操作:订单状态变更为“已关闭”,释放库存,优惠券回退
- 执行方式:系统自动执行,支持手动干预
- 性能要求:5分钟内完成关闭即可
- 数据量:日均10万订单,峰值1000单/分钟
第二步:领域建模——从业务语言到技术概念
2.1 识别核心业务实体
// 不是一上来就写getter/setter,而是先理解业务含义
public class Order{
// 订单的唯一标识 - 业务标识符
private String orderNo;
// 订单的状态流转 - 业务生命周期
private OrderStatus status;
// 时间点 - 业务时效性
private LocalDateTime createTime;
private LocalDateTime payTime;
private LocalDateTime closeTime;
// 业务规则:是否应该关闭
public boolean shouldAutoClose(){
return status == OrderStatus.WAITING_PAYMENT &&
isCreatedOver30MinutesAgo();
}
// 业务操作:关闭订单
public void close(String closeReason){
this.status = OrderStatus.CLOSED;
this.closeTime = LocalDateTime.now();
this.closeReason = closeReason;
}
}
2.2 定义业务枚举和值对象
// 订单状态 - 业务状态机
public enum OrderStatus {
WAITING_PAYMENT("待支付"),
PAID("已支付"),
CLOSED("已关闭"),
DELIVERED("已发货");
private final String desc;
OrderStatus(String desc) {
this.desc = desc;
}
// 状态流转规则
public boolean canChangeTo(OrderStatus newStatus){
switch (this) {
case WAITING_PAYMENT:
return newStatus == PAID || newStatus == CLOSED;
case PAID:
return newStatus == DELIVERED;
default:
return false;
}
}
}
// 订单金额 - 值对象(业务含义)
public class OrderAmount{
private final BigDecimal originalAmount; // 原始金额
private final BigDecimal discountAmount; // 优惠金额
private final BigDecimal finalAmount; // 实付金额
public OrderAmount(BigDecimal originalAmount, BigDecimal discountAmount){
validateAmount(originalAmount, discountAmount);
this.originalAmount = originalAmount;
this.discountAmount = discountAmount;
this.finalAmount = originalAmount.subtract(discountAmount);
}
private void validateAmount(BigDecimal original, BigDecimal discount){
if (original.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("金额不能为负");
}
if (discount.compareTo(original) > 0) {
throw new IllegalArgumentException("优惠金额不能超过订单金额");
}
}
}
第三步:技术方案设计——多种思路的权衡
面对一个业务需求,思考多种Java实现方案并进行权衡,是优秀工程师的基本功。
3.1 方案一:数据库轮询(简单直接)
适用场景:数据量小,实时性要求不高
@Component
public class OrderTimeoutChecker{
@Scheduled(fixedRate = 60000) // 每分钟执行
public void checkTimeoutOrders(){
// 查询30分钟前的待支付订单
List<Order> timeoutOrders = orderRepository
.findByStatusAndCreateTimeBefore(
OrderStatus.WAITING_PAYMENT,
LocalDateTime.now().minusMinutes(30));
for (Order order : timeoutOrders) {
closeOrder(order);
}
}
}
优点:实现简单,逻辑清晰
缺点:精度低,数据库压力大,空轮询浪费资源
3.2 方案二:延迟消息(推荐方案)
适用场景:实时性要求高,系统解耦
@Service
@Transactional
public class OrderService{
public void createOrder(CreateOrderCommand command){
// 创建订单
Order order = new Order(command);
orderRepository.save(order);
// 发送延迟消息
sendTimeoutCheckMessage(order.getId(), 30);
}
private void sendTimeoutCheckMessage(Long orderId, int delayMinutes){
Message message = MessageBuilder.withPayload(orderId)
.setHeader("delay", delayMinutes * 60 * 1000)
.build();
delayQueue.send(message);
}
@EventListener
public void handleOrderTimeout(OrderTimeoutEvent event){
Order order = orderRepository.findById(event.getOrderId());
if (order.shouldAutoClose()) {
closeOrder(order);
}
}
}
优点:实时性好,解耦,节省资源
缺点:依赖消息队列,消息可能丢失
3.3 方案三:时间轮算法(高性能方案)
适用场景:海量数据,高性能要求
@Component
public class TimingWheel{
private final WheelBucket[] wheel;
private int currentTick;
public void addTimeoutTask(Long orderId, int delaySeconds){
int ticks = delaySeconds;
int index = (currentTick + ticks) % wheel.length;
wheel[index].addTask(orderId);
}
@Scheduled(fixedRate = 1000)
public void tick(){
processCurrentBucket();
currentTick = (currentTick + 1) % wheel.length;
}
}
优点:性能极高,内存占用小
缺点:实现复杂,调试困难
第四步:架构设计——平衡复杂性和扩展性
4.1 分层架构的职责划分
Controller层:HTTP协议适配,参数校验,格式转换
↓
Service层:业务逻辑编排,事务控制,异常处理
↓
Manager层:通用业务能力,组件编排
↓
Repository层:数据持久化,缓存处理
↓
基础设施:数据库,消息队列,缓存等
4.2 订单关闭的完整实现
// Controller层:关注HTTP交互
@RestController
@RequestMapping("/orders")
@Validated
public class OrderController{
@PostMapping
public ApiResult<String> createOrder(@Valid @RequestBody CreateOrderRequest request){
String orderNo = orderApplicationService.createOrder(request);
return ApiResult.success(orderNo);
}
}
// ApplicationService层:业务用例编排
@Service
@Transactional
public class OrderApplicationService{
public String createOrder(CreateOrderCommand command){
// 1. 参数校验
validationService.validate(command);
// 2. 执行业务逻辑
Order order = orderDomainService.createOrder(command);
// 3. 保存数据
orderRepository.save(order);
// 4. 发送领域事件
eventPublisher.publish(new OrderCreatedEvent(order));
return order.getOrderNo();
}
}
// DomainService层:核心业务逻辑
@Service
public class OrderDomainService{
public Order createOrder(CreateOrderCommand command){
// 库存检查
inventoryService.checkStock(command.getItems());
// 价格计算
OrderAmount amount = priceCalculator.calculate(command);
// 构建订单
Order order = OrderFactory.create(command, amount);
return order;
}
public void closeOrder(Order order, String closeReason){
if (!order.canClose()) {
throw new BusinessException("订单当前状态不可关闭");
}
order.close(closeReason);
// 释放库存
inventoryService.restoreStock(order);
// 退回优惠券
couponService.returnCoupon(order);
}
}
第五步:异常处理——业务边界情况考虑
5.1 定义业务异常体系
// 基础业务异常
public abstract class BusinessException extends RuntimeException{
private final String code;
private final String message;
public BusinessException(String code, String message){
super(message);
this.code = code;
this.message = message;
}
}
// 具体业务异常
public class OrderNotFoundException extends BusinessException{
public OrderNotFoundException(String orderNo){
super("ORDER_NOT_FOUND", "订单不存在: " + orderNo);
}
}
public class OrderStatusException extends BusinessException{
public OrderStatusException(String currentStatus, String expectStatus){
super("ORDER_STATUS_ERROR",
"订单状态错误,当前状态: " + currentStatus + ",期望状态: " + expectStatus);
}
}
public class InventoryShortageException extends BusinessException{
public InventoryShortageException(String skuCode){
super("INVENTORY_SHORTAGE", "库存不足: " + skuCode);
}
}
5.2 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler{
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
ErrorResponse response = new ErrorResponse(e.getCode(), e.getMessage());
return ResponseEntity.badRequest().body(response);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("系统异常", e);
ErrorResponse response = new ErrorResponse("SYSTEM_ERROR", "系统繁忙");
return ResponseEntity.internalServerError().body(response);
}
}
@Data
class ErrorResponse{
private final String code;
private final String message;
private final long timestamp = System.currentTimeMillis();
}
第六步:测试策略——保障代码质量
6.1 单元测试:核心业务逻辑
class OrderDomainServiceTest{
@Test
void should_close_order_when_timeout(){
// given
Order order = OrderFixture.timeoutOrder();
// when
orderDomainService.closeOrder(order, "超时关闭");
// then
assertThat(order.getStatus()).isEqualTo(OrderStatus.CLOSED);
assertThat(order.getCloseReason()).isEqualTo("超时关闭");
}
@Test
void should_throw_exception_when_close_paid_order(){
// given
Order order = OrderFixture.paidOrder();
// when & then
assertThatThrownBy(() -> orderDomainService.closeOrder(order, "测试"))
.isInstanceOf(OrderStatusException.class);
}
}
// 测试数据构造
class OrderFixture{
public static Order timeoutOrder(){
Order order = new Order();
order.setStatus(OrderStatus.WAITING_PAYMENT);
order.setCreateTime(LocalDateTime.now().minusMinutes(31));
return order;
}
}
6.2 集成测试:完整业务流程
@SpringBootTest
class OrderIntegrationTest{
@Test
void should_complete_order_flow(){
// 创建订单
CreateOrderRequest request = buildRequest();
String orderNo = orderController.createOrder(request).getData();
// 验证订单创建
Order order = orderRepository.findByOrderNo(orderNo);
assertThat(order).isNotNull();
// 模拟超时
testTimeProvider.plusMinutes(31);
timeoutChecker.checkTimeoutOrders();
// 验证订单关闭
Order closedOrder = orderRepository.findByOrderNo(orderNo);
assertThat(closedOrder.getStatus()).isEqualTo(OrderStatus.CLOSED);
}
}
总结:从需求到代码的思考框架
7.1 我的六个思考步骤
- 需求澄清(30%时间):确保理解业务本质
- 领域建模(20%时间):将业务概念转化为技术模型
- 方案设计(15%时间):权衡不同技术方案的利弊
- 架构设计(15%时间):设计清晰的分层和职责
- 异常处理(10%时间):考虑各种边界情况
- 测试策略(10%时间):保障代码质量
7.2 避坑经验分享
新手常犯的错误:
- 直接写代码,不思考业务本质
- 过度设计,引入不必要的复杂性
- 忽略异常情况,系统健壮性差
- 不考虑性能,后期优化困难
我的建议:
- 先理解业务,再思考技术
- 简单方案能解决就不要复杂化
- 代码是写给人看的,可读性很重要
- 测试不是可有可无,而是必备环节
记住:优秀的程序员不是写出最复杂代码的人,而是用最简单方案解决复杂问题的人。
希望这个从需求到代码的完整思考框架能对你的开发工作有所启发。如果你想了解更多关于Java开发、架构设计的实战经验,欢迎来云栈社区交流探讨。