ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 步骤 1 : 先运行,看到效果,再学习 先将完整的 tmall_ssm 项目(向老师要相关资料),配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。 ## 步骤 2 : 模仿和排错 在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较**正确答案** ( 可运行项目 ) 和自己的代码,来定位问题所在。 采用这种方式,**学习有效果,排错有效率**,可以较为明显地提升学习速度,跨过学习路上的各个槛。 ## 步骤 3 : 界面效果 ![](https://box.kancloud.cn/68d1ddb918ded2961f08416e81579817_1514x897.png) ## 步骤 4 : search.jsp以及simpleSearch.jsp 每个页面都包含了搜索的jsp,首页和搜索结果页包含的是search.jsp,其他页面包含的是simpleSearch.jsp。 这两个页面都提供了一个form,提交数据keyword到foresearch这个路径。 search.jsp ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <a href="${contextPath}"> <img id="logo" src="img/site/logo.gif" class="logo"> </a> <form action="foresearch" method="post" > <div class="searchDiv"> <input name="keyword" type="text" placeholder="时尚男鞋 太阳镜 "> <button type="submit" class="searchButton">搜索</button> <div class="searchBelow"> <c:forEach items="${cs}" var="c" varStatus="st"> <c:if test="${st.count>=5 and st.count<=8}"> <span> <a href="forecategory?cid=${c.id}"> ${c.name} </a> <c:if test="${st.count!=8}"> <span>|</span> </c:if> </span> </c:if> </c:forEach> </div> </div> </form> ~~~ simpleSearch.jsp: ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div > <a href="${contextPath}"> <img id="simpleLogo" class="simpleLogo" src="img/site/simpleLogo.png"> </a> <form action="foresearch" method="post" > <div class="simpleSearchDiv pull-right"> <input type="text" placeholder="平衡车 原汁机" name="keyword"> <button class="searchButton" type="submit">搜天猫</button> <div class="searchBelow"> <c:forEach items="${cs}" var="c" varStatus="st"> <c:if test="${st.count>=8 and st.count<=11}"> <span> <a href="forecategory?cid=${c.id}"> ${c.name} </a> <c:if test="${st.count!=11}"> <span>|</span> </c:if> </span> </c:if> </c:forEach> </div> </div> </form> <div style="clear:both"></div> </div> ~~~ ## 步骤 5 : ForeController.search() 通过search.jsp或者simpleSearch.jsp提交数据到路径 /foresearch, 导致ForeController.search()方法被调用 1. 获取参数keyword 2. 根据keyword进行模糊查询,获取满足条件的前20个产品 3. 为这些产品设置销量和评价数量 4. 把产品结合设置在model的"ps"属性上 5. 服务端跳转到 searchResult.jsp 页面 ~~~ 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.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"; } } ~~~ ## 步骤 6 : ProductService 修改ProductService,增加search方法 ~~~ package com.dodoke.tmall.service; import java.util.List; import com.dodoke.tmall.pojo.Category; import com.dodoke.tmall.pojo.Product; public interface ProductService { void add(Product c); void delete(int id); void update(Product c); Product get(int id); List list(int categoryId); void setFirstProductImage(Product p); /** * 为分类填充产品集合 * * @param categorys */ public void fill(List<Category> categorys); /** * 为多个分类填充产品集合 * * @param category */ public void fill(Category category); /** * 为多个分类填充推荐产品集合,即把分类下的产品集合,按照8个为一行,拆成多行,以利于后续页面上进行显示 * * @param categorys */ public void fillByRow(List<Category> categorys); /** * 为产品设置销量和评价数量 * * @param p * 产品对象 */ void setSaleAndReviewNumber(Product p); void setSaleAndReviewNumber(List<Product> ps); /** * 根据keyword进行模糊查询 * * @param keyword * 搜索关键字 * @return List<Product> */ List<Product> search(String keyword); } ~~~ ## 步骤 7 : ProductServiceImpl 修改ProductServiceImpl实现search方法,通过关键字进行模糊查询 ~~~ package com.dodoke.tmall.service.impl; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.dodoke.tmall.mapper.ProductMapper; import com.dodoke.tmall.pojo.Category; import com.dodoke.tmall.pojo.Product; import com.dodoke.tmall.pojo.ProductExample; import com.dodoke.tmall.pojo.ProductImage; import com.dodoke.tmall.service.CategoryService; import com.dodoke.tmall.service.OrderItemService; import com.dodoke.tmall.service.ProductImageService; import com.dodoke.tmall.service.ProductService; import com.dodoke.tmall.service.ReviewService; @Service public class ProductServiceImpl implements ProductService { @Autowired ProductMapper productMapper; @Autowired CategoryService categoryService; @Autowired ProductImageService productImageService; @Autowired OrderItemService orderItemService; @Autowired ReviewService reviewService; @Override public void add(Product p) { p.setCreateDate(new Date()); productMapper.insert(p); } @Override public void delete(int id) { productMapper.deleteByPrimaryKey(id); } @Override public void update(Product p) { productMapper.updateByPrimaryKeySelective(p); } @Override public Product get(int id) { Product p = productMapper.selectByPrimaryKey(id); setFirstProductImage(p); setCategory(p); return p; } private void setCategory(Product p) { int categoryId = p.getCategoryId(); Category category = categoryService.get(categoryId); p.setCategory(category); } @Override public List list(int categoryId) { ProductExample example = new ProductExample(); example.createCriteria().andCategoryIdEqualTo(categoryId); example.setOrderByClause("id desc"); List result = productMapper.selectByExample(example); setFirstProductImage(result); setCategory(result); return result; } public void setCategory(List<Product> ps) { for (Product p : ps) { setCategory(p); } } /** * 根据productId和图片类型查询出所有的单个图片,然后把第一个取出来放在firstProductImage上。 * * @param p * 产品 */ @Override public void setFirstProductImage(Product p) { List<ProductImage> pis = productImageService.list(p.getId(), ProductImageService.type_single); if (!pis.isEmpty()) { ProductImage pi = pis.get(0); p.setFirstProductImage(pi); } } /** * 给多个产品设置图片 * * @param ps * 产品集合 */ public void setFirstProductImage(List<Product> ps) { for (Product p : ps) { setFirstProductImage(p); } } /** * 为分类填充产品集合 * * @param categorys */ @Override public void fill(Category c) { List<Product> ps = list(c.getId()); c.setProducts(ps); } /** * 为多个分类填充产品集合 * * @param category */ @Override public void fill(List<Category> cs) { for (Category c : cs) { fill(c); } } /** * 为多个分类填充推荐产品集合,即把分类下的产品集合,按照8个为一行,拆成多行,以利于后续页面上进行显示 * * @param categorys */ @Override public void fillByRow(List<Category> cs) { // 把分类下的产品集合,按照8个为一行,拆成多行,以利于后续页面上进行显示 int productNumberEachRow = 8; // 将categorylist中每个category拿出来循环 for (Category c : cs) { // 获取每个分类中对应的产品,在使用fillByRow(List<Category> // cs)这个方法前,需要先使用fill方法,注入分类中的所有产品,因此在这里才可以取出产品 List<Product> products = c.getProducts(); // 每一行产品的list List<List<Product>> productsByRow = new ArrayList<>(); for (int i = 0; i < products.size(); i += productNumberEachRow) { int size = i + productNumberEachRow; // 界限判断 size = size > products.size() ? products.size() : size; // 该方法返回的是父list的一个子集合,从fromIndex(包含),到toIndex(不包含) List<Product> productsOfEachRow = products.subList(i, size); productsByRow.add(productsOfEachRow); } c.setProductsByRow(productsByRow); } } @Override public void setSaleAndReviewNumber(Product p) { int saleCount = orderItemService.getSaleCount(p.getId()); p.setSaleCount(saleCount); int reviewCount = reviewService.getCount(p.getId()); p.setReviewCount(reviewCount); } @Override public void setSaleAndReviewNumber(List<Product> ps) { for (Product p : ps) { setSaleAndReviewNumber(p); } } @Override public List<Product> search(String keyword) { ProductExample example = new ProductExample(); example.createCriteria().andNameLike("%" + keyword + "%"); example.setOrderByClause("id desc"); List result = productMapper.selectByExample(example); setFirstProductImage(result); setCategory(result); return result; } } ~~~ ## 步骤 8 : searchResult.jsp 与 register.jsp 相仿,searchResult.jsp 也包含了header.jsp, top.jsp, search.jsp, footer.jsp 等公共页面。 中间是搜索结果业务页面 searchResultPage.jsp 注: 在search.jsp中,又把参数keyword显示在输入框中,因为是foresearch转发过来的,属于同一个request,可以使用param获取相应的值,`${param.xxx}`相当于`request.getParameter("xxx")`。 ~~~ <input name="keyword" type="text" value="${param.keyword}" placeholder="时尚男鞋 太阳镜 "> ~~~ searchResult.jsp: ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@include file="../include/fore/header.jsp"%> <%@include file="../include/fore/top.jsp"%> <%@include file="../include/fore/search.jsp"%> <%@include file="../include/fore/searchResultPage.jsp"%> <%@include file="../include/fore/footer.jsp"%> ~~~ ## 步骤 9 : searchResultPage.jsp searchResultPage.jsp 本身没做什么。。。。 直接包含了 **productsBySearch.jsp** ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div id="searchResult"> <div class="searchResultDiv"> <%@include file="productsBySearch.jsp"%> </div> </div> ~~~ ## 步骤 10 : productsBySearch.jsp productsBySearch.jsp 显示产寻结果: 1. 遍历ps,把每个产品的图片,价格,标题等信息显示出来 2. 如果ps为空,则显示 "没有满足条件的产品" ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%@ taglib prefix='fmt' uri="http://java.sun.com/jsp/jstl/fmt" %> <div class="searchProducts"> <c:forEach items="${ps}" var="p"> <div class="productUnit"> <a href="foreproduct?pid=${p.id}"> <img class="productImage" src="img/productSingle/${p.firstProductImage.id}.jpg"> </a> <span class="productPrice">¥<fmt:formatNumber type="number" value="${p.promotePrice}" minFractionDigits="2" maxFractionDigits="2"/></span> <a class="productLink" href="foreproduct?pid=${p.id}"> ${fn:substring(p.name, 0, 50)} </a> <a class="tmallLink" href="foreproduct?pid=${p.id}">天猫专卖</a> <div class="productInfo"> <span class="monthDeal ">月成交 <span class="productDealNumber">${p.saleCount}笔</span></span> <span class="productReview">评价<span class="productReviewNumber">${p.reviewCount}</span></span> <span class="wangwang"><img src="img/site/wangwang.png"></span> </div> </div> </c:forEach> <c:if test="${empty ps}"> <div class="noMatch">没有满足条件的产品<div> </c:if> <div style="clear:both"></div> </div> ~~~ > 拓展: > 1. 现在需要在搜索结果页,也要做类似分类页的排序,如何实现呢? > 2. 月成交量,最近30天内交易成功的订单量。也就是已确认收货的订单量。 这里后台是该的产品所有订单项的数目之和,自己**完善月成交功能**。