前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >eshop商城项目实训代码重构

eshop商城项目实训代码重构

作者头像
777nx
发布2023-05-02 14:49:59
5090
发布2023-05-02 14:49:59
举报
文章被收录于专栏:Fantasy的技术小站

eshop商城项目实训系列教程导航

  1. eshop商城项目实训源码
  2. eshop商城项目实训代码重构 <= 当前位置

基本架构

要先把基本框架搭建起来,才能够愉快的写代码

1. entity

先看下数据库表结构

要在eshop-business模块下新建src\main\java的文件夹,在该文件夹下创建com.eshop.entity的包,在该包下创建StoreProductRelation的实体类与之数据表一一对应,代码如下:

代码语言:javascript
复制
package com.eshop.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StoreProductRelation extends BaseDomain{

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private Long uid;
    private Long productId;
    private String type;
    private String category;

}

因为该表继承了BaseDomain,拥有父的属性

BaseDomain的部分代码如下:

代码语言:javascript
复制
private static final long serialVersionUID = 1L;
   @TableField(
       fill = FieldFill.INSERT
   )
   @JsonFormat(
       pattern = "yyyy-MM-dd HH:mm:ss",
       timezone = "GMT+8"
   )
   private Date createTime;
   @TableField(
       fill = FieldFill.UPDATE
   )
   @JsonFormat(
       pattern = "yyyy-MM-dd HH:mm:ss",
       timezone = "GMT+8"
   )
   private Date updateTime;
   @TableLogic
   @JsonIgnore
   @TableField(
       fill = FieldFill.INSERT
   )
   private Integer isDel;

2. mapper

mapper的包下新建ProductRelationMapper的类,代码如下:

代码语言:javascript
复制
package com.eshop.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.eshop.domain.StoreProductRelation;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ProductRelationMapper extends BaseMapper<StoreProductRelation> {
}

3. service

service的包下新建ProductRelationService的类,代码如下:

代码语言:javascript
复制
package com.eshop.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.eshop.domain.StoreProductRelation;

public interface ProductRelationService extends IService<StoreProductRelation> {
}

新建ProductRelationServiceImpl的实现类,实现ProductRelationService的接口,代码如下:

代码语言:javascript
复制
package com.eshop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eshop.api.EshopException;
import com.eshop.domain.StoreProductRelation;
import com.eshop.mapper.ProductRelationMapper;
import com.eshop.service.ProductRelationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ProductRelationServiceImpl extends ServiceImpl<ProductRelationMapper, StoreProductRelation> implements ProductRelationService {

    @Autowired
    private ProductRelationMapper productRelationMapper;

}

4. controller

com.eshop下新建一个包,包名叫controller,新建一个类,类名叫ProductCollectController

代码语言:javascript
复制
package com.eshop.controller;

import cn.hutool.core.util.NumberUtil;
import com.eshop.api.ApiResult;
import com.eshop.api.EshopException;
import com.eshop.common.bean.LocalUser;
import com.eshop.common.interceptor.AuthCheck;
import com.eshop.modules.product.param.StoreProductRelationQueryParam;
import com.eshop.service.ProductRelationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/collect")
public class ProductCollectController {

    @Autowired
    private ProductRelationService productRelationService;

}

功能编码

1. 商品添加收藏

前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/add

看报错Request method 'POST' not supported,不支持请求方法“POST”

开始编写controller层的代码

代码语言:javascript
复制
/**
 * 添加收藏
 * @param param body部分,需要查询的参数
 * @return 是否成功:成功 true,失败 false
 */
@PostMapping("/add")
@AuthCheck
public ApiResult<Boolean> addProductCollect(@Validated @RequestBody StoreProductRelationQueryParam param){
    Long uid = LocalUser.getUser().getUid();
    if (!NumberUtil.isNumber(param.getId())){
        throw new EshopException("参数非法");
    }
    productRelationService.addProductCollect(Long.valueOf(param.getId()), uid, param.getCategory());
    return ApiResult.ok();
}

部分注解说明:

  • @NoRepeatSubmit:防止重复提交自定义注解
  • @AuthCheck:自定义注解实现用户行为认证
  • @Validated:参数验证注解
1.1 是否收藏

service接口:

代码语言:javascript
复制
/**
 * 是否收藏
 * @param productId 商品ID
 * @param uid 用户ID
 * @return Boolean
 */
Boolean isProductRelation(long productId, long uid);

实现该业务功能:

代码语言:javascript
复制
/**
 * 是否收藏
 * @param productId 商品Id
 * @param uid 用户Id
 * @return
 */
@Override
public boolean isProductCollect(long productId, long uid) {
    LambdaQueryWrapper<StoreProductRelation> lqw = new LambdaQueryWrapper<>();
    lqw.eq(StoreProductRelation::getProductId, productId)
            .eq(StoreProductRelation::getUid, uid)
            .eq(StoreProductRelation::getType, "collect");
    int count = productRelationMapper.selectCount(lqw);
    if (count > 0) {
        return true;
    }
    return false;
}
1.2 添加收藏

service接口:

