[TOC]
## 步驟 1 : 先運行,看到效果,再學習
先將完整的 spring 項目(向老師要相關資料),配置運行起來,確認可用之后,再學習做了哪些步驟以達到這樣的效果。
## 步驟 2 : 模仿和排錯
在確保可運行項目能夠正確無誤地運行之后,再嚴格照著教程的步驟,對代碼模仿一遍。
模仿過程難免代碼有出入,導致無法得到期望的運行結果,此時此刻通過比較**正確答案** ( 可運行項目 ) 和自己的代碼,來定位問題所在。
采用這種方式,**學習有效果,排錯有效率**,可以較為明顯地提升學習速度,跨過學習路上的各個檻。
## 步驟 3 : 準備數據SQL
訂單數據,以及其對應的訂單項數據,都是由前臺功能增加的。
為了在后臺演示效果,需要自己在數據庫中插入數據
1. 訂單:
```
insert into t_order (order_code, address, post, receiver, mobile, user_message,
create_date, pay_date, delivery_date, confirm_date, status, user_id)
VALUES ('201608241638122609867','某某市,某某區,某某街道,某某號 ','610000','某某某','15111111111',NULL,'2018-08-30',NULL,NULL,NULL,'waitDelivery',6);
```
注: 倒數第二個參數6是對應的用戶id,需要在數據庫中存在,請根據自己的數據信息自行修改。
2. 訂單項
```
insert into t_order_item (number, user_id, product_id, order_id)
VALUES (2,6,1,1);
insert into t_order_item (number, user_id, product_id, order_id)
VALUES (3,6,2,1);
```
注: 第三個參數1、2分別對應的產品id,需要在數據庫中存在,請根據自己的數據信息自行修改。
## 步驟 4 : 頁面截圖

