【Java】Java 实战项目:手把手教你写一个电商订单系统

Java 实战项目:手把手教你写一个电商订单系统


🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

  • [Java 实战项目:手把手教你写一个电商订单系统](#Java 实战项目:手把手教你写一个电商订单系统)

本文将带领大家基于 Spring Boot + MyBatis + MySQL 开发一个简易电商订单系统,覆盖下单核心流程,适合具备 Java 基础和 Spring Boot 入门经验的开发者。系统聚焦核心业务模块,不冗余复杂功能,助力快速掌握订单系统设计与开发思路。

一、系统核心模块与技术栈说明

1.1 核心业务模块

电商订单系统的核心是围绕"用户下单到支付完成"的全流程管控,主要包含以下模块:

  • 用户下单模块:接收用户下单请求,校验用户状态、收货地址等基础信息;

  • 订单创建模块:生成唯一订单号,保存订单主表与订单明细表数据,关联商品、用户、地址信息;

  • 库存扣减模块:下单时校验并扣减商品库存,处理库存不足场景,保证数据一致性;

  • 订单状态管理模块:通过状态机管控订单生命周期(待支付、已支付、待发货、已完成等);

  • 支付回调模块 :接收支付平台回调通知,更新订单状态,触发后续流程(如通知发货)。

1.2 技术栈详情

  • 后端框架:Spring Boot 2.7.x(稳定版,适配性强);

  • ORM 框架:MyBatis 3.x(入门简单,SQL 可控性强);

  • 数据库:MySQL 8.0(关系型数据库,适配订单数据存储);

  • 工具依赖:Lombok(简化实体类代码)、MyBatis Generator(自动生成 Mapper 代码)、Spring Boot Starter Web(Web 服务);

  • 流程图工具:Mermaid(可视化业务流程)。

二、环境搭建与项目初始化

2.1 初始化 Spring Boot 项目

通过 Spring Initializr(https://start.spring.io/)创建项目,选择以下依赖:

  • Spring Web

  • MyBatis Framework

  • MySQL Driver

  • Lombok

生成项目后,导入 IDEA,配置 application.yml 文件,配置数据库连接与 MyBatis 相关参数:

yaml 复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ecommerce_order?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath:mapper/**/*.xml  # Mapper.xml 文件路径
  type-aliases-package: com.example.ecommerceorder.entity  # 实体类包路径
  configuration:
    map-underscore-to-camel-case: true  # 下划线转驼峰命名
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL 日志

server:
  port: 8080

2.2 数据库表设计

创建核心数据表:订单主表(t_order)、订单明细表(t_order_item)、商品表(t_product)、用户表(t_user),SQL 脚本如下:

sql 复制代码
-- 用户表(简化版)
CREATE TABLE t_user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    status TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '用户表';

-- 商品表(简化版)
CREATE TABLE t_product (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID',
    product_name VARCHAR(100) NOT NULL COMMENT '商品名称',
    stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
    price DECIMAL(10,2) NOT NULL COMMENT '商品单价',
    status TINYINT DEFAULT 1 COMMENT '状态:1-上架,0-下架',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '商品表';

-- 订单主表
CREATE TABLE t_order (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
    order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单编号',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    total_amount DECIMAL(10,2) NOT NULL COMMENT '订单总金额',
    status TINYINT NOT NULL COMMENT '订单状态:0-待支付,1-已支付,2-待发货,3-已完成,4-已取消',
    receive_address VARCHAR(200) NOT NULL COMMENT '收货地址',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    pay_time DATETIME COMMENT '支付时间',
    FOREIGN KEY (user_id) REFERENCES t_user(id)
) COMMENT '订单主表';

-- 订单明细表
CREATE TABLE t_order_item (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '明细ID',
    order_id BIGINT NOT NULL COMMENT '订单ID',
    product_id BIGINT NOT NULL COMMENT '商品ID',
    product_name VARCHAR(100) NOT NULL COMMENT '商品名称',
    product_price DECIMAL(10,2) NOT NULL COMMENT '购买单价',
    buy_num INT NOT NULL COMMENT '购买数量',
    FOREIGN KEY (order_id) REFERENCES t_order(id),
    FOREIGN KEY (product_id) REFERENCES t_product(id)
) COMMENT '订单明细表';

三、核心模块开发实现

3.1 实体类设计

基于数据表创建对应实体类,使用 Lombok 简化 Getter/Setter/构造方法:

java 复制代码
// 订单主表实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal totalAmount;
    private Integer status;
    private String receiveAddress;
    private LocalDateTime createTime;
    private LocalDateTime payTime;
}

// 订单明细表实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {
    private Long id;
    private Long orderId;
    private Long productId;
    private String productName;
    private BigDecimal productPrice;
    private Integer buyNum;
}

// 商品实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    private Long id;
    private String productName;
    private Integer stock;
    private BigDecimal price;
    private Integer status;
    private LocalDateTime createTime;
}

