Files
aidemo-202606/teacher-docs
2026-06-13 16:53:08 +08:00
..
2026-06-13 16:53:08 +08:00

遗留订单系统 - 项目说明与学习指南

本项目是一个刻意构造的“遗留系统”代码练习场,供团队内部进行代码审查、重构、安全测试等技能训练使用。


一、项目概述

这是一个模拟真实环境的电商订单系统,代码风格、架构设计、命名规范均刻意模仿了多年维护、多人经手的遗留项目。系统本身功能可用,但代码质量和安全性存在大量隐患。

技术栈

组件 版本
JDK 1.8
Spring Boot 2.1.18.RELEASE
数据库 H2 (内存模式)
数据库访问 原生 JDBC
构建工具 Maven 3.x

快速启动

mvn spring-boot:run

启动后:

  • API 地址:http://localhost:8080/api/orders
  • H2 控制台:http://localhost:8080/h2-consoleJDBC URL: jdbc:h2:mem:legacydb,用户名 sa,密码 password123

接口一览

方法 路径 说明
POST /api/orders 创建订单
GET /api/orders 查询所有订单
GET /api/orders/{id} 按ID查询订单
GET /api/orders/search?name=xxx 按商品名称搜索
PUT /api/orders/{id}/cancel 取消订单
GET /api/orders/report 订单统计报表

创建订单示例

curl -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{
    "kehuMingzi": "张三",
    "items": [
      {"shangpinMingcheng": "机械键盘", "danjia": 399, "shuliang": 2},
      {"shangpinMingcheng": "鼠标垫", "danjia": 29.9, "shuliang": 5}
    ]
  }'

二、已知问题清单

2.1 安全漏洞

# 问题 位置 严重程度 说明
1 SQL 注入 OrderDao.findByProductName() 严重 使用 Statement + 字符串拼接构建 SQL攻击者可注入任意 SQL 语句

2.2 并发缺陷

# 问题 位置 说明
2 订单号生成非线程安全 OrderService.generateOrderNumber()static orderNumberSequence++ 多线程并发创建订单时可能产生重复订单号

2.3 空指针风险

# 问题 位置 说明
3 未判空直接调用 OrderService.cancelOrder()order.getZhuangtai() 查询不存在的订单返回 null 后直接调用,抛出 NullPointerException

2.4 数值处理缺陷

# 问题 位置 说明
4 整型溢出 OrderService.createOrder()int intDiscount = (int)discount 大金额订单折扣计算可能导致溢出,结果不符合预期

2.5 异常处理不当

# 问题 位置 说明
5 异常被吞没 全局 catch(Exception e) { e.printStackTrace(); } 数据库异常等关键错误被隐藏,上层无法感知

三、代码质量问题速查

问题类型 典型位置 说明
上帝类 OrderService.java350+ 行) 单个类承载了订单创建、取消、查询、报表等多种职责
长方法 createOrder() 方法200+ 行) 校验、金额计算、折扣、税额全部耦合在一个方法内
重复代码 OrderService 中价格计算逻辑 至少 3 处完全相同的价格计算循环
硬编码 税率 0.17、折扣阶梯、数据库密码 业务常量散布在代码中,没有配置化
手动 JDBC 整个 DAO 层 未使用 JPA/MyBatis 等 ORM 框架,手写 SQL 和管理连接
静态单例 DbUtil.java 绕过 Spring 容器,自行管理数据库连接池
线程不安全 SimpleDateFormatstatic orderNumberSequence 多线程环境下共享可变状态
System.out 日志 整个项目 没有使用 SLF4J/Logback 等日志框架
命名混乱 dingdanBianhaokehuMingzi 等字段名 拼音 + 英文混合命名,无统一规范
无单元测试 整个项目 测试覆盖率为零,重构风险极高
无 DTO 分层 Controller 直接暴露实体类 API 响应结构与数据库模型耦合

四、推荐练习场景

以下场景可按顺序逐步推进,也可根据时间选取重点练习。每个场景均适合使用 AI 辅助完成。

场景 1阅读理解建议 10-15 分钟)

目标:面对无注释、命名混乱的代码,快速理解业务逻辑。

练习内容

  1. 打开 OrderService.java,阅读 createOrder() 方法
  2. 尝试解释每个方法的业务含义
  3. 为代码添加必要的中文注释
  4. 绘制订单创建流程图

关注点

  • 拼音 + 英文混合命名的识别dingdanBianhao = 订单编号kehuMingzi = 客户名字)
  • 硬编码税率 0.17 的历史背景
  • 多级 if-else 折扣逻辑的业务规则

场景 2代码重构建议 15-25 分钟)

目标:拆分上帝类和大方法,消除重复代码。

