[TOC]
## 步驟 1 : 先運行,看到效果,再學習
先將完整的 tmall_ssm 項目(向老師要相關資料),配置運行起來,確認可用之后,再學習做了哪些步驟以達到這樣的效果。
## 步驟 2 : 模仿和排錯
在確保可運行項目能夠正確無誤地運行之后,再嚴格照著教程的步驟,對代碼模仿一遍。
模仿過程難免代碼有出入,導致無法得到期望的運行結果,此時此刻通過比較**正確答案** ( 可運行項目 ) 和自己的代碼,來定位問題所在。
采用這種方式,**學習有效果,排錯有效率**,可以較為明顯地提升學習速度,跨過學習路上的各個檻。
## 步驟 3 : 運行效果
立即購買之后,在頁面上會報錯,因為截至目前的學習進度,購買成功之后跳轉到的結算頁面還沒有開發,將在后續知識點開發。
`HTTP Status 404 - /tmall_ssm/forebuy`
那么點擊購買都做了什么事情呢? 會在t_order_item表里插入一條數據,舉例說明,這條數據會表示:
1. pid =844 購買的商品id
2. oid = null, 這個訂單項還沒有生成對應的訂單,即還在購物車中
3. uid= 3,用戶的id是3
4. number=3, 購買了3件產品

## 步驟 4 : 在產品頁點擊立即購買
如果未登錄,那么點擊立即購買之前會彈出`模態登錄窗口`,關于這個功能的詳細介紹在**模態登錄**,在此不做贅述。
登錄之后,點擊立即購買,會訪問地址
http://127.0.0.1:8080/tmall_ssm/forebuyone?pid=844&num=3
并帶上了產品id 844和購買數量3