## 步驟 5 : OrderItem
OrderItem在自動創建的基礎上增加了一個product屬性。 因為在訂單管理頁面需要看到訂單下面的訂單項里的產品圖片。
```
package com.dodoke.tmall.pojo;
public class OrderItem {
private Integer id;
private Integer number;
private Integer userId;
private Integer productId;
private Integer orderId;
/* 非數據庫字段 */
private Product product;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
```
## 步驟 6 : Order.java
Order類在自動生成的基礎上,新增4個字段:
1. 該訂單下的訂單項列表
`private List<OrderItem> orderItems;`
2. 該訂單對應的用戶
` private User user;`
3. 該訂單的總計金額
`private float total;`
4 該訂單的總計數量
`private int totalNumber;`
除此之外,還提供了一個getStatusDesc方法,用于把英文表達的Status信息轉換為中文:
```
package com.dodoke.tmall.pojo;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.dodoke.tmall.service.OrderService;
public class Order {
private Integer id;
private String orderCode;
private String address;
private String post;
private String receiver;
private String mobile;
private String userMessage;
private Date createDate;
private Date payDate;
private Date deliveryDate;
private Date confirmDate;
private String status;
private Integer userId;
/* 如下是非數據庫字段 */
private List<OrderItem> orderItems;
private User user;
private float total;
private int totalNumber;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderCode() {
return orderCode;
}
public void setOrderCode(String orderCode) {
this.orderCode = orderCode == null ? null : orderCode.trim();
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address == null ? null : address.trim();
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post == null ? null : post.trim();
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver == null ? null : receiver.trim();
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile == null ? null : mobile.trim();
}
public String getUserMessage() {
return userMessage;
}
public void setUserMessage(String userMessage) {
this.userMessage = userMessage == null ? null : userMessage.trim();
}
public String getCreateDate() {
String createDateFormat = null;
if (null != createDate) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
createDateFormat = simpleDateFormat.format(createDate);
}
return createDateFormat;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public String getPayDate() {
String payDateFormat = null;
if (null != payDate) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
payDateFormat = simpleDateFormat.format(payDate);
}
return payDateFormat;
}
public void setPayDate(Date payDate) {
this.payDate = payDate;
}
public String getDeliveryDate() {
String deliveryDateFormat = null;
if (null != deliveryDate) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
deliveryDateFormat = simpleDateFormat.format(deliveryDate);
}
return deliveryDateFormat;
}
public void setDeliveryDate(Date deliveryDate) {
this.deliveryDate = deliveryDate;
}
public String getConfirmDate() {
String confirmDateFormat = null;
if (null != confirmDate) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
confirmDateFormat = simpleDateFormat.format(confirmDate);
}
return confirmDateFormat;
}
public void setConfirmDate(Date confirmDate) {
this.confirmDate = confirmDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status == null ? null : status.trim();
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getTotal() {
DecimalFormat decimalFormat=new DecimalFormat(".00");//構造方法的字符格式這里如果小數不足2位,會以0補足.
return decimalFormat.format(total);
}
public void setTotal(float total) {
this.total = total;
}
public int getTotalNumber() {
return totalNumber;
}
public void setTotalNumber(int totalNumber) {
this.totalNumber = totalNumber;
}
public static void main(String args[]) {
Order o = new Order();
o.setStatus(OrderService.delete);
System.out.println(o.getStatusDesc());
}
public String getStatusDesc() {
String desc = "未知";
switch (status) {
case OrderService.waitPay:
desc = "待付款";
break;
case OrderService.waitDelivery:
desc = "待發貨";
break;
case OrderService.waitConfirm:
desc = "待收貨";
break;
case OrderService.waitReview:
desc = "等評價";
break;
case OrderService.finish:
desc = "完成";
break;
case OrderService.delete:
desc = "刪除";
break;
default:
desc = "未知";
}
return desc;
}
}
```
## 步驟 7 : OrderItemService
新建OrderItemService,聲明CRUD一套,以及`fill(Order order) `和`fill(List<Order> orders) `, 這兩個方法有什么用呢? 將在下個步驟OrderItemServiceImpl里講解。
```
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.pojo.OrderItem;
public interface OrderItemService {
void add(OrderItem c);
void delete(int id);
void update(OrderItem c);
OrderItem get(int id);
List list();
void fill(List<Order> os);
void fill(Order o);
}
```
## 步驟 8 : OrderItemServiceImpl
OrderItemServiceImpl實現OrderItemService,提供CRUD一套方法的實現。
同時還提供`fill(Order order)`和`fill(List<Order> orders)`,
先說`fill(Order order) `:
為什么要提供這個方法呢? 因為在訂單管理界面,首先是遍歷多個訂單,然后遍歷這個訂單下的多個訂單項。 而由MybatisGenerator逆向工程所創建的一套自動生成代碼,是不具備一對多關系的,需要自己去二次開發。 這里就是做訂單與訂單項的一對多關系。
在`fill(Order order)`中:
1. 根據訂單id查詢出其對應的所有訂單項
2. 通過setProduct為所有的訂單項設置Product屬性
3. 遍歷所有的訂單項,然后計算出該訂單的總金額和總數量
4. 最后再把訂單項設置在訂單的orderItems屬性上。
在`fill(List<Order> orders)` 中,就是遍歷每個訂單,然后挨個調用`fill(Order order)`。
```
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.OrderItemMapper;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.pojo.OrderItem;
import com.dodoke.tmall.pojo.OrderItemExample;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.service.OrderItemService;
import com.dodoke.tmall.service.ProductService;
@Service
public class OrderItemServiceImpl implements OrderItemService {
@Autowired
OrderItemMapper orderItemMapper;
@Autowired
ProductService productService;
@Override
public void add(OrderItem c) {
orderItemMapper.insert(c);
}
@Override
public void delete(int id) {
orderItemMapper.deleteByPrimaryKey(id);
}
@Override
public void update(OrderItem c) {
orderItemMapper.updateByPrimaryKeySelective(c);
}
@Override
public OrderItem get(int id) {
OrderItem result = orderItemMapper.selectByPrimaryKey(id);
setProduct(result);
return result;
}
public List<OrderItem> list() {
OrderItemExample example = new OrderItemExample();
example.setOrderByClause("id desc");
return orderItemMapper.selectByExample(example);
}
@Override
public void fill(List<Order> os) {
// 遍歷每個訂單,然后挨個調用fill(Order order)。
for (Order o : os) {
fill(o);
}
}
public void fill(Order o) {
// 1. 根據訂單id查詢出其對應的所有訂單項
OrderItemExample example = new OrderItemExample();
example.createCriteria().andOrderIdEqualTo(o.getId());
example.setOrderByClause("id desc");
List<OrderItem> ois = orderItemMapper.selectByExample(example);
// 2. 通過setProduct為所有的訂單項設置Product屬性
setProduct(ois);
float total = 0;
int totalNumber = 0;
// 3. 遍歷所有的訂單項,然后計算出該訂單的總金額和總數量
for (OrderItem oi : ois) {
total += oi.getNumber() * oi.getProduct().getPromotePrice();
totalNumber += oi.getNumber();
}
o.setTotal(total);
o.setTotalNumber(totalNumber);
// 4. 最后再把訂單項設置在訂單的orderItems屬性上。
o.setOrderItems(ois);
}
public void setProduct(List<OrderItem> ois) {
for (OrderItem oi : ois) {
setProduct(oi);
}
}
private void setProduct(OrderItem oi) {
Product p = productService.get(oi.getProductId());
oi.setProduct(p);
}
}
```
## 步驟 9 : OrderService
創建OrderService,提供CRUD一套,并提供訂單狀態的常量值。
```
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Order;
public interface OrderService {
String waitPay = "waitPay";
String waitDelivery = "waitDelivery";
String waitConfirm = "waitConfirm";
String waitReview = "waitReview";
String finish = "finish";
String delete = "delete";
void add(Order c);
void delete(int id);
void update(Order c);
Order get(int id);
List list();
}
```
## 步驟 10 : OrderServiceImpl
創建OrderServiceImpl ,實現CRUD一套方法
```
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.OrderMapper;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.pojo.OrderExample;
import com.dodoke.tmall.pojo.User;
import com.dodoke.tmall.service.OrderService;
import com.dodoke.tmall.service.UserService;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
OrderMapper orderMapper;
@Autowired
UserService userService;
@Override
public void add(Order c) {
orderMapper.insert(c);
}
@Override
public void delete(int id) {
orderMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Order c) {
orderMapper.updateByPrimaryKeySelective(c);
}
@Override
public Order get(int id) {
return orderMapper.selectByPrimaryKey(id);
}
public List<Order> list() {
OrderExample example = new OrderExample();
example.setOrderByClause("id desc");
List<Order> result = orderMapper.selectByExample(example);
setUser(result);
return result;
}
public void setUser(List<Order> os) {
for (Order o : os) {
setUser(o);
}
}
public void setUser(Order o) {
int uid = o.getUserId();
User u = userService.get(uid);
o.setUser(u);
}
}
```
## 步驟 11 : OrderController.java
因為訂單的增加和刪除,都是在前臺進行的。 所以OrderController提供的是list方法和`delivery(發貨)`方法
```
package com.dodoke.tmall.controller;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.service.OrderItemService;
import com.dodoke.tmall.service.OrderService;
import com.dodoke.tmall.util.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@Controller
@RequestMapping("")
public class OrderController {
@Autowired
OrderService orderService;
@Autowired
OrderItemService orderItemService;
/**
* 分頁查詢
* @param model 模型
* @param page 頁面對象
* @return 集合
*/
@RequestMapping("admin_order_list")
public String list(Model model, Page page) {
PageHelper.offsetPage(page.getStart(), page.getCount());
List<Order> os = orderService.list();
int total = (int) new PageInfo<>(os).getTotal();
page.setTotal(total);
orderItemService.fill(os);
model.addAttribute("os", os);
model.addAttribute("page", page);
return "admin/listOrder";
}
/**
* 發貨
* @param o 訂單對象
* @return 頁面路徑
* @throws IOException
*/
@RequestMapping("admin_order_delivery")
public String delivery(Order o) throws IOException {
o.setDeliveryDate(new Date());
o.setStatus(OrderService.waitConfirm);
orderService.update(o);
return "redirect:admin_order_list";
}
}
```
## 步驟 12 : listOrder.jsp
增加listOrder.jsp。
## 步驟 13 : 查詢功能講解
訪問地址:
`http://127.0.0.1:8080/tmall_ssm/admin_order_list`
即可看到訂單查詢界面。
`admin_order_list `導致`OrderController.list()`方法被調用
1. 獲取分頁對象
2. 查詢訂單集合
3. 獲取訂單總數并設置在分頁對象上
4. 借助`orderItemService.fill()`方法為這些訂單填充上orderItems信息
5. 把訂單集合和分頁對象設置在model上
6. 服務端跳轉到`admin/listOrder.jsp`頁面
7. 在`listOrder.jsp`借助`c:forEach`把訂單集合遍歷出來
8. 遍歷訂單的時候,再把當前訂單的orderItem訂單項集合遍歷出來