3.2 Mapper 层开发

创建 Mapper 接口与对应的 XML 文件,实现数据CRUD操作。以订单和商品 Mapper 为例:

java 复制代码
// OrderMapper.java
@Mapper
public interface OrderMapper {
    // 创建订单
    int insertOrder(Order order);
    // 根据订单号查询订单
    Order selectOrderByNo(String orderNo);
    // 更新订单状态
    int updateOrderStatus(@Param("orderNo") String orderNo, @Param("status") Integer status, @Param("payTime") LocalDateTime payTime);
}

// ProductMapper.java
@Mapper
public interface ProductMapper {
    // 根据商品ID查询商品(加锁,防止并发问题)
    Product selectProductByIdForUpdate(Long productId);
    // 扣减库存
    int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}

OrderMapper.xml 核心内容:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.ecommerceorder.mapper.OrderMapper">
    <insert id="insertOrder" parameterType="com.example.ecommerceorder.entity.Order" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_order (order_no, user_id, total_amount, status, receive_address, create_time)
        VALUES (#{orderNo}, #{userId}, #{totalAmount}, #{status}, #{receiveAddress}, #{createTime})
    </insert>

    <select id="selectOrderByNo" parameterType="String" resultType="com.example.ecommerceorder.entity.Order">
        SELECT * FROM t_order WHERE order_no = #{orderNo}
    </select>

    <update id="updateOrderStatus">
        UPDATE t_order
        SET status = #{status}, pay_time = #{payTime}
        WHERE order_no = #{orderNo}
    </update>
</mapper>

3.3 Service 层开发(核心业务逻辑)

Service 层是业务核心,需处理订单创建、库存扣减、事务控制等逻辑。首先定义订单状态枚举,明确订单生命周期:

java 复制代码
// 订单状态枚举
public enum OrderStatusEnum {
    PENDING_PAY(0, "待支付"),
    PAID(1, "已支付"),
    PENDING_SHIP(2, "待发货"),
    COMPLETED(3, "已完成"),
    CANCELLED(4, "已取消");

    private Integer code;
    private String desc;

    OrderStatusEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // Getter 方法
    public Integer getCode() { return code; }
    public String getDesc() { return desc; }
}

创建 OrderService,实现订单创建核心方法,通过 @Transactional 控制事务,保证库存扣减与订单创建的原子性:

java 复制代码
@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 创建订单
     * @param userId 用户ID
     * @param productId 商品ID
     * @param buyNum 购买数量
     * @param receiveAddress 收货地址
     * @return 订单编号
     */
    @Transactional(rollbackFor = Exception.class)
    public String createOrder(Long userId, Long productId, Integer buyNum, String receiveAddress) {
        // 1. 校验基础信息
        User user = userMapper.selectById(userId);
        if (user == null || user.getStatus() == 0) {
            throw new RuntimeException("用户状态异常,无法下单");
        }
        if (buyNum <= 0) {
            throw new RuntimeException("购买数量不能小于等于0");
        }

        // 2. 校验商品库存(加行锁,防止并发下单超卖)
        Product product = productMapper.selectProductByIdForUpdate(productId);
        if (product == null || product.getStatus() == 0) {
            throw new RuntimeException("商品已下架或不存在");
        }
        if (product.getStock() < buyNum) {
            throw new RuntimeException("商品库存不足");
        }

        // 3. 扣减库存
        int deductResult = productMapper.deductStock(productId, buyNum);
        if (deductResult < 1) {
            throw new RuntimeException("库存扣减失败");
        }

        // 4. 生成订单编号(时间戳+随机数,保证唯一性)
        String orderNo = generateOrderNo();

        // 5. 计算订单总金额
        BigDecimal totalAmount = product.getPrice().multiply(new BigDecimal(buyNum));

        // 6. 保存订单主表
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setTotalAmount(totalAmount);
        order.setStatus(OrderStatusEnum.PENDING_PAY.getCode());
        order.setReceiveAddress(receiveAddress);
        order.setCreateTime(LocalDateTime.now());
        orderMapper.insertOrder(order);

        // 7. 保存订单明细表
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(productId);
        orderItem.setProductName(product.getProductName());
        orderItem.setProductPrice(product.getPrice());
        orderItem.setBuyNum(buyNum);
        orderItemMapper.insertOrderItem(orderItem);

        log.info("订单创建成功,订单编号:{}", orderNo);
        return orderNo;
    }

    /**
     * 生成唯一订单编号
     * 格式:yyyyMMddHHmmss + 3位随机数
     */
    private String generateOrderNo() {
        String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String randomStr = String.valueOf(new Random().nextInt(900) + 100); // 3位随机数
        return dateStr + randomStr;
    }

    /**
     * 支付回调更新订单状态
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderAfterPay(String orderNo) {
        Order order = orderMapper.selectOrderByNo(orderNo);
        if (order == null) {
            throw new RuntimeException("订单不存在");
        }
        // 只有待支付状态可以更新为已支付
        if (!OrderStatusEnum.PENDING_PAY.getCode().equals(order.getStatus())) {
            log.warn("订单{}状态异常,当前状态:{},无法更新为已支付", orderNo, order.getStatus());
            return false;
        }
        int updateResult = orderMapper.updateOrderStatus(orderNo, OrderStatusEnum.PAID.getCode(), LocalDateTime.now());
        return updateResult > 0;
    }
}

关键说明

  • 事务控制:@Transactional(rollbackFor = Exception.class) 表示所有异常都回滚,确保库存扣减与订单创建要么同时成功,要么同时失败;

  • 并发防超卖:通过 selectProductByIdForUpdate 方法加行锁(MySQL 悲观锁),避免多用户同时下单导致库存超卖;

  • 订单编号生成:采用时间戳+随机数策略,保证唯一性,实际生产可优化为分布式ID(如雪花算法)。

3.4 Controller 层开发(接口暴露)

创建 OrderController,暴露下单、支付回调接口,供前端调用:

java 复制代码
@RestController
@RequestMapping("/api/order")
@Slf4j
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 下单接口
     */
    @PostMapping("/create")
    public ResultVO createOrder(@RequestParam Long userId,
                                @RequestParam Long productId,
                                @RequestParam Integer buyNum,
                                @RequestParam String receiveAddress) {
        try {
            String orderNo = orderService.createOrder(userId, productId, buyNum, receiveAddress);
            return ResultVO.success("下单成功", orderNo);
        } catch (RuntimeException e) {
            log.error("下单失败", e);
            return ResultVO.error(e.getMessage());
        }
    }

    /**
     * 支付回调接口(模拟支付平台回调)
     */
    @PostMapping("/pay/callback")
    public ResultVO payCallback(@RequestParam String orderNo) {
        try {
            boolean result = orderService.updateOrderAfterPay(orderNo);
            if (result) {
                return ResultVO.success("订单状态更新成功");
            } else {
                return ResultVO.error("订单状态更新失败");
            }
        } catch (RuntimeException e) {
            log.error("支付回调处理失败", e);
            return ResultVO.error(e.getMessage());
        }
    }

    /**
     * 查询订单状态
     */
    @GetMapping("/status")
    public ResultVO getOrderStatus(@RequestParam String orderNo) {
        Order order = orderService.getOrderByNo(orderNo);
        if (order == null) {
            return ResultVO.error("订单不存在");
        }
        Map<String, Object> data = new HashMap<>();
        data.put("orderNo", orderNo);
        data.put("status", order.getStatus());
        data.put("statusDesc", OrderStatusEnum.values()[order.getStatus()].getDesc());
        return ResultVO.success("查询成功", data);
    }
}