练习内容

  1. 拆分 createOrder() 方法,提取为独立职责的方法:
    • 请求校验 → validateOrderRequest()
    • 金额计算 → computePrices()
    • 折扣计算 → applyDiscount()
    • 税额计算 → calculateTax()
  2. 找出并消除 OrderService 中 3 处重复的价格计算逻辑
  3. 将硬编码常量提取为配置项

关注点

  • 提取方法时确保不改变原有业务逻辑
  • 重复代码的识别标准(不仅仅是完全相同的代码,逻辑等价也要识别)

场景 3补充测试建议 15-20 分钟)

目标:为无测试覆盖的遗留系统建立测试保护网。

练习内容

  1. 添加 spring-boot-starter-test 依赖
  2. 为核心方法编写单元测试:
    • createOrder() — 正常场景、边界值、异常场景
    • cancelOrder() — 正常取消、不存在订单、已取消订单
    • calcDiscount() — 各折扣阶梯
  3. OrderDao 编写集成测试(使用 H2 内存数据库)

关注点

  • 由于代码耦合度高,编写测试本身就会发现设计问题
  • static 单例和共享状态使测试变得困难,说明可测试性对设计的重要性

场景 4修复缺陷建议 15-20 分钟)

目标:审查代码,发现并修复典型缺陷。

练习内容

  1. 审查整个项目,找出所有潜在缺陷和安全漏洞
  2. 逐一修复,每种修复完成后运行测试验证
  3. SQL 注入验证:构造恶意请求确认漏洞存在,修复后验证已消除

SQL 注入复现

# 正常搜索
curl -G "http://localhost:8080/api/orders/search" --data-urlencode "name=键盘"

# 注入验证(返回所有订单,说明漏洞存在)
curl "http://localhost:8080/api/orders/search?name=' OR '1'='1'--"

修复要点

  • SQL 注入:StatementPreparedStatement,参数化查询
  • 并发竞态:使用 AtomicLong 或数据库序列
  • 空指针:添加 null 检查或使用 Optional
  • 整型溢出:使用 BigDecimal 处理金额
  • 异常处理:区分可恢复和不可恢复异常,日志记录 + 业务处理

场景 5技术栈升级建议 15-20 分钟)

目标:将 Spring Boot 2.1.18 + Java 8 升级到 Spring Boot 3.x + Java 17/21。

涉及变更

  1. pom.xml:升级版本号
  2. 包名迁移:javax.*jakarta.*
  3. 配置迁移:spring.datasource.initialization-modespring.sql.init.mode
  4. H2 版本兼容性处理
  5. 已知 API 变更适配

关注点

  • 升级后功能是否正常
  • 哪些废弃 API 需要替换

场景 6添加新功能建议 20-30 分钟)

选择一个方向在现有代码基础上添加新功能。建议先写测试再写代码

方向 A退款功能

  1. 新增 REFUNDED 订单状态
  2. 实现退款逻辑(已完成订单可退款,退款金额为原单 80%
  3. 添加 PUT /api/orders/{id}/refund API

方向 B优惠券功能

  1. 新增优惠券模型(支持满减券和折扣券)
  2. createOrder() 中集成优惠券折扣
  3. 处理优惠券与现有折扣的叠加规则

关注点

  • 在不破坏原有逻辑的前提下扩展功能
  • 是否应先重构再添加新功能

五、项目代码结构

legacy-order-system/
├── pom.xml
├── README.md
├── teacher-docs/                          # 本目录
│   └── README.md
└── src/main/
    ├── java/com/legacy/order/
    │   ├── LegacyOrderApplication.java    # 启动类
    │   ├── controller/
    │   │   └── OrderController.java       # REST 接口
    │   ├── service/
    │   │   └── OrderService.java          # 业务逻辑
    │   ├── dao/
    │   │   └── OrderDao.java              # 数据访问
    │   ├── model/
    │   │   ├── Order.java                 # 订单实体
    │   │   ├── OrderItem.java             # 订单项实体
    │   │   └── OrderStatus.java           # 订单状态枚举
    │   └── util/
    │       └── DbUtil.java                # 数据库连接工具
    └── resources/
        ├── application.properties         # 配置文件
        └── schema.sql                     # 数据库 DDL

六、使用建议

  1. 代码审查优先:在对代码进行任何修改之前,先完整阅读和理解现有代码。
  2. 小步迭代:每次只修改一个问题,修改后立即运行测试验证。
  3. 测试先行:在重构前先补上关键路径的单元测试,确保重构不破坏功能。
  4. 记录修改:每次修改后记录改动点、原因和影响范围。
  5. 关注设计:不仅修 Bug还要思考为什么会写出这样的 Bug以及如何在设计层面避免。