SpringBoot电脑商城项目--商品详情+加入购物车
商品详情
1. 持久层
1.1. 规划sql语句
根据id查询商品详情
1.2 mapper层编写抽象方法
/*** 根据商品id查询商品详情* @param id 商品id* @return 匹配的id商品详情,如果没有匹配的数据,则返回null*/Product findById(Integer id);
1.3 xml文件中编写sql映射
查到的数据要封装成resultMap结果集
<select id="findById" resultMap="ProductEntityMap">select * from t_product where id=#{id}</select>
2. 业务层
2.1 规划异常
根据id查询商品信息时可能会出现找不到的异常,需要定义一个ProductNotFoundException类
package com.cy.store.service.ex;/** 商品数据不存在的异常 */
public class ProductNotFoundException extends ServiceException {public ProductNotFoundException() {super();}public ProductNotFoundException(String message) {super(message);}public ProductNotFoundException(String message, Throwable cause) {super(message, cause);}public ProductNotFoundException(Throwable cause) {super(cause);}protected ProductNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}
2.2 在Service类编写接口和抽象方法
/** 根据商品id查询商品详情 */Product findById(Integer id);
2.3 实现类实现接口重写抽象方法
/*** 根据商品id查询商品详情* @param id 商品id* @return 匹配的id商品详情,如果没有匹配的数据,则返回null*/@Overridepublic Product findById(Integer id) {Product product = productMapper.findById(id);// 判断查询结果是否为nullif (product == null) {throw new ProductNotFoundException("尝试访问的商品数据不存在");}// 将查询结果中的部分属性设置为nullproduct.setPriority(null);product.setCreatedUser(null);product.setCreatedTime(null);product.setModifiedUser(null);product.setModifiedTime(null);return product;}
3. 控制层
3.1 捕获异常
在BaseController中捕获ProductNotFoundException异常
else if (e instanceof ProductNotFoundException){result.setState(4006);result.setMessage("商品数据不存在");}
3.2 controller实现请求
/*** 获取商品详情* @param id 商品id* @return 商品详情*/@RequestMapping("/details/{id}")public JsonResult<Product> getById(@PathVariable("id") Integer id){Product data = productService.findById(id);return new JsonResult<>(OK,data);}
4. 前端页面
product.html页面
<!-- 引入用于获取URL参数的jQuery插件,以便后续JS代码中获取商品ID --><script type="text/javascript" src="../js/jquery-getUrlParam.js"></script><script type="text/javascript">// 文档加载完成后执行以下代码$(document).ready(function () {// 使用 jQuery 插件 getUrlParam 获取 URL 中的 "id" 参数值,即商品IDvar id = $.getUrlParam("id");console.log("id=" + id);// 发起 AJAX 请求获取该商品的详细信息$.ajax({url: "/products/details/"+ id, // 请求地址type: "GET", // 请求方法为 GETsuccess: function (json) { // 请求成功时的回调函数if (json.state == 200) { // 如果返回状态码为 200,表示请求成功console.log("title=" + json.data.title);// 将商品标题、卖点、价格填充到页面对应元素中$("#product-title").html(json.data.title);$("#product-sell-point").html(json.data.sellPoint);$("#product-price").html(json.data.price);// 循环更新5张商品图片的路径(大图和缩略图)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 if (json.state == 4006) { // 如果商品数据不存在,则跳转回首页location.href = "index.html";} else { // 其他错误情况弹出提示信息alert("获取商品信息失败!" + json.message);}}});});</script>
加入购物车
1. 在数据库中创建购物车的表(t_cart)
CREATE TABLE t_cart (cid INT AUTO_INCREMENT COMMENT '购物车数据id',uid INT NOT NULL COMMENT '用户id',pid INT NOT NULL COMMENT '商品id',price BIGINT COMMENT '加入时商品单价',num INT COMMENT '商品数量',created_user VARCHAR(20) COMMENT '创建人',created_time DATETIME COMMENT '创建时间',modified_user VARCHAR(20) COMMENT '修改人',modified_time DATETIME COMMENT '修改时间',PRIMARY KEY (cid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 创建Cart实体类
@Data:是Lombok提供的注解,自动生成类的getter、setter、toString等方法
package com.cy.store.entity;import lombok.Data;/**购物车数据的实体类*/
@Data
public class Cart extends BaseEntity {private Integer cid;private Integer uid;private Integer pid;private Long price;private Integer num;
}
3. 持久层
3.1 规划sql语句
-
向购物车表中插入数据
insert into t_cart () value()
-
如果当前商品已经存在与购物车中,则直接更新数量num就行
update t_cart set
num=#{num},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where cid=#{cid}
-
在插入或者更新具体执行哪个语句,取决于数据库中是否有当前这个购物车商品的数量,得去查询才能确定
select * from t_cart where uid=#{uid} AND pid=#{pid}
3.2 创建CartMapper接口,编写抽象方法
@Mapper
public interface CartMapper {/*** 插入购物车数据* @param cart 购物车数据* @return 受影响的行数*/Integer insert(Cart cart);/*** 根据用户id和商品id修改购物车数据* @param cid 购物车id* @param num 修改之后的数量* @param modifiedUser 修改执行人* @param modifiedTime 修改时间* @return*/Integer updateNumByCid(Integer cid,Integer num,String modifiedUser,Date modifiedTime);/*** 根据用户id和商品id查询购物车数据* @param uid 用户id* @param pid 商品id* @return 匹配的购物车数据,如果没有匹配的数据则返回null*/Cart findByUidAndPid(Integer uid, Integer pid);}
3.3 在CartMapper.xml文件中进行sql映射以及sql的编写
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.CartMapper"><resultMap id="CartEntityMap" type="com.cy.store.entity.Cart"><id column="cid" property="cid"/><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><!-- 插入购物车数据--><insert id="insert" useGeneratedKeys="true" keyProperty="cid">insert into t_cart (uid, pid, price, num, created_user, created_time, modified_user, modified_time)values (#{uid}, #{pid}, #{price}, #{num}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})</insert><!-- 修改购物车数据中商品的数量--><update id="updateNumByCid">update t_cart setnum=#{num},modified_user=#{modifiedUser},modified_time=#{modifiedTime}where cid=#{cid}</update><!-- 根据用户id和商品id查询购物车中的数据--><select id="findByUidAndPid" resultMap="CartEntityMap">select * from t_cart where uid=#{uid} AND pid=#{pid}</select>
</mapper>
3.4 在CartMapperTest类中进行测试
@SpringBootTest
class CartMapperTest {@Autowiredprivate CartMapper cartMapper;@Testpublic void insert() {Cart cart = new Cart();cart.setUid(1);cart.setPid(2);cart.setNum(2);cart.setPrice(3000l);cart.setCreatedTime(new Date());cart.setModifiedTime(new Date());cart.setCreatedUser("admin1");cart.setModifiedUser("admin1");Integer rows = cartMapper.insert(cart);System.out.println(rows);}@Testpublic void updateNumByCid() {Integer rows = cartMapper.updateNumByCid(1, 2, "admin", new Date());System.out.println(rows);}@Testpublic void findByUidAndPid() {Cart cart = cartMapper.findByUidAndPid(1, 1);System.out.println(cart);}}
4. 业务层
4.1 规划异常
- 插入数据库购物车数据时可能产生InsertException异常
- 更新数据时可能出现UpdateException异常
4.2 创建CartService接口类,编写业务层的抽象方法
更新时间这些字段可以通过new Date()获取,所以CartService接口层只需要用户id,商品id以及数量,更新人这些字段
package com.cy.store.service;public interface CartService {/*** 添加商品到购物车* @param uid 用户id* @param pid 商品id* @param amount 商品数量* @param username 用户名*/void addToCart(Integer uid, Integer pid, Integer amount, String username);}
4.3 实现类实现接口,重写抽象方法
其中的商品价格可以通过商品表获取,这里需要注入ProductMapper,调用根据pid获取商品价格
@Service
public class CartServiceImpl implements CartService {@Autowiredprivate CartMapper cartMapper;@Autowiredprivate ProductMapper productMapper;/*** 添加购物车* @param uid 用户id* @param pid 商品id* @param amount 商品数量* @param username 用户名*/@Overridepublic void addToCart(Integer uid, Integer pid, Integer amount, String username) {
// 查询当前用户购物车中是否存在此商品Cart res = cartMapper.findByUidAndPid(uid, pid);Date date = new Date();// 不存在的话就添加if (res == null) {
// 补全价格Cart cart = new Cart();cart.setUid(uid);cart.setPid(pid);cart.setNum(amount);
// 获取商品表中商品的价格,封装到cart中Product product = productMapper.findById(pid);cart.setPrice(product.getPrice());cart.setCreatedTime(date);cart.setModifiedTime(date);cart.setCreatedUser( username);cart.setModifiedUser(username);cartMapper.insert(cart);}else {//存在的话就修改购物车中商品的数量
// 商品数量=购物车中的数量+用户传递的数量amount = res.getNum()+amount;Integer integer = cartMapper.updateNumByCid(res.getCid(), amount, username, date);if (integer!=1){throw new UpdateException("更新数据时产生未知的异常");}}}
}
4.4 测试
package com.cy.store.service;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class CartServiceTest {@Autowiredprivate CartService cartService;@Testvoid addToCart() {cartService.addToCart(3, 10000001, 10, "admin");}
}
5. 控制层
5.1 设计请求
请求路径:/carts/addToCart
请求方式:GET
请求参数:pid,amount ,session
响应数据:JsonResult<Void>
5.2 实现请求
package com.cy.store.controller;import com.cy.store.service.CartService;
import com.cy.store.util.JsonResult;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/carts")
public class CartController extends BaseController{@Autowiredprivate CartService cartService;@RequestMapping("addToCart")public JsonResult addToCart(Integer pid, Integer amount, HttpSession session) {Integer uid = getUidFromSession(session);String username = getUsernameFromSession(session);cartService.addToCart( uid,pid, amount, username);return new JsonResult<>(OK);}
}
5.3 启动项目,测试
6. 前端页面
在product.html页面给【加入购物车】按钮添加点击事件。发送ajax请求
在ajax函数中data参数的数据设置的方式:
- data:$("form表单名称").seriallize():当参数过多并且在同一个表单中,字符串的提交时
- data:new FormData($("form表单名称")[0]):只适用于文件
- data:"username=Tom":适合于参数值固定并且参数值列表有限,可以进行手动拼接
let user = "admin"
data:"username="+user
- 使用JSON格式提交数据
data: {"pid": id, // 商品id"amount": $("#num").val() // 监听数量输入框中的值 }
代码如下:
// 点击“加入购物车”按钮时触发 AJAX 请求,将商品加入购物车(当前被注释)$("#btn-add-to-cart").click(function() {$.ajax({url: "/carts/addToCart",type: "POST",// 通过json格式传参data: {"pid": id, // 商品id"amount": $("#num").val() // 监听数量输入框中的值},dataType: "JSON",success: function(json) {if (json.state == 200) {alert("增加成功!");} else {alert("增加失败!" + json.message);}},error: function(xhr) {alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status);location.href = "login.html";}});});
重启项目进行测试,查看数据库是否新增成功