其中 ResultVO 是统一返回结果封装类,简化接口响应格式:

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVO {
    private Integer code; // 0-成功,1-失败
    private String msg;
    private Object data;

    // 成功响应
    public static ResultVO success(String msg, Object data) {
        return new ResultVO(0, msg, data);
    }

    // 失败响应
    public static ResultVO error(String msg) {
        return new ResultVO(1, msg, null);
    }
}

3.5 订单状态机设计(优化版)

对于复杂订单状态流转,可引入 Spring StateMachine 简化状态管理。首先添加依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>3.0.1.RELEASE</version>
</dependency>

配置状态机,定义状态与事件流转规则:

java 复制代码
@Configuration
@EnableStateMachineFactory
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderEventEnum> {

    // 状态流转配置
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEventEnum> states) throws Exception {
        states
            .withStates()
            .initial(OrderStatusEnum.PENDING_PAY) // 初始状态:待支付
            .state(OrderStatusEnum.PAID)
            .state(OrderStatusEnum.PENDING_SHIP)
            .state(OrderStatusEnum.COMPLETED)
            .end(OrderStatusEnum.CANCELLED) // 结束状态:已取消
            .end(OrderStatusEnum.COMPLETED); // 结束状态:已完成
    }

    // 状态流转规则
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEventEnum> transitions) throws Exception {
        // 待支付 -> 已支付(支付事件)
        transitions.withExternal()
            .source(OrderStatusEnum.PENDING_PAY).target(OrderStatusEnum.PAID)
            .event(OrderEventEnum.PAY)
            .guard(payGuard()) // 守卫:校验状态合法性
            .action(payAction()); // 动作:执行支付后的业务逻辑

        // 已支付 -> 待发货(发货通知事件)
        transitions.withExternal()
            .source(OrderStatusEnum.PAID).target(OrderStatusEnum.PENDING_SHIP)
            .event(OrderEventEnum.SHIP_NOTIFY);

        // 待发货 -> 已完成(确认收货事件)
        transitions.withExternal()
            .source(OrderStatusEnum.PENDING_SHIP).target(OrderStatusEnum.COMPLETED)
            .event(OrderEventEnum.CONFIRM_RECEIVE);

        // 待支付 -> 已取消(取消订单事件)
        transitions.withExternal()
            .source(OrderStatusEnum.PENDING_PAY).target(OrderStatusEnum.CANCELLED)
            .event(OrderEventEnum.CANCEL);
    }

    // 支付守卫:校验订单是否可支付
    @Bean
    public Guard<OrderStatusEnum, OrderEventEnum> payGuard() {
        return context -> {
            Order order = context.getExtendedState().get("order", Order.class);
            return order != null && OrderStatusEnum.PENDING_PAY.equals(order.getStatus());
        };
    }

    // 支付动作:更新订单支付时间等信息
    @Bean
    public Action<OrderStatusEnum, OrderEventEnum> payAction() {
        return context -> {
            Order order = context.getExtendedState().get("order", Order.class);
            order.setPayTime(LocalDateTime.now());
            log.info("订单{}支付成功,更新支付时间:{}", order.getOrderNo(), order.getPayTime());
        };
    }
}