代码语言:javascript
复制
/**
 * 添加收藏
 * @param productId 商品Id
 * @param uid 用户Id
 * @param category
 */
void addProductCollect(long productId, long uid, String category);

实现该业务功能:

代码语言:javascript
复制
/**
 * 添加收藏
 * @param productId 商品Id
 * @param uid 用户Id
 * @param category
 */
@Override
public void addProductCollect(long productId, long uid, String category) {
    if (isProductCollect(productId, uid)){
        throw new EshopException("已收藏");
    }
    StoreProductRelation storeProductRelation = new StoreProductRelation();
    storeProductRelation.setProductId(productId);
    storeProductRelation.setUid(uid);
    storeProductRelation.setType(category);
    productRelationMapper.insert(storeProductRelation);
}

2. 商品取消收藏

前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/del

controller层:

代码语言:javascript
复制
/**
 * 取消收藏
 * @param param body部分,需要查询的参数
 * @return 是否成功:成功 true,失败 false
 */
@NoRepeatSubmit
@PostMapping("/del")
@AuthCheck
public ApiResult<Boolean> delProductCollect(@Validated @RequestBody StoreProductRelationQueryParam param){
    Long uid = LocalUser.getUser().getUid();
    if (!NumberUtil.isNumber(param.getId())){
        throw new EshopException("参数非法");
    }
    productRelationService.delProductCollect(Long.valueOf(param.getId()), uid, param.getCategory());
    return ApiResult.ok();
}

service接口:

代码语言:javascript
复制
/**
 * 取消收藏
 * @param productId 商品Id
 * @param uid 用户Id
 * @param category
 */
void delProductCollect(long productId, long uid, String category);

实现该业务功能:

代码语言:javascript
复制
/**
 * 取消收藏
 *
 * @param productId 商品Id
 * @param uid       用户Id
 * @param category
 */
@Override
public void delProductCollect(long productId, long uid, String category) {
    LambdaQueryWrapper<StoreProductRelation> lqw = new LambdaQueryWrapper<>();
    lqw.eq(StoreProductRelation::getProductId, productId)
            .eq(StoreProductRelation::getUid, uid)
            .eq(StoreProductRelation::getType, category);
    StoreProductRelation productRelation = productRelationMapper.selectOne(lqw);
    if (productRelation == null) {
        throw new EshopException("已取消");
    }
    this.removeById(productRelation.getId());
}

3. 批量删除收藏/足迹

前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/dels/{productIds}

controller层:

代码语言:javascript
复制
/**
 * 批量删除收藏/足迹 collect收藏 foot 足迹
 * @param productIds 商品Id
 * @param param body部分,需要查询的参数
 * @return 是否成功:成功 true,失败 false
 */
@NoRepeatSubmit
@PostMapping("/dels/{productIds}")
@AuthCheck
@Transactional
public ApiResult<Boolean> delCollects(@PathVariable String productIds,@RequestBody StoreProductRelationQueryParam param) {
    Long uid = LocalUser.getUser().getUid();
    String[] ids = productIds.split(",");
    if (ids.length > 0){
        for (String id : ids) {
            productRelationService.delProductCollect(Long.valueOf(id), uid, param.getCategory());
        }
    }else {
        throw new EshopException("参数非法");
    }
    return ApiResult.ok();
}

4. 获取收藏或足迹

前端发过来的请求(使用的是get方式):http://localhost:8008/api/collect/user?limit=10&page=1&type=foot

后端接受的请求:

代码语言:javascript
复制
@GetMapping("/user")
@AuthCheck
public ApiResult<List<StoreProductRelationQueryVo>> UserCollect(@RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int limit, @RequestParam(value = "type") String type) {
    log.info("limit: {}", limit);
    log.info("page: {}", page);
    log.info("type: {}", type);
    return null;
}

发现可以接收到前端参数后,开始补全controller层代码,添加下面的接口代码

代码语言:javascript
复制
/**
 * 获取用户收藏列表
 * @param page 页码,默认为 1
 * @param limit 页大小,默认为 10
 * @param type foot为足迹,collect为收藏
 * @return
 */
@GetMapping("/user")
@AuthCheck
public ApiResult<List<StoreProductRelationQueryVo>> UserCollect(@RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int limit, @RequestParam(value = "type") String type) {
    Long uid = LocalUser.getUser().getUid();
    List<StoreProductRelationQueryVo> storeProductRelationQueryVos = productRelationService.userProductCollect(page, limit, uid, type);
    return ApiResult.ok(storeProductRelationQueryVos);
}

此时我们发现API文档要返回的数据并不能够满足我们的需求

通过分析返回的数据是来自store_product_relationstore_product两张表的字段,因此需要在vo包下造个StoreProductRelationQueryVo的类,代码如下:

代码语言:javascript
复制
package com.eshop.vo;

import com.eshop.serializer.DoubleSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
public class StoreProductRelationQueryVo implements Serializable {

