springboot项目
- **中国加油,武汉加油!**
- 篇幅较长,配合目录观看
- 案例准备
- 1. 购物车模块环境搭建
- 1.1 新建数据库表t_cart
- 1.2 shop-entity定义Cart
- 1.3 shop-mapper编写Mapper接口
- 1.4 shop-service编写Servic接口
- 1.5 shop-service-iml新建cart-service(module-springboot)
- 1.6 cart-service导包
- 1.7 编写yml
- 1.8 cart-service编写ServiceImpl实现类
- 1.9 shop-web新建shop-cart(module-springboot)
- 1.10 shop-cart编写Controller
- 1.11 编写yml
- 1.12 修改程序入口
- 1.13 启动程序入口Test
- 2. 自定义注解校验登录状况
- 3. 购物车模块-加入购物车
- 4. 购物车模块-我的购物车
- 5 购物车模块-设置购物车数量
- 6. 购物车模块-购物车合并
- 7. 购物车模块-提交订单
中国加油,武汉加油!
篇幅较长,配合目录观看
案例准备
1. 购物车模块环境搭建
1.1 新建数据库表t_cart
DROP TABLE IF EXISTS `t_cart`;
CREATE TABLE `t_cart` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gid` int(10) DEFAULT NULL,
`num` int(10) DEFAULT NULL,
`uid` int(10) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`sub_total` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.2 shop-entity定义Cart
package com.wpj.entity;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_cart")
public class Cart implements Serializable{
@TableId(type = IdType.AUTO)
private Integer id;
private Integer gid; // 商品id
private Integer num; // 商品数量
private Integer uid; // 用户id
@TableField(value = "create_time")
private Date createTime; // 创建
@TableField(value = "sub_total")
private BigDecimal subTotal; // 商品小计
@TableField(exist = false)
private Goods goods; // 商品信息
}
1.3 shop-mapper编写Mapper接口
package com.wpj.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;
public interface ICartMapper extends BaseMapper<Cart> {
}
1.4 shop-service编写Servic接口
package com.wpj.service;
import com.wpj.entity.Cart;
public interface ICartService extends IBaseService<Cart> {
}
1.5 shop-service-iml新建cart-service(module-springboot)
1.6 cart-service导包
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-mapper</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
1.7 编写yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/nz1904-springboot-shop
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
type-aliases-package: com.wpj.entity
mapper-locations: classpath:/mapper/*.xml
dubbo:
application:
name: cart-service
registry:
address: zookeeper://192.168.59.100:2181
protocol:
port: -1
1.8 cart-service编写ServiceImpl实现类
package com.wpj.service.impl;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;
import com.wpj.mapper.ICartMapper;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CartServiceImpl extends BaseServiceImpl<Cart> implements ICartService {
@Autowired
private ICartMapper cartMapper;
@Override
public BaseMapper<Cart> getMapper() {
return cartMapper;
}
}
1.9 shop-web新建shop-cart(module-springboot)
- Spring Boot DevTools
- Spring Web
- Thymeleaf
- Spring Data Redis(Access+Driver)
1.10 shop-cart编写Controller
package com.wpj.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/cart")
public class CartController {
@RequestMapping("/addCart")
@ResponseBody
public String addCart(Integer gid, Integer num) {
System.out.println("gid = " + gid + ", num = " + num);
return "ok";
}
}
1.11 编写yml
server:
port: 8085
spring:
thymeleaf:
cache: false
prefix: classpath:/templates/
redis:
host: 192.168.59.100
password: admin
dubbo:
application:
name: shop-cart
registry:
address: zookeeper://192.168.59.100:2181
consumer:
check: false
retries: 3
timeout: 10000
1.12 修改程序入口
package com.wpj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(scanBasePackages = "com.wpj", exclude = DataSourceAutoConfiguration.class)
public class ShopCartApplication {
public static void main(String[] args) {
SpringApplication.run(ShopCartApplication.class, args);
}
}
1.13 启动程序入口Test
2. 自定义注解校验登录状况
2.1 shop-common导包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.24.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.8.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
2.2 编写IsLogin注解
package com.wpj.common.aop;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface IsLogin {
// 默认不强制注入user
boolean mustUser() default false;
}
2.3 编写IsLoginAOP
package com.wpj.common.aop;
import com.wpj.common.constant.Constants;
import com.wpj.entity.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class IsLoginAOP {
@Autowired
private RedisTemplate redisTemplate;
@Around("@annotation(IsLogin)")
public Object handler(ProceedingJoinPoint point) {
// 添加了两个依赖 servlet-api 和 spring-web
// 获取HttpServletRequest对象(和Servlet容器解耦的情况下获取ServletAPI)
ServletRequestAttributes requestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 根据req对象获取Cookie
String loginToken = ""; // 登录用户的凭证
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(int i=0;i<cookies.length;i++){
Cookie cookie = cookies[i];
if(Constants.LOGIN_TOKEN.equals(cookie.getName())){
loginToken = cookie.getValue();
break;
}
}
}
// 再从Redis中查询登录用户
User user = null; // 当前登录用户
if(!StringUtils.isEmpty(loginToken)){
user = (User) redisTemplate.opsForValue().get(loginToken);
}
// 判断用户是否登录,如果没有登录,再判断mustUser是否为true
if(user == null) {
// 获取目标方法
MethodSignature methodSignature = (MethodSignature)point.getSignature();
// 获取方法上面的IsLogin的注解
IsLogin annotation = methodSignature.getMethod().getAnnotation(IsLogin.class);
// 判断user是否是必须的
if(annotation.mustUser()){
return "redirect:http://localhost:8084/toLogin";
}
}
// 把user传递Controller中的形参
Object[] args = point.getArgs();// 获取目标方法的参数
for(int i =0;i<args.length;i++){
if(args[i]!= null && args[i].getClass() == User.class){ // 判断当前的参数是否是user类型
args[i]=user; // 替换
break;
}
}
Object proceed = null;
try {
// 调用Controller,把我们处理过的参数传递过去
proceed = point.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
}
2.4 shop-cart编写Contrller
package com.wpj.controller;
import com.wpj.common.aop.IsLogin;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/cart")
public class CartController {
@RequestMapping(value = "/addCart")
// @ResponseBody
@IsLogin(mustUser = true)
public String addCart(String callback) {
System.out.println("CartController.addCart");
String result = "ok";
return callback == null?result+"":callback+"('"+result+"')";
}
}
2.5 启动程序入口测试
- 注意清楚cookie达到没有登录情况下
- 调用addCart就会跳转到login页面
3. 购物车模块-加入购物车
3.1 shop-cart导包
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wpj</groupId>
<artifactId>shop-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
3.2 Service添加方法
package com.wpj.service;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
public interface ICartService extends IBaseService<Cart> {
int addCart(Cart cart, User user, String cartToken);
}
3.3 ServiceImpl重写方法
package com.wpj.service.impl;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import com.wpj.mapper.ICartMapper;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class CartServiceImpl extends BaseServiceImpl<Cart> implements ICartService {
@Autowired
private ICartMapper cartMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public BaseMapper<Cart> getMapper() {
return cartMapper;
}
@Override
public int addCart(Cart cart, User user, String cartToken) {
cart.setCreateTime(new Date());
// 判断用户是否登录
if (user != null) {
cart.setUid(user.getId());
cartMapper.insert(cart); // 添加到数据库
return 1;
} else {
redisTemplate.opsForList().rightPush(cartToken, cart);
return 1;
}
}
}
3.2 shop-cart完善Controller的addCart方法
package com.wpj.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.wpj.common.aop.IsLogin;
import com.wpj.common.constant.Constants;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Controller
@RequestMapping("/cart")
public class CartController {
@Autowired
private RedisTemplate redisTemplate;
@Reference
private ICartService cartService;
/**
* 登录了放到数据库中
* 未登录放到Redis
* 1)key放到cookie中
* @param callback
* @return
*/
@RequestMapping(value = "/addCart")
@ResponseBody
@IsLogin
public String addCart(String callback, Cart cart, User user,
@CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken,
HttpServletResponse resp){
// 判断用户浏览器中是否有cartToken
if(StringUtils.isEmpty(cartToken)){
cartToken= UUID.randomUUID().toString();
Cookie cookie = new Cookie(Constants.CART_TOKEN,cartToken);
cookie.setMaxAge(60*60*24*5);
cookie.setPath("/");
cookie.setHttpOnly(false);
resp.addCookie(cookie);
}
int result = cartService.addCart(cart,user,cartToken);
return callback == null?result+"":callback+"('"+result+"')";
}
}
3.3 cart-service修改ServiceImpl的@Service
3.4 配置yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/nz1904-springboot-shop
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
redis:
host: 192.168.59.100
password: admin
mybatis-plus:
type-aliases-package: com.wpj.entity
mapper-locations: classpath:/mapper/*.xml
dubbo:
application:
name: cart-service
registry:
address: zookeeper://192.168.59.100:2181
protocol:
port: -1
3.4 启动程序入口Test
3.4.1 未登录状态下
3.4.2 登录状态下
4. 购物车模块-我的购物车
4.1 shop-service-api添加方法
List<Cart> getUserCartList(User user, String cartToken);
4.2 cart-service重写方法
@Reference
private IGoodService goodService;
@Override
public List<Cart> getUserCartList(User user, String cartToken) {
// 查询用户购物车
List<Cart> cartList = new ArrayList<Cart>();
if (user != null) {
EntityWrapper wrapper = new EntityWrapper();
wrapper.eq("uid", user.getId());
cartList = cartMapper.selectList(wrapper);
} else {
cartList = redisTemplate.opsForList().range(cartToken, 0, -1);
}
// 查询购物车中每个商品的信息
for (Cart cart : cartList) {
Goods goods = goodService.selectById(cart.getGid());
cart.setGoods(goods);
BigDecimal gprice = goods.getGprice();
Integer num = cart.getNum();
BigDecimal subTotal = gprice.multiply(BigDecimal.valueOf(num));// 价格*num
cart.setSubTotal(subTotal); // 小计
}
// 去重
Set<Integer> set = new HashSet<>(); // set目的是为了判断是否有重复
Map<Integer, Cart> map = new HashMap<>(); // Map<商品Id,购车>
for (Cart cart : cartList) {
if (set.add(cart.getGoods().getId())) {
map.put(cart.getGoods().getId(), cart);
} else {
Cart mapCart = map.get(cart.getGoods().getId());
mapCart.setNum(mapCart.getNum() + cart.getNum());
mapCart.setSubTotal(mapCart.getSubTotal().add(cart.getSubTotal()));
}
}
// 清空集合
cartList.clear();
// 把集合中的商品放到list里面
Set<Map.Entry<Integer, Cart>> entries = map.entrySet();
for (Map.Entry<Integer, Cart> entry : entries) {
cartList.add(entry.getValue());
}
return cartList;
}
4.3 shop-cart引入静态资源
4.4 编写cartList.html
4.5 shop-cart添加方法
@RequestMapping(value = "/getUserCartList")
@IsLogin
public String getUserCartList(User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken, ModelMap map){
List<Cart> cartList = cartService.getUserCartList(user,cartToken);
map.put("cartList",cartList);
return "cartList";
}
4.6 修改shop-search的searchGoodList.html
4.7 启动程序入口测试
5 购物车模块-设置购物车数量
5.1 修改shop-search的searchGoodsList.html
<script type="text/javascript">
$(function(){
$.ajax({
url:"http://localhost:8085/cart/getUserCartCount",
dataType:"JSONP",
jsonpCallback:"callback1",
success:function(data){
$("#showCart").text(data);
}
})
})
</script>
<li><a href="http://localhost:8085/cart/getUserCartList">我的购物车<span id="showCart"></span></a></li>
5.2 shop-cart的Controller编写方法
@RequestMapping(value = "/getUserCartCount")
@IsLogin
@ResponseBody
public String getUserCartCount(String callback,User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken){
Integer count = 0;
List<Cart> cartList = cartService.getUserCartList(user,cartToken);
for(Cart cart:cartList){
count+=cart.getNum();
}
return callback == null?count+"":callback+"('"+count+"')";
}
6. 购物车模块-购物车合并
6.1 shop-service-api添加方法
int mergeCart(User user, String cartToken);
6.2 cart-service重写方法
@Override
public int mergeCart(User user, String cartToken) {
// 从reids中查询
List<Cart> cartList = redisTemplate.opsForList().range(cartToken, 0, -1);
for (Cart cart : cartList) {
cart.setUid(user.getId());
cartMapper.insert(cart);
}
// 清空redis
redisTemplate.delete(cartToken);
return 1;
}
6.3 shop-sso修改Controller的login方法
@Reference
private ICartService cartService;
@RequestMapping(value = "/login")
@ResponseBody
public ResultEntity login(String username, String password, String returnUrl,HttpServletResponse resp, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken){
// 根据用户名查询对象
User user = userService.selectByUsername(username);
if(user != null){
// 密码的比对
if(PasswordUtils.checkpw(password,user.getPassword())){
// 购车的合并
if(!StringUtils.isEmpty(cartToken)){
int result = cartService.mergeCart(user,cartToken);
}
// 把登陆用户放道redis中
redisTemplate.opsForValue().set(username,user,5,TimeUnit.DAYS);
// 把用户的凭证放道cookie
Cookie cookie = new Cookie(Constants.LOGIN_TOKEN,username);
cookie.setMaxAge(60*60*24*5);
cookie.setHttpOnly(false); // 是否允许js访问
cookie.setPath("/"); // 同一个域名下面可以访问
// cookie.setDomain(); // 正则表达式,解决一级域名的cookie跨域
// cookie.setSecure(true); // 必须是加密(https)的请求才可以携带cookie
// 写到cookie
resp.addCookie(cookie);
if(StringUtils.isEmpty(returnUrl)){
System.out.println(returnUrl);
returnUrl = "http://localhost:8081/";
}
return ResultEntity.SUCCESS(returnUrl);
}else{
return ResultEntity.FALL("用户名或密码错误");
}
}else{
return ResultEntity.FALL("用户名不存在");
}
}
7. 购物车模块-提交订单
7.1 shop-cart修改cartList.html
7.2 shop-web修改Controller的getUserCartList方法
@RequestMapping(value = "/getUserCartList")
@IsLogin
public String getUserCartList(User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken, ModelMap map){
List<Cart> cartList = cartService.getUserCartList(user,cartToken);
BigDecimal totalPrice = new BigDecimal(0);
for(Cart cart:cartList){
totalPrice= totalPrice.add(cart.getSubTotal());
}
map.put("cartList",cartList);
map.put("totalPrice",totalPrice);
return "cartList";
}