// 订单事件枚举
enum OrderEventEnum {
    PAY, // 支付
    SHIP_NOTIFY, // 发货通知
    CONFIRM_RECEIVE, // 确认收货
    CANCEL // 取消订单
}

在 Service 中使用状态机处理订单状态流转,替代手动状态判断,使代码更清晰、可维护。

四、系统核心流程图

以下是"用户下单到支付完成"的完整业务流程图,使用 Mermaid 语法绘制,涵盖参数校验、库存扣减、订单创建、支付回调等全流程:
携带参数:用户ID、商品ID、购买数量、收货地址
校验失败(用户异常/数量非法)
校验成功
商品不存在/已下架
商品存在
库存不足
库存充足
扣减失败
扣减成功
支付失败
支付成功
取消订单
用户发起下单请求
Controller层接收请求
校验基础信息
返回错误信息
Service层开启事务
查询商品信息(加行锁)
事务回滚,返回错误
校验库存是否充足
扣减商品库存
生成唯一订单编号
计算订单总金额
保存订单主表数据
保存订单明细表数据
事务提交,返回订单编号
用户发起支付
支付平台处理支付
用户重新支付/取消订单
支付平台回调接口
Service层更新订单状态为已支付
返回回调成功响应
后续流程(通知发货、物流跟踪等)
恢复商品库存,更新订单状态为已取消

五、项目结构与开源仓库说明

5.1 项目结构(GitHub风格)

