創建商品表及商品分類表
# 商品表
CREATE TABLE `t_product` (
`id` int(20) NOT NULL COMMENT '商品id',
`category_id` int(20) DEFAULT NULL COMMENT '分類id',
`item_type` varchar(100) DEFAULT NULL COMMENT '商品系列',
`title` varchar(100) DEFAULT NULL COMMENT '商品標題',
`sell_point` varchar(150) DEFAULT NULL COMMENT '商品賣點',
`price` bigint(20) DEFAULT NULL COMMENT '商品單價',
`num` int(10) DEFAULT NULL COMMENT '庫存數量',
`image` varchar(500) DEFAULT NULL COMMENT '圖片路徑',
`status` int(1) DEFAULT 1 COMMENT '商品狀態 1:上架 2:下架 3:刪除',
`priority` int(10) DEFAULT NULL COMMENT '顯示優先級',
`created_time` datetime DEFAULT NULL COMMENT '創建時間',
`modified_time` datetime DEFAULT NULL COMMENT '最後修改時間',
`created_user` varchar(50) DEFAULT NULL COMMENT '創建人',
`modified_user` varchar(50) DEFAULT NULL COMMENT '最後修改人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
# 商品分類表
分類表(id,title,image,desc,isdelete,created_user,created_time,modified_user,modified_time)
54. 將商品相關頁面添加到攔截器白名單
當前項目中的登錄攔截器默認是攔截/**
路徑 ,則所有請求路徑默認都需要登錄,而主頁、商品詳情等頁面應該不需要登錄就可以訪問,所以,需要將/web/index.html
、/web/product.html
添加到配置攔截器的白名單中!
55. 創建商品數據的實體類
在cn.demo.store.entity
包中創建Product
實體類:
/**
* 商品數據的實體類
*/
public class Product extends BaseEntity {
private static final long serialVersionUID = 6884621327188442341L;
private Integer id;
private Integer categoryId;
private String itemType;
private String title;
private String sellPoint;
private Long price;
private Integer num;
private String image;
private Integer status;
private Integer priority;
// 生成get/set方法,toString()方法,hashCode/equals等方法
}
56. 主頁-熱銷排行-持久層
(a) 規劃需要的SQL語句
查詢熱銷排行商品的SQL語句是(此頁面熱銷商品只顯示4個):
select * from t_product where status=1 order by priority desc limit 0,4
(b) 設計抽象方法
在cn.demo.store.mapper
包下創建ProductMapper
接口,並添加抽象方法:
/**
* 處理商品數據的持久層接口
*/
public interface ProductMapper {
/**
* 查詢熱銷商品排行的前4個商品
* @return 熱銷商品排行的前4個商品
*/
List<Product> findHotList();
}
© 配置映射並測試
在src/main/resources/mappers下創建ProductMapper.xml,用於配置以上抽象方法的映射:
<mapper namespace="cn.demo.store.mapper.ProductMapper">
<resultMap type="cn.demo.store.entity.Product" id="ProductEntityMap">
<id column="id" property="id"/>
<result column="category_id" property="categoryId"/>
<result column="item_type" property="itemType"/>
<result column="sell_point" property="sellPoint"/>
<result column="created_user" property="createdUser"/>
<result column="created_time" property="createdTime"/>
<result column="modified_user" property="modifiedUser"/>
<result column="modified_time" property="modifiedTime"/>
</resultMap>
<!-- 查詢熱銷商品排行的前4個商品 -->
<!-- List<Product> findHotList() -->
<select id="findHotList" resultMap="ProductEntityMap">
SELECT * FROM t_product WHERE status=1 ORDER BY priority DESC LIMIT 0, 4
</select>
</mapper>
在src/test/java的cn.demo.store.mapper
包中創建ProductMapperTests
測試類,測試以上抽象方法:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductMapperTests {
@Autowired
private ProductMapper mapper;
@Test
public void findByParent() {
List<Product> list = mapper.findHotList();
System.err.println("count=" + list.size());
for (Product item : list) {
System.err.println(item);
}
}
}
57. 主頁-熱銷排行-業務層
(a) 規劃業務流程、業務邏輯,並創建可能出現的異常
無
(b) 設計抽象方法
在cn.demo.store.service
包中創建ProductService
接口,添加抽象方法:
/**
* 處理商品數據的業務層接口
*/
public interface ProductService {
/**
* 查詢熱銷商品排行的前4個商品
* @return 熱銷商品排行的前4個商品
*/
List<Product> getHotList();
}
© 實現抽象方法並測試
在cn.demo.store.service.impl
包中創建ProductServiceImpl
,實現ProductService
接口,在類的聲明之前添加@Service
註解,在類中添加@Autowired private ProductMapper productMapper;
持久層對象:
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
}
在具體實現接口的抽象方法之前,先將持久層中的方法複製到業務層實現類,並改爲私有方法,並實現:
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
private List<Product> findHotList() {
return productMapper.findHotList();
}
}
然後,再重寫接口中的抽象方法,抽象方法將依賴於私有方法來實現:
/**
* 處理商品數據的業務層實現類
*/
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public List<Product> getHotList() {
List<Product> products = findHotList();
// 將不需要響應給前端的數據設置爲null
for (Product product : products) {
product.setCategoryId(null);
product.setStatus(null);
product.setPriority(null);
product.setCreatedUser(null);
product.setCreatedTime(null);
product.setModifiedUser(null);
product.setModifiedTime(null);
}
return products;
}
private List<Product> findHotList() {
return productMapper.findHotList();
}
}
完成後,在src/test/java的cn.demo.store.service
包中創建ProductServiceTests
測試類,測試以上方法:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceTests {
@Autowired
private ProductService service;
@Test
public void getHotList() {
List<Product> list = service.getHotList();
System.err.println("count=" + list.size());
for (Product item : list) {
System.err.println(item);
}
}
}
58. 主頁-熱銷排行-控制器層
(a) 處理新創建的異常類型
(b) 設計需要處理的請求
- 請求路徑:
/products/list/hot
- 請求參數:無
- 請求方式:
GET
- 響應數據:
JsonResult<>
© 處理請求並測試
在cn.demo.store.controller
中創建ProductController
,並處理請求:
@RestController
@RequestMapping("products")
public class ProductController extends BaseController {
@Autowired
private ProductService productService;
// http://localhost:8080/products/list/hot
@GetMapping("list/hot")
public JsonResult<List<Product>> getHotList() {
List<Product> data = productService.getHotList();
return new JsonResult<>(OK, data);
}
}
在攔截器的配置中,添加新的白名單:
patterns.add("/products/**");
完成後,打開瀏覽器進行測試。
59. 主頁-熱銷排行-前端頁面
先將頁面中第180行的``標籤設置id="hot-list"
,它是顯示熱銷排行數據的區域的容器,然後,再添加代碼加載數據並顯示
<script type="text/javascript">
$(document).ready(function() {
showHotList();
});
function showHotList() {
console.log("準備熱銷商品列表……");
$("#hot-list").empty();
$.ajax({
"url":"/products/list/hot",
"type":"get",
"dataType":"json",
"success":function(json) {
var list = json.data;
console.log("count=" + list.length);
for (var i = 0; i < list.length; i++) {
console.log(list[i].title);
var html = '<div class="col-md-12">'
+ '<div class="col-md-7 text-row-2"><a href="product.html">#{title}</a></div>'
+ '<div class="col-md-2">¥#{price}</div>'
+ '<div class="col-md-3"><img src="#{image}collect.png" class="img-responsive" /></div>'
+ '</div>';
html = html.replace(/#{title}/g, list[i].title);
html = html.replace(/#{price}/g, list[i].price);
html = html.replace(/#{image}/g, list[i].image);
$("#hot-list").append(html);
}
}
});
}
</script>
60. 商品-顯示詳情-持久層
(a) 規劃需要的SQL語句
查詢商品詳情的SQL語句大致是:
select * from t_product where id=?
(b) 設計抽象方法
在ProductMapper
接口中添加:
/**
* 根據商品id查詢商品詳情
* @param id 商品id
* @return 匹配的商品詳情,如果沒有匹配的數據,則返回null
*/
Product findById(Integer id);
© 配置映射並測試
在ProductMapper.xml中配置映射:
<!-- 根據商品id查詢商品詳情 -->
<!-- Product findById(Integer id) -->
<select id="findById" resultMap="ProductEntityMap">
SELECT * FROM t_product WHERE id=#{id}
</select>
在ProductMapperTests
中測試:
@Test
public void findById() {
Integer id = 10000017;
Product result = mapper.findById(id);
System.err.println(result);
}
61. 商品-顯示詳情-業務層
(a) 規劃業務流程、業務邏輯,並創建可能出現的異常
需要創建ProductNotFoundException
。
(b) 設計抽象方法
在ProductService
接口中添加:
/**
* 根據商品id查詢商品詳情
* @param id 商品id
* @return 匹配的商品詳情
*/
Product getById(Integer id);
© 實現抽象方法並測試
在ProductServiceImpl
中,先將持久層接口中的方法複製過來,改爲私有方法並實現:
/**
* 根據商品id查詢商品詳情
* @param id 商品id
* @return 匹配的商品詳情,如果沒有匹配的數據,則返回null
*/
private Product findById(Integer id) {
return productMapper.findById(id);
}
然後,重寫接口中的抽象方法:
@Override
public Product getById(Integer id) {
// 調用自身私有方法查詢數據
Product product = findById(id);
// 檢查查詢結果數據是否爲null
if (product == null) {
// 是:拋出ProductNotFoundException
throw new ProductNotFoundException("獲取商品詳情失敗!嘗試訪問的商品數據不存在!");
}
// 將不必要響應給客戶端的屬性值設爲null
product.setCategoryId(null);
product.setStatus(null);
product.setPriority(null);
product.setCreatedUser(null);
product.setCreatedTime(null);
product.setModifiedUser(null);
product.setModifiedTime(null);
// 返回數據
return product;
}
最後,在ProductServiceTests
中測試:
@Test
public void getById() {
try {
Integer id = 10080017;
Product result = service.getById(id);
System.err.println(result);
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
62. 商品-顯示詳情-控制器層
(a) 處理新創建的異常類型
需要處理ProductNotFoundException
。
(b) 設計需要處理的請求
- 請求路徑:
/products/{id}/details
- 請求參數:
@PathVariable("id") Integer id
- 請求方式:
GET
- 響應數據:
JsonResult
© 處理請求並測試
在ProductController
中添加處理請求的方法:
// http://localhost:8080/products/10000017/details
@GetMapping("{id}/details")
public JsonResult<Product> getById(@PathVariable("id") Integer id) {
Product data = productService.getById(id);
return new JsonResult<>(OK, data);
}
63. 商品-顯示詳情-前端頁面
創建js文件jquery-getUrlParam.js(此文件是從頁面讀取所需的參數)
(function ($) {
$.getUrlParam = function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null)
return unescape(r[2]);
return null;
}
})(jQuery);
在product.html中添加代碼:
<script type="text/javascript" src="../js/jquery-getUrlParam.js"></script>
<script type="text/javascript">
var id = $.getUrlParam("id");
$(document).ready(function() {
$.ajax({
"url":"/products/" + id + "/details",
"type":"get",
"dataType":"json",
"success":function(json) {
if (json.state == 2000) {
$("#product-title").html(json.data.title);
$("#product-sell-point").html(json.data.sellPoint)
$("#product-price").html(json.data.price);
// $("#product-image-5-big").attr("src", json.data.image + "5_big.png");
// $("#product-image-5").attr("src", json.data.image + "5.jpg");
for (var i = 1; i <= 5; i++) {
$("#product-image-" + i + "-big").attr("src", json.data.image + i + "_big.png");
$("#product-image-" + i).attr("src", json.data.image + i + ".jpg");
}
} else {
alert(json.message);
// location.href = "index.html";
}
}
});
});
</script>