OrderController:
```
/**
* 分頁查詢
* @param model 模型
* @param page 頁面對象
* @return 集合
*/
@RequestMapping("admin_order_list")
public String list(Model model, Page page) {
PageHelper.offsetPage(page.getStart(), page.getCount());
List<Order> os = orderService.list();
int total = (int) new PageInfo<>(os).getTotal();
page.setTotal(total);
orderItemService.fill(os);
model.addAttribute("os", os);
model.addAttribute("page", page);
return "admin/listOrder";
}
```
listOrder.jsp:
```
<c:forEach items="${os}" var="o">
<tr>
<td>${o.id}</td>
<td>${o.statusDesc}</td>
<td>¥${o.total}</td>
<td align="center">${o.totalNumber}</td>
<td align="center">${o.user.name}</td>
<td>${o.createDate}</td>
<td>${o.payDate}</td>
<td>${o.deliveryDate}</td>
<td>${o.confirmDate}</td>
<td>
<button oid=${o.id} class="orderPageCheckOrderItems btn btn-primary btn-xs">查看詳情</button>
<c:if test="${o.status=='waitDelivery'}">
<a href="admin_order_delivery?id=${o.id}">
<button class="btn btn-primary btn-xs">發貨</button>
</a>
</c:if>
</td>
</tr>
<tr class="orderPageOrderItemTR" oid=${o.id}>
<td colspan="10" align="center">
<div class="orderPageOrderItem">
<table width="800px" align="center" class="orderPageOrderItemTable">
<c:forEach items="${o.orderItems}" var="oi">
<tr>
<td align="left">
<img width="40px" height="40px" src="img/productSingle/${oi.product.firstProductImage.id}.jpg">
</td>
<td>
<a href="foreproduct?pid=${oi.product.id}">
<span>${oi.product.name}</span>
</a>
</td>
<td align="right">
<span class="text-muted">${oi.number}個</span>
</td>
<td align="right">
<span class="text-muted">單價:¥${oi.product.promotePrice}</span>
</td>
</tr>
</c:forEach>
</table>
</div>
</td>
</tr>
</c:forEach>
```
## 步驟 14 : 發貨功能講解
當訂單狀態是waitDelivery的時候,就會出現發貨按鈕
1. 發貨按鈕鏈接跳轉到`admin_order_delivery`
2. `OrderController.delivery()`方法被調用
2.1 注入訂單對象
2.2 修改發貨時間,設置發貨狀態
2.3 更新到數據庫
2.4 客戶端跳轉到`admin_order_list`頁面