Java 复制代码
ecommerce-order-system/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── ecommerceorder/
│   │   │               ├── EcommerceOrderApplication.java  // 项目启动类
│   │   │               ├── controller/  // 控制层
│   │   │               │   └── OrderController.java
│   │   │               ├── entity/  // 实体类
│   │   │               │   ├── Order.java
│   │   │               │   ├── OrderItem.java
│   │   │               │   ├── Product.java
│   │   │               │   └── User.java
│   │   │               ├── enums/  // 枚举类
│   │   │               │   ├── OrderStatusEnum.java
│   │   │               │   └── OrderEventEnum.java
│   │   │               ├── mapper/  // Mapper接口
│   │   │               │   ├── OrderMapper.java
│   │   │               │   ├── OrderItemMapper.java
│   │   │               │   ├── ProductMapper.java
│   │   │               │   └── UserMapper.java
│   │   │               ├── service/  // 服务层
│   │   │               │   ├── impl/
│   │   │               │   │   └── OrderServiceImpl.java
│   │   │               │   └── OrderService.java
│   │   │               ├── config/  // 配置类
│   │   │               │   ├── OrderStateMachineConfig.java
│   │   │               │   └── MyBatisConfig.java
│   │   │               └── vo/  // 响应VO
│   │   │                   └── ResultVO.java
│   │   └── resources/
│   │       ├── application.yml  // 全局配置文件
│   │       ├── mapper/  // Mapper XML文件
│   │       │   ├── OrderMapper.xml
│   │       │   ├── OrderItemMapper.xml
│   │       │   ├── ProductMapper.xml
│   │       │   └── UserMapper.xml
│   │       └── db/  // SQL脚本
│   │           └── init.sql
│   └── test/  // 测试类
│       └── java/
│           └── com/
│               └── example/
│                   └── ecommerceorder/
│                       └── OrderServiceTest.java
├── pom.xml  // 依赖配置
├── README.md  // 项目说明文档
└── .gitignore  // Git忽略文件

5.2 开源仓库链接(虚构)

完整项目代码已开源至 GitHub,包含详细注释、SQL脚本、测试用例,可直接克隆运行:

GitHub 地址:https://github.com/example/ecommerce-order-system

运行说明

  1. 克隆项目至本地,导入 IDEA;

  2. 创建 MySQL 数据库 ecommerce_order,执行 resources/db/init.sql 脚本初始化表结构与测试数据;

  3. 修改 application.yml 中的数据库连接信息,适配本地环境;

  4. 启动项目,通过 Postman 调用接口测试(接口文档可参考 README.md)。

六、扩展与优化建议

本项目为简易版订单系统,实际生产环境需进行以下优化:

  • 分布式场景优化:使用分布式ID生成订单号(雪花算法),分布式锁(Redisson)替代数据库行锁,解决集群环境下并发问题;

  • 性能优化:添加Redis缓存热点商品信息,减少数据库查询;订单创建可引入消息队列(RabbitMQ/Kafka),异步处理非核心流程(如通知、日志);

  • 异常处理优化:自定义业务异常类,全局异常处理器统一捕获异常,避免重复代码;

  • 安全性优化:接口添加签名验证,防止非法调用;用户ID通过Token解析,避免直接传参;

  • 功能扩展:增加购物车、优惠券、物流跟踪、订单退款等模块,完善电商生态。

📕个人领域 :Linux/C++/java/AI

🚀 个人主页有点流鼻涕 · CSDN

💬 座右铭 : "向光而行,沐光而生。"

相关推荐
寻星探路1 小时前
【JVM 终极通关指南】万字长文从底层到实战全维度深度拆解 Java 虚拟机
java·开发语言·jvm·人工智能·python·算法·ai
星河耀银海1 小时前
Java安全开发实战:从代码防护到架构安全
java·安全·架构
青云交1 小时前
Java 大视界 -- 基于 Java 的大数据可视化在城市水资源管理与节水策略制定中的应用
java·java 大数据·java 大数据可视化·城市水资源管理·spark 数据清洗·echarts 热力图·管网漏损控制
岱宗夫up1 小时前
FastAPI入门(上篇):快速构建高性能Python Web API
开发语言·前端·python·fastapi
Dxy12393102162 小时前
中文乱码恢复方案
开发语言·python
紫陌涵光2 小时前
112. 路径总和
java·前端·算法
workflower2 小时前
多变量时间序列预测
java·hadoop·nosql·需求分析·big data·结对编程
rongyili882 小时前
Dify 外部知识库集成 Milvus 实战指南
开发语言·python·milvus
Hello eveybody3 小时前
什么是动态规划(DP)?(Python版)
python·动态规划