## 步驟 5 : OrderItemService
為OrderItemService新增方法listByUser
~~~
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);
int getSaleCount(int pid);
List<OrderItem> listByUser(int uid);
}
~~~
## 步驟 6 : OrderItemServiceImpl
為OrderItemServiceImpl新增加方法listByUser的實現
~~~
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);
}
@Override
public int getSaleCount(int pid) {
OrderItemExample example = new OrderItemExample();
example.createCriteria().andProductIdEqualTo(pid);
List<OrderItem> ois = orderItemMapper.selectByExample(example);
int result = 0;
for (OrderItem oi : ois) {
result += oi.getNumber();
}
return result;
}
@Override
public List<OrderItem> listByUser(int uid) {
OrderItemExample example =new OrderItemExample();
example.createCriteria().andUserIdEqualTo(uid).andOrderIdIsNull();
List<OrderItem> result =orderItemMapper.selectByExample(example);
setProduct(result);
return result;
}
}
~~~
## 步驟 7 : ForeController.buyone()
通過上個步驟訪問的地址 /forebuyone 導致ForeController.buyone()方法被調用
1. 獲取參數pid
2. 獲取參數num
3. 根據pid獲取產品對象p
4. 從session中獲取用戶對象user
接下來就是新增訂單項OrderItem, 新增訂單項要考慮兩個情況
a. 如果已經存在這個產品對應的OrderItem,并且還沒有生成訂單,即還在購物車中。 那么就應該在對應的OrderItem基礎上,調整數量
a.1 基于用戶對象user,查詢沒有生成訂單的訂單項集合
a.2 遍歷這個集合
a.3 如果產品是一樣的話,就進行數量追加
a.4 獲取這個訂單項的 id
b. 如果不存在對應的OrderItem,那么就新增一個訂單項OrderItem
b.1 生成新的訂單項
b.2 設置數量,用戶和產品
b.3 插入到數據庫
b.4 獲取這個訂單項的 id
最后, 基于這個訂單項id客戶端跳轉到結算頁面/forebuy
~~~
package com.dodoke.tmall.controller;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpSession;
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 org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;
import com.dodoke.tmall.comparator.ProductAllComparator;
import com.dodoke.tmall.comparator.ProductDateComparator;
import com.dodoke.tmall.comparator.ProductPriceComparator;
import com.dodoke.tmall.comparator.ProductReviewComparator;
import com.dodoke.tmall.comparator.ProductSaleCountComparator;
import com.dodoke.tmall.pojo.Category;
import com.dodoke.tmall.pojo.OrderItem;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.pojo.ProductImage;
import com.dodoke.tmall.pojo.PropertyValue;
import com.dodoke.tmall.pojo.Review;
import com.dodoke.tmall.pojo.User;
import com.dodoke.tmall.service.CategoryService;
import com.dodoke.tmall.service.OrderItemService;
import com.dodoke.tmall.service.OrderService;
import com.dodoke.tmall.service.ProductImageService;
import com.dodoke.tmall.service.ProductService;
import com.dodoke.tmall.service.PropertyValueService;
import com.dodoke.tmall.service.ReviewService;
import com.dodoke.tmall.service.UserService;
import com.github.pagehelper.PageHelper;
@Controller
@RequestMapping("")
public class ForeController {
@Autowired
CategoryService categoryService;
@Autowired
ProductService productService;
@Autowired
UserService userService;
@Autowired
ProductImageService productImageService;
@Autowired
PropertyValueService propertyValueService;
@Autowired
OrderService orderService;
@Autowired
OrderItemService orderItemService;
@Autowired
ReviewService reviewService;
@RequestMapping("forehome")
public String home(Model model) {
List<Category> cs = categoryService.list();
productService.fill(cs);
productService.fillByRow(cs);
model.addAttribute("cs", cs);
return "fore/home";
}
@RequestMapping("foreregister")
public String register(Model model, User user) {
String name = user.getName();
// 把賬號里的特殊符號進行轉義
name = HtmlUtils.htmlEscape(name);
user.setName(name);
boolean exist = userService.isExist(name);
if (exist) {
String m = "用戶名已經被使用,不能使用";
model.addAttribute("msg", m);
model.addAttribute("user", null);
return "fore/register";
}
userService.add(user);
return "redirect:registerSuccessPage";
}
@RequestMapping("forelogin")
public String login(@RequestParam("name") String name, @RequestParam("password") String password, Model model,
HttpSession session) {
name = HtmlUtils.htmlEscape(name);
User user = userService.get(name, password);
if (null == user) {
model.addAttribute("msg", "賬號密碼錯誤");
return "fore/login";
}
session.setAttribute("user", user);
return "redirect:forehome";
}
@RequestMapping("forelogout")
public String logout(HttpSession session) {
session.removeAttribute("user");
return "redirect:forehome";
}
@RequestMapping("foreproduct")
public String product(int pid, Model model) {
Product p = productService.get(pid);
// 根據對象p,獲取這個產品對應的單個圖片集合
List<ProductImage> productSingleImages = productImageService.list(p.getId(), ProductImageService.type_single);
// 根據對象p,獲取這個產品對應的詳情圖片集合
List<ProductImage> productDetailImages = productImageService.list(p.getId(), ProductImageService.type_detail);
p.setProductSingleImages(productSingleImages);
p.setProductDetailImages(productDetailImages);
// 獲取產品的所有屬性值
List<PropertyValue> pvs = propertyValueService.list(p.getId());
// 獲取產品對應的所有的評價
List<Review> reviews = reviewService.list(p.getId());
// 設置產品的銷量和評價數量
productService.setSaleAndReviewNumber(p);
model.addAttribute("reviews", reviews);
model.addAttribute("p", p);
model.addAttribute("pvs", pvs);
return "fore/product";
}
@RequestMapping("forecheckLogin")
@ResponseBody
public String checkLogin(HttpSession session) {
User user = (User) session.getAttribute("user");
if (null != user) {
return "success";
}
return "fail";
}
@RequestMapping("foreloginAjax")
@ResponseBody
public String loginAjax(@RequestParam("name") String name, @RequestParam("password") String password,
HttpSession session) {
name = HtmlUtils.htmlEscape(name);
User user = userService.get(name, password);
if (null == user) {
return "fail";
}
session.setAttribute("user", user);
return "success";
}
@RequestMapping("forecategory")
public String category(int cid, String sort, Model model) {
Category c = categoryService.get(cid);
productService.fill(c);
productService.setSaleAndReviewNumber(c.getProducts());
if (null != sort) {
switch (sort) {
case "review":
Collections.sort(c.getProducts(), new ProductReviewComparator());
break;
case "date":
Collections.sort(c.getProducts(), new ProductDateComparator());
break;
case "saleCount":
Collections.sort(c.getProducts(), new ProductSaleCountComparator());
break;
case "price":
Collections.sort(c.getProducts(), new ProductPriceComparator());
break;
case "all":
Collections.sort(c.getProducts(), new ProductAllComparator());
break;
}
}
model.addAttribute("c", c);
return "fore/category";
}
@RequestMapping("foresearch")
public String search(String keyword, Model model) {
PageHelper.offsetPage(0, 20);
List<Product> ps = productService.search(keyword);
productService.setSaleAndReviewNumber(ps);
model.addAttribute("ps", ps);
return "fore/searchResult";
}
@RequestMapping("forebuyone")
public String buyone(int pid, int num, HttpSession session) {
Product p = productService.get(pid);
int oiid = 0;
User user = (User) session.getAttribute("user");
boolean found = false;
List<OrderItem> ois = orderItemService.listByUser(user.getId());
for (OrderItem oi : ois) {
if (oi.getProduct().getId().intValue() == p.getId().intValue()) {
oi.setNumber(oi.getNumber() + num);
orderItemService.update(oi);
found = true;
oiid = oi.getId();
break;
}
}
if (!found) {
OrderItem oi = new OrderItem();
oi.setUserId(user.getId());
oi.setNumber(num);
oi.setProductId(pid);
orderItemService.add(oi);
oiid = oi.getId();
}
return "redirect:forebuy?oiid=" + oiid;
}
}
~~~
> oiid 接下來的知識點就會講解
## 拓展
如果模仿現在的天貓網站,立即購買和購物車沒有關聯,思路如何實現呢?
> 提示: 可以為 orderItem 增加一個 type 字段, 用來標記是從 立即購買創建的,還是加入購物車創建的。
在購物車頁面,之顯示后者,不顯示前者即可。
- 項目簡介
- 功能一覽
- 前臺
- 后臺
- 開發流程
- 需求分析-展示
- 首頁
- 產品頁
- 分類頁
- 搜索結果頁
- 購物車查看頁
- 結算頁
- 確認支付頁
- 支付成功頁
- 我的訂單頁
- 確認收貨頁
- 確認收貨成功頁
- 評價頁
- 需求分析-交互
- 分類頁排序
- 立即購買
- 加入購物車
- 調整訂單項數量
- 刪除訂單項
- 生成訂單
- 訂單頁功能
- 確認付款
- 確認收貨
- 提交評價信息
- 登錄
- 注冊
- 退出
- 搜索
- 前臺需求列表
- 需求分析后臺
- 分類管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性設置
- 用戶管理
- 訂單管理
- 后臺需求列表
- 表結構設計
- 數據建模
- 表與表之間的關系
- 后臺-分類管理
- 可運行的項目
- 靜態資源
- JSP包含關系
- 查詢
- 分頁
- 增加
- 刪除
- 編輯
- 修改
- 做一遍
- 重構
- 分頁方式
- 分類逆向工程
- 所有逆向工程
- 后臺其他頁面
- 屬性管理實現
- 產品管理實現
- 產品圖片管理實現
- 產品屬性值設置
- 用戶管理實現
- 訂單管理實現
- 前端
- 前臺-首頁
- 可運行的項目
- 靜態資源
- ForeController
- home方法
- home.jsp
- homePage.jsp
- 前臺-無需登錄
- 注冊
- 登錄
- 退出
- 產品頁
- 模態登錄
- 分類頁
- 搜索
- 前臺-需要登錄
- 購物流程
- 立即購買
- 結算頁面
- 加入購物車
- 查看購物車頁面
- 登錄狀態攔截器
- 其他攔截器
- 購物車頁面操作
- 訂單狀態圖
- 生成訂單
- 我的訂單頁
- 我的訂單頁操作
- 評價產品
- 總結