```
/**
* 發貨
* @param o 訂單對象
* @return 頁面路徑
* @throws IOException
*/
@RequestMapping("admin_order_delivery")
public String delivery(Order o) throws IOException {
o.setDeliveryDate(new Date());
o.setStatus(OrderService.waitConfirm);
orderService.update(o);
return "redirect:admin_order_list";
}
```
## 步驟 15 : 增加,修改,刪除功能
訂單的增加和刪除功能交由前臺完成,后臺不提供
- 項目簡介
- 功能一覽
- 前臺
- 后臺
- 開發流程
- 需求分析-展示
- 首頁
- 產品頁
- 分類頁
- 搜索結果頁
- 購物車查看頁
- 結算頁
- 確認支付頁
- 支付成功頁
- 我的訂單頁
- 確認收貨頁
- 確認收貨成功頁
- 評價頁
- 需求分析-交互
- 分類頁排序
- 立即購買
- 加入購物車
- 調整訂單項數量
- 刪除訂單項
- 生成訂單
- 訂單頁功能
- 確認付款
- 確認收貨
- 提交評價信息
- 登錄
- 注冊
- 退出
- 搜索
- 前臺需求列表
- 需求分析后臺
- 分類管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性設置
- 用戶管理
- 訂單管理
- 后臺需求列表
- 表結構設計
- 數據建模
- 表與表之間的關系
- 后臺-分類管理
- 可運行的項目
- 靜態資源
- JSP包含關系
- 查詢
- 分頁
- 增加
- 刪除
- 編輯
- 修改
- 做一遍
- 重構
- 分頁方式
- 分類逆向工程
- 所有逆向工程
- 后臺其他頁面
- 屬性管理實現
- 產品管理實現
- 產品圖片管理實現
- 產品屬性值設置
- 用戶管理實現
- 訂單管理實現
- 前端
- 前臺-首頁
- 可運行的項目
- 靜態資源
- ForeController
- home方法
- home.jsp
- homePage.jsp
- 前臺-無需登錄
- 注冊
- 登錄
- 退出
- 產品頁
- 模態登錄
- 分類頁
- 搜索
- 前臺-需要登錄
- 購物流程
- 立即購買
- 結算頁面
- 加入購物車
- 查看購物車頁面
- 登錄狀態攔截器
- 其他攔截器
- 購物車頁面操作
- 訂單狀態圖
- 生成訂單
- 我的訂單頁
- 我的訂單頁操作
- 評價產品
- 總結