基于Spring Boot的生鲜电子商务网站的设计与实现
该项目也用到了Redis缓存(也有商品分类缓存的相关代码)最下方有我的联系方式。
功能
本产品包括用户注册、登陆、商品管理、订单管理、前台商品显示、商品购买以及结账七个主要范围。
用户注册包括:用户注册,建立账号。
用户登录包括:用户登陆后可以查看自己的订单还可以购物。
商品管理包括:管理员对商品的查看、增加、修改、删除四种功能。
订单管理包括:管理员对订单的删除、修改、查询三种功能。
前台商品显示包括:显示商品的图片和详细介绍,及链接地址。
商品购买包括:购物车以及生成订单,购物车包括商品的照片,详细信息介绍。
结账包括:通过生成订单来进行结账
设计图:
E-R图设计:
1.购物车代码:
/**
* 描述: 购物车Service实现类
*/
@Service
public class CartServiceImpl implements CartService {
@Autowired
ProductMapper productMapper;
@Autowired
CartMapper cartMapper;
/**
* 获取用户购买的单一商品并计算总价格
*/
public List<CartVO> list(Integer userId) {
List<CartVO> cartVOS = cartMapper.selectList(userId);
for (int i = 0; i < cartVOS.size(); i++) {
CartVO cartVO = cartVOS.get(i);
cartVO.setTotalPrice(cartVO.getPrice() * cartVO.getQuantity());
}
return cartVOS;
}
@Override
public List<CartVO> add(Integer userId, Integer productId, Integer count) {
validProduct(productId, count);
Cart cart = cartMapper.selectCartByUserIdAndProductId(userId, productId);
if (cart == null) {
//这个商品之前不在购物车里,需要新增一个记录
cart = new Cart();
cart.setProductId(productId);
cart.setUserId(userId);
cart.setQuantity(count);
cart.setSelected(Constant.Cart.CHECKED);
cartMapper.insertSelective(cart);
} else {
//这个商品已经在购物车里了,则数量相加
count = cart.getQuantity() + count;
Cart cartNew = new Cart();
cartNew.setSelected(Constant.Cart.CHECKED);
cartMapper.updateByPrimaryKeySelective(cartNew);
}
return this.list(userId);
}
private void validProduct(Integer productId, Integer count) {
Product product = productMapper.selectByPrimaryKey(productId);
//判断商品是否存在,商品是否上架
if (product == null || product.getStatus().equals(SaleStatus.NOT_SALE)) {
throw new MallException(MallExceptionEnum.NOT_SALE);
}
//判断商品库存
if (count > product.getStock()) {
throw new MallException(MallExceptionEnum.NOT_ENOUGH);
}
}
@Override
public List<CartVO> update(Integer userId, Integer productId, Integer count) {
validProduct(productId, count);
Cart cart = cartMapper.selectCartByUserIdAndProductId(userId, productId);
if (cart == null) {
//这个商品之前不在购物车里,无法更新
throw new MallException(MallExceptionEnum.UPDATE_FAILED);
} else {
//这个商品已经在购物车里了,则更新数量
Cart cartNew = new Cart();
cartNew.setQuantity(count);
cartNew.setId(cart.getId());
cartNew.setProductId(cart.getProductId());
cartNew.setUserId(cart.getUserId());
cartNew.setSelected(Constant.Cart.CHECKED);
cartMapper.updateByPrimaryKeySelective(cartNew);
}
return this.list(userId);
}
@Override
public List<CartVO> delete(Integer userId, Integer productId) {
Cart cart = cartMapper.selectCartByUserIdAndProductId(userId, productId);
if (cart == null) {
//这个商品之前不在购物车里,无法删除
throw new MallException(MallExceptionEnum.DELETE_FAILED);
} else {
//这个商品已经在购物车里了,则可以删除
cartMapper.deleteByPrimaryKey(cart.getId());
}
return this.list(userId);
}
@Override
public List<CartVO> selectOrNot(Integer userId, Integer productId, Integer selected) {
Cart cart = cartMapper.selectCartByUserIdAndProductId(userId, productId);
if (cart == null) {
//这个商品之前不在购物车里,无法选择/不选中
throw new MallException(MallExceptionEnum.UPDATE_FAILED);
} else {
//这个商品已经在购物车里了,则可以选中/不选中
cartMapper.selectOrNot(userId, productId, selected);
}
return this.list(userId);
}
@Override
public List<CartVO> selectAllOrNot(Integer userId, Integer selected) {
//改变选中状态
cartMapper.selectOrNot(userId, null, selected);
return this.list(userId);
}
}
2.购物车实体
/**
* 描述: CartVO,给前端展示用
*/
public class CartVO {
private Integer id;
private Integer productId;
private Integer userId;
private Integer quantity;
private Integer selected;
private Integer price;
private Integer totalPrice;
private String productName;
private String productImage;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Integer getSelected() {
return selected;
}
public void setSelected(Integer selected) {
this.selected = selected;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Integer getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(Integer totalPrice) {
this.totalPrice = totalPrice;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductImage() {
return productImage;
}
public void setProductImage(String productImage) {
this.productImage = productImage;
}
}
3.商品分类使用Redis代码
@Override
@Cacheable(value = "listCategoryForCustomer")// 这里获取缓存的注解
public List<CategoryVO> listCategoryForCustomer(Integer parentId) {
ArrayList<CategoryVO> categoryVOList = new ArrayList<>();
recursivelyFindCategories(categoryVOList, parentId);
return categoryVOList;
}
private void recursivelyFindCategories(List<CategoryVO> categoryVOList, Integer parentId) {
//递归获取所有子类别,并组合成为一个“目录树”
List<Category> categoryList = categoryMapper.selectCategoriesByParentId(parentId);
if (!CollectionUtils.isEmpty(categoryList)) {
for (int i = 0; i < categoryList.size(); i++) {
Category category = categoryList.get(i);
CategoryVO categoryVO = new CategoryVO();
BeanUtils.copyProperties(category, categoryVO);
categoryVOList.add(categoryVO);
recursivelyFindCategories(categoryVO.getChildCategory(), categoryVO.getId());
}
}
}
4.将分类存入Redis缓存中的方法
@Override
public PageInfo list(ProductListReq productListReq) {
//构建Query对象
ProductListQuery productListQuery = new ProductListQuery();
//搜索处理
if (!StringUtils.isEmpty(productListReq.getKeyword())) {
String keyword = new StringBuilder().append("%").append(productListReq.getKeyword())
.append("%").toString();
productListQuery.setKeyword(keyword);
}
//目录处理:如果查某个目录下的商品,不仅是需要查出该目录下的,还要把所有子目录的所有商品都查出来,所以要拿到一个目录id的List
if (productListReq.getCategoryId() != null) {
List<CategoryVO> categoryVOList = categoryService
.listCategoryForCustomer(productListReq.getCategoryId());
ArrayList<Integer> categoryIds = new ArrayList<>();
categoryIds.add(productListReq.getCategoryId());
getCategoryIds(categoryVOList, categoryIds);
productListQuery.setCategoryIds(categoryIds);
}
//排序处理
String orderBy = productListReq.getOrderBy();
if (ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)) {
PageHelper
.startPage(productListReq.getPageNum(), productListReq.getPageSize(), orderBy);
} else {
PageHelper
.startPage(productListReq.getPageNum(), productListReq.getPageSize());
}
List<Product> productList = productMapper.selectList(productListQuery);
PageInfo pageInfo = new PageInfo(productList);
return pageInfo;
}
数据库设计:
表mall_cart
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id | int | True | 购物车id |
product_id | int | True | 商品id |
user_id | int | True | 用户id |
quantity | int | True | 商品数量 |
selected | int | True | 是否已勾选:0代表未勾选,1代表已勾选 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
表mall_category
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id | int | True | 主键 |
name | varchar | True | 分类目录名称 |
type | int | True | 分类目录级别,例如1代表一级,2代表二级,3代表三级 |
parent_id | int | True | 父id,也就是上一级目录的id,如果是一级目录,那么父id为0 |
order_num | int | True | 目录展示时的排序 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
表mall_order:
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id | int | True | 主键id |
order_no | varchar | True | 订单号(非主键id) |
user_id | int | True | 用户id |
total_price | int | True | 订单总价格 |
receiver_name | varchar | True | 收货人姓名快照 |
receiver_mobile | varchar | True | 收货人手机号快照 |
receiver_address | varchar | True | 收货地址快照 |
order_status | int | True | 订单状态: 0用户已取消,10未付款(初始状态),20已付款,30已发货,40交易完成 |
postage | int | False | 运费,默认为0 |
payment_type | int | True | 支付类型,1-在线支付 |
delivery_time | timestamp | False | 发货时间 |
pay_time | timestamp | False | 支付时间 |
end_time | timestamp | False | 交易完成时间 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
表mall_order_item
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id | int | True | 主键id |
order_no | varchar | True | 归属订单id |
product_id | int | True | 商品id |
product_name | varchar | True | 商品名称 |
product_img | varchar | True | 商品图片 |
unit_price | int | True | 单价(下单时的快照) |
quantity | int | True | 商品数量 |
total_price | int | True | 商品总价 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
表mall_product
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id int | True | 商品主键id | |
name | varchar | True | 商品名称 |
image | varchar | False | 产品图片,相对路径地址 |
detail | varchar | False | 商品详情 |
category_id | int | True | 分类id |
price | int | True | 价格,单位-分 |
stock | int | True | 库存数量 |
status | int | True | 商品上架状态:0-下架,1-上架 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
表mall_user:
字段名 | 类型 | 是否可为空 | 注释 |
---|---|---|---|
id | int | True | 用户id |
username | varchar | True | 用户名 |
password | varchar | True | 用户密码,MD5加密 |
personalized_signature | varchar | True | 个性签名 |
role | int | True | 角色,1-普通用户,2-管理员 |
create_time | timestamp | True | 创建时间 |
update_time | timestamp | True | 更新时间 |
项目需要可以找我私聊。