人人都会AI编程

6.2 后端开发场景:接口、业务逻辑编写

更新时间:2026-06-28

后端开发的核心产出就两样:接口业务逻辑。前者是对外的约定,后者是内部的规则。这一节我们聊如何在实际项目中高效、稳妥地完成这两部分工作。

6.2.1 接口开发:先约定,后实现

别急着写代码,先和服务端/前端同事对齐接口文档。推荐先用 Swagger 或 YApi 定义好请求响应格式,再开始编码。

标准接口的三件套:

# 以 Python FastAPI 为例,其他语言思路相同
@app.post("/api/v1/orders")
async def create_order(
    request: OrderCreateRequest,  # 1. 入参模型(自动校验)
    current_user: User = Depends(get_current_user)  # 2. 通用鉴权
):
    # 3. 统一响应包装
    try:
        order = await order_service.create(request, current_user.id)
        return Response(data=order, msg="创建成功")
    except BizException as e:
        return Response(code=e.code, msg=e.message)

关键细节:

  • 参数校验放在最外层:用 Pydantic、Java Bean Validation 或 Go Validator,别让脏数据进入业务层
  • HTTP 状态码别太随意:200 代表成功,400 是客户端问题,500 是服务端问题,别所有接口都返回 200 然后在 body 里写 code=-1
  • 版本号提前设计:v1、v2 的路径前缀,避免后期重构时互相打架

6.2.2 业务逻辑:分层是底线

一个典型的后端请求处理流程:

路由层(Controller) → 业务层(Service) → 数据层(DAO/Repository) → 数据库

各层职责要清晰:

  1. Controller 只干三件事:接收参数、调用 Service、返回结果。不要在这里写 if-else 业务判断
  2. Service 是核心战场:事务控制在这里,复杂逻辑在这里,对外部服务的调用也在这里
  3. DAO 只负责 SQL:别在 SQL 里写业务逻辑,复杂的可以用 ORM 的 query builder,但别拼字符串 SQL

真实案例:订单创建流程

// Java Spring Boot 示例
@Service
public class OrderService {
    
    @Transactional  // 事务边界明确
    public OrderVO createOrder(OrderDTO dto, Long userId) {
        // 1. 参数预处理(幂等性校验)
        if (redisTemplate.hasKey("order:idempotent:" + dto.getIdempotentKey())) {
            throw new BizException("重复提交");
        }
        
        // 2. 业务规则校验
        Product product = productDao.findById(dto.getProductId());
        if (product.getStock() < dto.getQuantity()) {
            throw new BizException("库存不足");
        }
        
        // 3. 数据持久化
        Order order = new Order();
        BeanUtils.copyProperties(dto, order);
        order.setUserId(userId);
        order.setStatus(OrderStatus.PENDING);
        orderDao.save(order);
        
        // 4. 异步处理(发送消息,不阻塞主流程)
        rabbitTemplate.convertAndSend("order.created", order);
        
        // 5. 返回 VO(脱敏,不直接返回 DO)
        return convertToVO(order);
    }
}

6.2.3 常见坑与避坑指南

1. 空指针地狱

// 烂代码
String city = user.getAddress().getCity();  // 随时 NPE

// 好代码
String city = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCity)
    .orElse("未知");

2. 事务边界过大
别在 @Transactional 方法里调外部接口,否则外部接口超时会导致数据库连接被长期占用。

3. 循环查库

// 性能灾难
for (Long id : ids) {
    userDao.findById(id);  // 发 N 次 SQL
}

// 正确做法
userDao.findByIdIn(ids);  // 一次 IN 查询,或走批量查询

4. 异常处理混乱
定义统一的异常体系:

  • BizException:业务异常(参数错误、状态不符),返回 400,记 WARN 日志
  • SystemException:系统异常(数据库挂了、第三方服务超时),返回 500,记 ERROR 日志并告警

6.2.4 调试技巧

本地联调:

  • 用 Postman 或 Insomnia 保存接口集合,别每次都手写 curl
  • 数据库加 @Profile("dev") 的自动填充,插入测试数据时自动设置 create_time、update_time

日志规范:

// 入口处打参数
log.info("创建订单开始, userId={}, request={}", userId, JSON.toJSONString(request));

// 关键节点打状态
log.info("库存扣减成功, orderId={}, stock={}", orderId, remainStock);

// 异常处打上下文
log.error("支付回调处理失败, orderNo={}, thirdPartyResp={}", orderNo, resp, exception);

6.2.5 小结

写后端代码就一个原则:让别人(包括三个月后的自己)一眼能看懂数据是怎么流转的

  • 接口要稳:入参校验严格,出参格式统一
  • 逻辑要清:分层明确,事务边界清晰,异常处理到位
  • 性能要顾:N+1 查询、大事务、同步阻塞都是隐形炸弹

写完代码后,用 SonarLint 扫一遍,把明显的代码异味先修了,再提交 PR。