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

269 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 遗留订单系统 - 项目说明与学习指南
> 本项目是一个刻意构造的“遗留系统”代码练习场,供团队内部进行代码审查、重构、安全测试等技能训练使用。
---
## 一、项目概述
这是一个模拟真实环境的电商订单系统,代码风格、架构设计、命名规范均刻意模仿了多年维护、多人经手的遗留项目。系统本身**功能可用**,但代码质量和安全性存在大量隐患。
### 技术栈
| 组件 | 版本 |
|------|------|
| JDK | 1.8 |
| Spring Boot | 2.1.18.RELEASE |
| 数据库 | H2 (内存模式) |
| 数据库访问 | 原生 JDBC |
| 构建工具 | Maven 3.x |
### 快速启动
```bash
mvn spring-boot:run
```
启动后:
- API 地址:`http://localhost:8080/api/orders`
- H2 控制台:`http://localhost:8080/h2-console`JDBC 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` | 订单统计报表 |
### 创建订单示例
```bash
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.java`350+ 行) | 单个类承载了订单创建、取消、查询、报表等多种职责 |
| 长方法 | `createOrder()` 方法200+ 行) | 校验、金额计算、折扣、税额全部耦合在一个方法内 |
| 重复代码 | `OrderService` 中价格计算逻辑 | 至少 3 处完全相同的价格计算循环 |
| 硬编码 | 税率 0.17、折扣阶梯、数据库密码 | 业务常量散布在代码中,没有配置化 |
| 手动 JDBC | 整个 DAO 层 | 未使用 JPA/MyBatis 等 ORM 框架,手写 SQL 和管理连接 |
| 静态单例 | `DbUtil.java` | 绕过 Spring 容器,自行管理数据库连接池 |
| 线程不安全 | `SimpleDateFormat``static orderNumberSequence` | 多线程环境下共享可变状态 |
| System.out 日志 | 整个项目 | 没有使用 SLF4J/Logback 等日志框架 |
| 命名混乱 | `dingdanBianhao``kehuMingzi` 等字段名 | 拼音 + 英文混合命名,无统一规范 |
| 无单元测试 | 整个项目 | 测试覆盖率为零,重构风险极高 |
| 无 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 注入复现**
```bash
# 正常搜索
curl -G "http://localhost:8080/api/orders/search" --data-urlencode "name=键盘"
# 注入验证(返回所有订单,说明漏洞存在)
curl "http://localhost:8080/api/orders/search?name=' OR '1'='1'--"
```
**修复要点**
- SQL 注入:`Statement``PreparedStatement`,参数化查询
- 并发竞态:使用 `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-mode``spring.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以及如何在设计层面避免。