    private static final Long serialVersion = 1L;
    private Long id;
    private Long uid;
    private Long productId;
    private String type;
    private String category;
    private Date createTime;
    private Long pid;
    private String image;
    private String storeName;
    private Double price;
    @JsonSerialize(using = DoubleSerializer.class)
    private Double otPrice;
    private Integer sales;
    private Integer isShow;
    private Integer isIntegral;
    private Integer integer;

}

service层:

代码语言:javascript
复制
/**
 * 获取用户收藏列表
 * @param limit 页大小
 * @param page 页码
 * @param type foot为足迹 collect为收藏
 * @param uid 用户Id
 * @return
 */
List<StoreProductRelationQueryVo> userProductCollect(int page, int limit, Long uid, String type);

实现该业务功能:

代码语言:javascript
复制
/**
 * 获取用户收藏列表
 * @param limit 页大小
 * @param page 页码
 * @param type foot为足迹 collect为收藏
 * @param uid 用户Id
 * @return
 */
@Override
public List<StoreProductRelationQueryVo> userProductCollect(int page, int limit, Long uid, String type) {
    // IPage<StoreProductRelation> pageModel = new Page<>(page, limit);
    Page<StoreProductRelation> pageModel = new Page<>(page, limit);
    List<StoreProductRelationQueryVo> list = productRelationMapper.selectRelationList(pageModel, uid, type);
    return list;
}

注意:这里不能够用mybatis-plus的分页插件,控制台会报Handler dispatch failed; nested exception is java.lang.NoSuchMethodError的错误,我推测的sql语句的问题

因为该业务涉及到多表查询,mybatisplus并未给我们提供相关可以调用的接口,所以我们需要自己编写sql语句,去实现我们的需求。sql语句如下:

代码语言:javascript
复制
select B.id pid,A.type as category,B.store_name as storeName,B.price,B.is_integral as isIntegral, B.ot_price as otPrice,B.sales,B.image,B.is_show as isShow,B.integral as integral from store_product_relation A left join store_product B on A.product_id = B.id where A.type='foot' and A.uid=1 and A.is_del = 0 and B.is_del = 0 order by A.create_time desc

mapper层:

代码语言:javascript
复制
@Select("select B.id pid,A.type as category,B.store_name as storeName,B.price,B.is_integral as isIntegral," +
        "B.ot_price as otPrice,B.sales,B.image,B.is_show as isShow,B.integral as integral" +
        " from store_product_relation A left join store_product B " +
        "on A.product_id = B.id where A.type=#{type} and A.uid=#{uid} and A.is_del = 0 and B.is_del = 0 order by A.create_time desc")
List<StoreProductRelationQueryVo> selectRelationList(Page page, @Param("uid") Long uid, @Param("type") String type);

至此功能编码完毕!!

程序排错

任务描述

接口地址/api/register输入相同的手机号注册新用户时,页面显示了SQL错误。正常情况应该显示该手机号已存在。

代码语言:javascript
复制
\r\n### Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'\r\n### The error may exist in com/eshop/modules/user/service/mapper/UserMapper.java (best guess)\r\n### The error may involve com.eshop.modules.user.service.mapper.UserMapper.insert-Inline\r\n### The error occurred while setting parameters\r\n### SQL: INSERT INTO eshop_user  ( username, password,       nickname, avatar, phone, add_ip, last_ip,         user_type,        create_time,  is_del )  VALUES  ( ?, ?,       ?, ?, ?, ?, ?,         ?,        ?,  ? )\r\n### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'\n; Duplicate entry '13512345678' for key 'username'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'

要求

修改完代码之后,相同手机号注册时应提示手机号已存在。

代码编写

对该接口代码进行分析,发现并未对获得shopUser的对象做判断是否为空对象

代码语言:javascript
复制
@PostMapping("/register")
@ApiOperation(value = "H5/APP注册新用户", notes = "H5/APP注册新用户")
public ApiResult<String> register(@Validated @RequestBody RegParam param) {
    Object codeObj = redisUtil.get("code_" + param.getAccount());
    if(codeObj == null){
        return ApiResult.fail("请先获取验证码");
    }
    String code = codeObj.toString();
    if (!StrUtil.equals(code, param.getCaptcha())) {
        return ApiResult.fail("验证码错误");
    }
    ShopUser shopUser = userService.getOne(Wrappers.<ShopUser>lambdaQuery()
            .eq(ShopUser::getPhone,param.getAccount()),false);

    authService.register(param);
    return ApiResult.ok("","注册成功");
}

因此编写以下代码对shopUser进行是否为空对象判断:

代码语言:javascript
复制
if (ObjectUtil.isNotNull(shopUser)){
    return ApiResult.fail("用户已存在");
}

经测试后成功!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本架构
    • 1. entity
      • 2. mapper
        • 3. service
          • 4. controller
          • 功能编码
            • 1. 商品添加收藏
              • 1.1 是否收藏
              • 1.2 添加收藏
            • 2. 商品取消收藏
              • 3. 批量删除收藏/足迹
                • 4. 获取收藏或足迹
                • 程序排错
                  • 任务描述
                    • 要求
                      • 代码编写
                      相关产品与服务
                      验证码
                      腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档