目录
项目介绍
最近在做一个网上购物商城的项目,而我主要负责该项目的购物车功能模块,购物车模块主要涉及到的功能有添加商品到购物车中,修改购物车中商品的数量,查询购物车中的商品,删除购物车中的商品等。
开发配置
- Java版本:JDK1.8.0
- 服务器版本:Tomcat8.5
- 数据库:Oracle Redis
- 开发框架:Spring Boot, MyBatis, apache-maven-3.6.1
- 开发工具:STS4
开发流程
购物车业务流程
开发流程
概要设计
本模块主要分为数据层(DAO)、数据对象(POJO、VO)、Redis配置层、服务层(ServiceImpl)、控制层(Controller)以及前端页面。下面分别介绍各个模块:
1、项目结构
2、数据层(DAO)
Dao层文件主要用于定义购物车中商品的增删改查接口,服务层通过该层调用框架底层的代理从数据库中进行数据操作
CartServiceDao.java中主要定义了5个数据接口,分别对应购物车中的添加商品、删除商品、查询商品、商品数量加1、商品数量减1
package com.chen.dao;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.chen.pojo.ShoppingCar;
/**
* @author Dragon_Kater
*
*/
@Mapper
public interface CartServiceDao {
@Insert("insert into cms_shopcar(id,user_id,product_id,product_amount,seller_id) values(#{id},#{userId},#{productId},#{productAmount},#{sellerId})")
public int addCart(ShoppingCar shoppingCar);
@Select("select * from cms_shopcar where user_id=#{userId}")
public List<ShoppingCar> findAllProduct(Integer userId);
@Update("update cms_shopcar set product_amount = product_amount + 1 where user_id=#{userId} and product_id=#{productId}")
public int productAmountASC(Integer userId, Integer productId);
@Update("update cms_shopcar set product_amount = product_amount - 1 where user_id=#{userId} and product_id=#{productId}")
public int productAmountDESC(Integer userId, Integer productId);
@Delete("delete from cms_shopcar where user_id=#{userId} and product_id=#{productId}")
public int delCartProduct(Integer userId, Integer productId);
}
ProductInfoDao.java主要功能是根据商品id从商品表中查询商品的信息
package com.chen.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.chen.pojo.ProductInfo;
/**
* @author Dragon_Kater
*
*/
@Mapper
public interface ProductInfoDao {
@Select("select * from os_product where id=#{id}")
public ProductInfo findProductById(Integer id);
}
3、数据层(POJO、VO)
ShoppingCar.java主要用于存储购物车信息
package com.chen.pojo;
import java.io.Serializable;
import lombok.Data;
/**
* @author Dragon_Kater
*/
@Data
public class ShoppingCar implements Serializable {
private static final long serialVersionUID = 7123687508750315545L;
private Integer id; //购物车id
private Integer userId; //用户id
private Integer productId; //商品id
private Integer productAmount; //商品数量
private Integer sellerId; //卖家id
}
ProductInfo.java用于存储商品信息
package com.chen.pojo;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* @author Dragon_Kater
*
*/
@Data
public class ProductInfo implements Serializable {
private static final long serialVersionUID = 3736494058827285316L;
private Integer id;// 货号
private String name;// 商品名
private String brand;// 品牌
private Double price;// 价格
private String special;// 有无特典
private String vendingType;// 类型(现货,预定)
private Integer smallCate;// 商品类别
private Integer stock;// 库存
private String roleName;// 角色名
private String workName;// 作品名
private String author;// 作者
private String facilitators;// 协力制作
private String color;// 颜色涂装
private String producer;// 生产商
private String distributor;// 销售商
private String copyright;// 版权
private String ratio;// 比例
private String box;// 盒
private Date registerDate;// 上架日期
private Date releaseDate;// 发售日期
private String description;// 商品说明
private String seller;// 卖家
}
ShoppingCarVo.java用于存储前端页面用户看到的购物车信息
package com.chen.common;
import java.io.Serializable;
import com.chen.pojo.ShoppingCar;
import lombok.Data;
/**
* @author Dragon_Kater
*
*/
@Data
public class ShoppingCarVo implements Serializable {
private static final long serialVersionUID = 2616783859683578863L;
private String productName; //商品名称
private String imgUrl; //商品图片路径
private ShoppingCar shoppingCar; //购物车信息
}
JsonResult.java用于封装后台处理的数据,后台将处理后的数据封装到Json对象中,返回给前端处理
package com.chen.common;
import java.io.Serializable;
import lombok.Data;
/**
* @author Dragon_Kater
*
*/
@Data
public class JsonResult implements Serializable {
private static final long serialVersionUID = 2944384082936622509L;
private Integer state = 1;
private String message = "ok";
private Object data;
public JsonResult() { }
public JsonResult(String message) {
this.message = message;
}
public JsonResult(Object data) {
this.data = data;
}
public JsonResult(Throwable t){
this.state = 0;
this.message = t.getMessage();
}
}
4、Redis配置层
RedisConfig.java用于存储从配置文件中读取到的Redis参数
package com.chen.redis;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
/**
* @author Dragon_Kater
*
*/
@Component
@ConfigurationProperties(prefix = "redis") //读取配置文件中redis相关参数
@Data
public class RedisConfig {
private String host;
private int port;
private int timeout;
private int poolMaxIdle;
private int poolMaxWait;
}
RedisPoolFactory.java用于配置JedisPool相关信息
package com.chen.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author Dragon_Kater
*
*/
@Configuration
public class RedisPoolFactory {
@Autowired
private RedisConfig redisConfig;
@Bean
public JedisPool jedisFactoryPool() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(redisConfig.getPoolMaxIdle());
config.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
config.setTestOnBorrow(true);
JedisPool jp = new JedisPool(config, "127.0.0.1", 6379, redisConfig.getTimeout(), null, 0);
return jp;
}
}
Redis文件夹下的KeyPrefix.java、CartPrefix.java、BasePrefix.java主要用于生成存入Redis中的唯一键,防止存入重复的键
5、服务层(Service)
RedisService.java用于从Redis中存数据、取数据、根据键判断对应的值是否存在等功能
package com.chen.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.chen.redis.KeyPrefix;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* redis服务
* @author Dragon_Kater
*/
@Service
public class RedisService {
@Autowired
private JedisPool jedisPool;
/**
* 从redis连接池获取redis实例
* @param prefix
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> T get(KeyPrefix prefix,String key, Class<T> clazz){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
//对key增加前缀,可用于分类,避免key重复
String realKey = prefix.getPrefix() + key;
String str = jedis.get(realKey);
T t = stringToBean(str,clazz);
return t;
}finally {
returnToPool(jedis);
}
}
/**
* 存储对象
* @param prefix
* @param key
* @param value
* @param <T>
* @return
*/
public <T> Boolean set(KeyPrefix prefix,String key,T value){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String str = beanToString(value);
if (str == null || str.length() <=0){
return false;
}
String realKey = prefix.getPrefix() + key;
int seconds = prefix.expireSeconds(); //获取过期时间
if (seconds <= 0 ){
jedis.set(realKey,str);
}else {
jedis.setex(realKey,seconds,str);
}
return true;
}finally {
returnToPool(jedis);
}
}
/**
* 删除
* @param prefix
* @param key
* @return
*/
public boolean delete(KeyPrefix prefix,String key){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix() + key;
long ret = jedis.del(realKey);
return ret>0;
}finally {
returnToPool(jedis);
}
}
/**
* 判断key是否存在
* @param prefix
* @param key 用户id
* @param <T>
* @return
*/
public <T> boolean exists(KeyPrefix prefix,String key){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix() + key;
return jedis.exists(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 判断key对应的value是否存在
* @param prefix
* @param key 用户id
* @param field 商品id
* @return
*/
public boolean existsValue(KeyPrefix prefix,String key,String field){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realkey = prefix.getPrefix() + key;
Boolean result = jedis.hexists(realkey,field);
return result;
}finally {
returnToPool(jedis);
}
}
/**
* 增加值
* @param prefix
* @param key
* @param <T>
* @return
*/
public <T> Long incr(KeyPrefix prefix,String key){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 减少值
* @param prefix
* @param key
* @param <T>
* @return
*/
public <T> Long decr(KeyPrefix prefix,String key){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 返回指定字段的值
* @param prefix
* @param key
* @param filed
* @param <T>
* @return
*/
public <T> String hget(KeyPrefix prefix,String key,String filed){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.hget(realKey,filed);
}finally {
returnToPool(jedis);
}
}
/**
*
* @param prefix
* @param key
* @param field
* @param value
* @param <T>
* @return
*/
public<T> Long hset(KeyPrefix prefix,String key,String field,String value){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.hset(realKey,field,value);
}finally {
returnToPool(jedis);
}
}
/**
* 获取列表数值
* @param prefix
* @param key
* @return
*/
@SuppressWarnings("resource")
public List<String> hvals(KeyPrefix prefix,String key){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.hvals(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 删除值
* @param prefix
* @param key
* @param field
* @return
*/
@SuppressWarnings("resource")
public Long hdel(KeyPrefix prefix,String key,String field){
Jedis jedis = new Jedis();
try {
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
return jedis.hdel(realKey,field);
}finally {
returnToPool(jedis);
}
}
public static <T> String beanToString(T value){
if (value ==null){
return null;
}
Class<?> clazz = value.getClass();
if (clazz ==int.class || clazz ==Integer.class){
return String.valueOf(value);
}else if(clazz ==long.class || clazz == Long.class){
return String.valueOf(value);
}else if (clazz ==String.class){
return (String) value;
}else{
return JSON.toJSONString(value);
}
}
// 将string类型转换为实体类
@SuppressWarnings("unchecked")
public static <T> T stringToBean(String str,Class<T> clazz){
if (str == null || str.length() <=0 || clazz==null){
return null;
}
if (clazz ==int.class || clazz == Integer.class){
return (T) Integer.valueOf(str);
}else if(clazz == long.class || clazz ==Long.class){
return (T) Long.valueOf(str);
}else if (clazz == String.class){
return (T) str;
}else {
return JSON.toJavaObject(JSON.parseObject(str),clazz);
}
}
private void returnToPool(Jedis jedis){
if(jedis != null){
jedis.close();
}
}
}
CartServiceImpl.java 用于实现具体的购物车业务
package com.chen.service;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chen.common.ShoppingCarVo;
import com.chen.dao.CartServiceDao;
import com.chen.dao.ProductInfoDao;
import com.chen.pojo.ProductInfo;
import com.chen.pojo.ShoppingCar;
import com.chen.redis.CartPrefix;
/**
* @author Dragon_Kater
*
*/
@Service
public class CartServiceImpl implements CartServiceDao{
@Autowired
RedisService redisService;
@Autowired
ProductInfoDao productInfoDao;
@Autowired
private CartServiceDao cartServiceDao;
/**
* 添加商品到购物车中
*/
@Override
public int addCart(ShoppingCar shoppingCar) {
String userId = String.valueOf(shoppingCar.getUserId());
String productId = String.valueOf(shoppingCar.getProductId());
//key为 userId_cart,校验是否已存在
Boolean exists = redisService.existsValue(CartPrefix.getCartList,userId,productId);
if (exists){
//获取现有的购物车中的数据
String json = redisService.hget(CartPrefix.getCartList,userId,productId);
if(json != null) {
//转换为java实体类
ShoppingCarVo shoppingCarVo = JSON.toJavaObject(JSONObject.parseObject(json),ShoppingCarVo.class);
int num = shoppingCarVo.getShoppingCar().getProductAmount()+shoppingCar.getProductAmount();
ShoppingCar shoppingCarNew = shoppingCarVo.getShoppingCar();
shoppingCarNew.setProductAmount(num);
shoppingCarVo.setShoppingCar(shoppingCarNew);
redisService.hset(CartPrefix.getCartList,userId,productId,JSON.toJSON(shoppingCarVo).toString());
} else {
return 0;
}
return 1;
}
//根据商品id获取商品
int id = shoppingCar.getProductId();
ProductInfo productInfo = productInfoDao.findProductById(id);
if(productInfo == null) {
return 0;
}
//将数据封装到ShoppingCarVo对象中
ShoppingCarVo shoppingCarVo = new ShoppingCarVo();
shoppingCarVo.setShoppingCar(shoppingCar);
shoppingCarVo.setImgUrl(null);
shoppingCarVo.setProductName(productInfo.getName());
//将ShoppingCarVo对象存放到Redis中
redisService.hset(CartPrefix.getCartList,userId,productId,JSON.toJSON(shoppingCarVo).toString());
return 1;
}
@Override
public List<ShoppingCar> findAllProduct(Integer userId){
return cartServiceDao.findAllProduct(userId);
}
/**
* 展示购物车
* @param userId 用户id
* @return
*/
public List<ShoppingCarVo> findAllProducts(Integer userId) {
String userID = String.valueOf(userId);
List<ShoppingCarVo> cartList = new LinkedList<ShoppingCarVo>();
//从Redis缓存中取数据
List<String> jsonList = redisService.hvals(CartPrefix.getCartList,userID);
if(jsonList != null) {
for(String json : jsonList) {
//将字符串对象转换成ShoppingCarVo对象
ShoppingCarVo shoppingCarVo = JSON.toJavaObject(JSONObject.parseObject(json),ShoppingCarVo.class);
cartList.add(shoppingCarVo);
}
} else {
List<ShoppingCar> list = findAllProduct(userId);
for (ShoppingCar shoppingCar : list) {
Integer productId = shoppingCar.getProductId();
ProductInfo productInfo = productInfoDao.findProductById(productId);
String productName = productInfo.getName();
String imgUrl = null;
ShoppingCarVo shoppingCarVo = new ShoppingCarVo();
shoppingCarVo.setShoppingCar(shoppingCar);
shoppingCarVo.setProductName(productName);
shoppingCarVo.setImgUrl(imgUrl);
cartList.add(shoppingCarVo);
}
}
return cartList;
}
@Override
public int productAmountASC(Integer userId, Integer productId) {
String userID = String.valueOf(userId);
String productID = String.valueOf(productId);
String json = redisService.hget(CartPrefix.getCartList,userID,productID);
if(json == null) {
return 0;
}
ShoppingCarVo shoppingCarVo = JSON.toJavaObject(JSONObject.parseObject(json),ShoppingCarVo.class);
int num = shoppingCarVo.getShoppingCar().getProductAmount() + 1;
ShoppingCar shoppingCar = shoppingCarVo.getShoppingCar();
shoppingCar.setProductAmount(num);
shoppingCarVo.setShoppingCar(shoppingCar);
redisService.hset(CartPrefix.getCartList,userID,productID,JSON.toJSON(shoppingCarVo).toString());
return 1;
}
@Override
public int productAmountDESC(Integer userId, Integer productId) {
String userID = String.valueOf(userId);
String productID = String.valueOf(productId);
String json = redisService.hget(CartPrefix.getCartList,userID,productID);
if(json == null) {
return 0;
}
ShoppingCarVo shoppingCarVo = JSON.toJavaObject(JSONObject.parseObject(json),ShoppingCarVo.class);
int num = shoppingCarVo.getShoppingCar().getProductAmount() - 1;
ShoppingCar shoppingCar = shoppingCarVo.getShoppingCar();
shoppingCar.setProductAmount(num);
shoppingCarVo.setShoppingCar(shoppingCar);
redisService.hset(CartPrefix.getCartList,userID,productID,JSON.toJSON(shoppingCarVo).toString());
return 1;
}
/**
* 删除商品
* @param userId
* @param productId
* @return
*/
@Override
public int delCartProduct(Integer userId, Integer productId) {
String userID = String.valueOf(userId);
String productID = String.valueOf(productId);
redisService.hdel(CartPrefix.getCartList,userID,productID);
return 1;
}
}
6、控制层(Controller)
ShoppingCarController.java用于处理前端请求
package com.chen.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chen.common.JsonResult;
import com.chen.pojo.ShoppingCar;
import com.chen.service.CartServiceImpl;
/**
* @author Dragon_Kate
*
*/
@RestController
@RequestMapping("/shoppingcar/")
public class ShoppingCarController {
@Autowired
private CartServiceImpl cartServiceImpl;
@RequestMapping("doAddProduct")
public JsonResult doAddProduct(ShoppingCar shoppingCar) {
cartServiceImpl.addCart(shoppingCar);
return new JsonResult("添加成功");
}
@RequestMapping("doFindAllProduct")
public JsonResult doFindAllProduct(Integer userId) {
return new JsonResult(cartServiceImpl.findAllProducts(userId));
}
@RequestMapping("doAddProductNum")
public JsonResult doAddProductNum(Integer userId, Integer productId) {
cartServiceImpl.productAmountASC(userId, productId);
return new JsonResult("修改成功");
}
@RequestMapping("doReduceProductNum")
public JsonResult doReduceProductNum(Integer userId, Integer productId) {
cartServiceImpl.productAmountDESC(userId, productId);
return new JsonResult("修改成功");
}
@RequestMapping("doDeleteProduct")
public JsonResult doDeleteProduct(Integer userId, Integer productId) {
cartServiceImpl.delCartProduct(userId, productId);
return new JsonResult("删除成功");
}
}
总结
本人也是在探索学习阶段,有什么问题可以互相交流讨论。当一个人沉迷于代码的世界时,会忘记这个世界,在做购物车的时候,中间也出现过多次BUG,通过不断的调试BUG,可以学到很多东西,尤其是当你成功解决了BUG之后,你对知识的掌握会更加牢靠,最后,需要源码的可以联系博主,联系方式:chendikai1314@163.com