文章目录
- [Spring Cloud 学习与实践(4):订单服务开发](#Spring Cloud 学习与实践(4):订单服务开发)
-
- [1. 本章目标](#1. 本章目标)
- [2. cloud-order 模块结构](#2. cloud-order 模块结构)
- [3. 订单表设计](#3. 订单表设计)
- [4. DTO 设计](#4. DTO 设计)
- [5. 订单实体](#5. 订单实体)
- [6. Mapper](#6. Mapper)
- [7. Service 接口与实现](#7. Service 接口与实现)
- [8. Controller](#8. Controller)
- [9. order.http 测试](#9. order.http 测试)
- [10. 故障演练总结](#10. 故障演练总结)
Spring Cloud 学习与实践(4):订单服务开发
本章目标:开发订单服务
cloud-order,实现本地订单创建和查询接口,并通过故障演练展示当前版本的设计缺陷,为后续 OpenFeign 调用用户和商品服务、库存扣减打基础。
1. 本章目标
本章主要完成:
- 搭建
cloud-order模块 - 创建订单表
t_order - 创建订单实体
Order - 创建 DTO
CreateOrderRequest - 创建 Mapper、Service 和 Controller
- 实现本地创建订单逻辑
- 订单查询接口
- 故意演练不校验用户、商品和价格
- 总结当前缺陷并引出后续服务间远程调用必要性
2. cloud-order 模块结构
text
cloud-order
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com.example.cloud.order
│ │ ├── CloudOrderApplication.java
│ │ ├── controller
│ │ │ └── OrderController.java
│ │ ├── dto
│ │ │ └── CreateOrderRequest.java
│ │ ├── entity
│ │ │ └── Order.java
│ │ ├── mapper
│ │ │ └── OrderMapper.java
│ │ └── service
│ │ ├── OrderService.java
│ │ └── impl
│ │ └── OrderServiceImpl.java
│ └── resources
│ └── application.yml
└── test
└── http
└── order.http
3. 订单表设计
在数据库 cloud_demo 中执行:
sql
DROP TABLE IF EXISTS t_order;
CREATE TABLE t_order (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
product_id BIGINT NOT NULL COMMENT '商品ID',
product_name VARCHAR(128) NOT NULL COMMENT '下单时的商品名称快照',
amount DECIMAL(10, 2) NOT NULL COMMENT '订单金额',
status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态:0待处理,1已完成,2已取消',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
KEY idx_user_id (user_id),
KEY idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
示例数据可通过接口创建。
4. DTO 设计
CreateOrderRequest.java:
java
@Data
public class CreateOrderRequest {
private Long userId;
private Long productId;
private String productName;
private BigDecimal amount;
}
说明:
- DTO 与实体分离,防止客户端直接控制数据库字段
- 便于后续 OpenFeign 调用重构接口参数
5. 订单实体
Order.java:
java
@Data
@TableName("t_order")
public class Order {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long productId;
private String productName;
private BigDecimal amount;
private Integer status;
private LocalDateTime createTime;
}
6. Mapper
OrderMapper.java:
java
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
7. Service 接口与实现
OrderService.java:
java
public interface OrderService extends IService<Order> {
Order createOrder(CreateOrderRequest request);
}
OrderServiceImpl.java:
java
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Override
@Transactional(rollbackFor = Exception.class)
public Order createOrder(CreateOrderRequest request) {
if (request == null || request.getUserId() == null || request.getProductId() == null
|| request.getProductName() == null || request.getAmount() == null) {
throw new BizException(ErrorCode.PARAM_ERROR, "参数不完整");
}
Order order = new Order();
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setProductName(request.getProductName());
order.setAmount(request.getAmount());
order.setStatus(0);
boolean success = save(order);
if (!success) {
throw new BizException(ErrorCode.BIZ_ERROR, "创建订单失败");
}
return order;
}
}
8. Controller
OrderController.java:
java
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@PostMapping
public Result<Order> create(@RequestBody CreateOrderRequest request) {
return Result.success(orderService.createOrder(request));
}
@GetMapping("/{id}")
public Result<Order> getById(@PathVariable Long id) {
Order order = orderService.getById(id);
if (order == null) {
return Result.fail(ErrorCode.NOT_FOUND, "订单不存在");
}
return Result.success(order);
}
}
9. order.http 测试
完整内容↓
javascript
### 正常创建订单
POST http://localhost:9400/orders
Content-Type: application/json
{
"userId": 1,
"productId": 1,
"productName": "机械键盘",
"amount": 299.00
}
### 查询订单详情
GET http://localhost:9400/orders/1
### 查询不存在的订单
GET http://localhost:9400/orders/999
### 参数错误:缺少用户 ID
POST http://localhost:9400/orders
Content-Type: application/json
{
"productId": 1,
"productName": "机械键盘",
"amount": 299.00
}
### 故障演练:不存在的用户仍然可以创建订单
POST http://localhost:9400/orders
Content-Type: application/json
{
"userId": 999999,
"productId": 1,
"productName": "机械键盘",
"amount": 299.00
}
### 故障演练:不存在的商品仍然可以创建订单
POST http://localhost:9400/orders
Content-Type: application/json
{
"userId": 1,
"productId": 999999,
"productName": "不存在的商品",
"amount": 0.01
}
### 故障演练:客户端可以伪造商品价格
POST http://localhost:9400/orders
Content-Type: application/json
{
"userId": 1,
"productId": 1,
"productName": "机械键盘",
"amount": 0.01
}
包含:
- 正常订单创建
- 查询订单
- 用户不存在、商品不存在、伪造金额的订单创建
示例:
http
POST http://localhost:9400/orders
Content-Type: application/json
{
"userId": 999999,
"productId": 1,
"productName": "机械键盘",
"amount": 299.00
}
结果仍然创建成功,说明本章故意保留的缺陷:
- 用户 ID 可以伪造
- 商品 ID 可以伪造
- 商品名称和金额可以伪造
- 库存未扣减
测试后数据库如图↓

10. 故障演练总结
- 本章订单服务完全本地落库
- 故意不校验用户、商品、价格,方便演练后续服务间调用
- 为第 5~7 章引出 OpenFeign、远程用户校验、商品校验及库存扣减逻辑做准备