H

Hillky

V1

2022/04/15阅读:28主题:默认主题

Springboot电商开发实战

Springboot电商项目

项目初始化

  • 引入sql文件,新建springboot工程,添加web依赖,修改springboot版本

  • 增加依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.9</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.hillky</groupId>
        <artifactId>imocc_mall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>imocc_mall</name>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.1</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.5.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity</artifactId>
                <version>1.7</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>

        </dependencies>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>

    </project>

  • 使用mybatis-plus生成代码

    package com.hillky.imocc_mall;

    import com.baomidou.mybatisplus.generator.FastAutoGenerator;
    import com.baomidou.mybatisplus.generator.config.OutputFile;
    import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
    import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
    import com.hillky.imooc_mall.ImoccMallApplication;
    import javafx.application.Application;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;

    import java.util.Collections;

    @SpringBootTest(classes = ImoccMallApplication.class)
    class ImoccMallApplicationTests 
    {

        @Test
        void contextLoads() {
            FastAutoGenerator.create("jdbc:mysql://localhost:3306/imooc_mall""root""root")
                    .globalConfig(builder -> {
                        builder.author("hillky"// 设置作者
                                .fileOverride() // 覆盖已生成文件
                                .outputDir("E:\\IdeaProjects\\imocc_mall\\src\\main\\java"); // 指定输出目录
                    })
                    .packageConfig(builder -> {
                        builder.parent("com.hillky.imooc_mall"// 设置父包名
                                .moduleName(""// 设置父包模块名
                                .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E:\\IdeaProjects\\imocc_mall\\src\\main\\resources\\mapper")); // 设置mapperXml生成路径
                    })
                    .strategyConfig(builder -> {
                        builder.addInclude("imooc_mall_category","imooc_mall_order","imooc_mall_order_item","imooc_mall_product","imooc_mall_user"// 设置需要生成的表名
                                .addTablePrefix("imooc_mall"); // 设置过滤表前缀
                    })
                    .templateEngine(new VelocityTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                    .execute();

        }

    }

  • 配置文件编写

    server:
      port: 9090
      ip: localhost
    #  ip: 39.108.128.26

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/imooc_mall?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf8&useSSL=false
        #    url: jdbc:mysql://39.108.128.26:3306/bishe?serverTimezone=GMT%2b8
        username: root
        password: root
      servlet:
        multipart:
          max-file-size: 100MB
          max-request-size: 100MB
    mybatis:
      mapper-locations: classpath:mapper/*.xml  #扫描所有mybatis的xml文件
    #  configuration:
    #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl



SpringBoot使用AOP统一打印请求和返回信息

  • maven配置的spring-boot-starter-web排去,然后加入依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <!--            排除logback依赖-->
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
  • 新建log4j2的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="fatal">
        <Properties>
            <!--    服务器的home目录-->
            <!--    <Property name="baseDir" value="${sys:user.home}/logs"/>-->
            <!--    当前项目的日志文件地址-->
            <Property name="baseDir" value="${sys:user.dir}/logs"/>
        </Properties>

        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
                <ThresholdFilter level="info" onMatch="ACCEPT"
                                 onMismatch="DENY"/>

                <PatternLayout
                        pattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/>

            </Console>

            <!--debug级别日志文件输出-->
            <RollingFile name="debug_appender" fileName="${baseDir}/debug.log"
                         filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">

                <!-- 过滤器 -->
                <Filters>
                    <!-- 限制日志级别在debug及以上在info以下 -->
                    <ThresholdFilter level="debug"/>
                    <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <!-- 日志格式 -->
                <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
                <!-- 策略 -->
                <Policies>
                    <!-- 每隔一天转存 -->
                    <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                    <!-- 文件大小 -->
                    <SizeBasedTriggeringPolicy size="100 MB"/>
                </Policies>
            </RollingFile>

            <!-- info级别日志文件输出 -->
            <RollingFile name="info_appender" fileName="${baseDir}/info.log"
                         filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">

                <!-- 过滤器 -->
                <Filters>
                    <!-- 限制日志级别在info及以上在error以下 -->
                    <ThresholdFilter level="info"/>
                    <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <!-- 日志格式 -->
                <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
                <!-- 策略 -->
                <Policies>
                    <!-- 每隔一天转存 -->
                    <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                    <!-- 文件大小 -->
                    <SizeBasedTriggeringPolicy size="100 MB"/>
                </Policies>
            </RollingFile>

            <!-- error级别日志文件输出 -->
            <RollingFile name="error_appender" fileName="${baseDir}/error.log"
                         filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">

                <!-- 过滤器 -->
                <Filters>
                    <!-- 限制日志级别在error及以上 -->
                    <ThresholdFilter level="error"/>
                </Filters>
                <!-- 日志格式 -->
                <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
                <Policies>
                    <!-- 每隔一天转存 -->
                    <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                    <!-- 文件大小 -->
                    <SizeBasedTriggeringPolicy size="100 MB"/>
                </Policies>
            </RollingFile>
        </Appenders>
        <Loggers>
            <Root level="debug">
                <AppenderRef ref="Console"/>
                <AppenderRef ref="debug_appender"/>
                <AppenderRef ref="info_appender"/>
                <AppenderRef ref="error_appender"/>
            </Root>

        </Loggers>
    </Configuration>


  • 新建logs目录

  • maven配置加入aop依赖

    <!--        AOP依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
  • 新建过滤器

    package com.hillky.imooc_mall.filter;

    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;

    import javax.servlet.http.HttpServletRequest;
    import java.util.Arrays;

    /**
     * 打印请求和响应信息
     */

    @Aspect
    @Component
    public class WebLogAspect {

        //拿到日志对象
        private final Logger log = LoggerFactory.getLogger(WebLogAspect.class);

        @Pointcut("execution(public * com.hillky.imooc_mall.controller.*.*(..))")
        public void webLog() {

        }

        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) {
            //收到请求,记录请求内容
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            HttpServletRequest request = requestAttributes.getRequest();

            //记录请求url
            log.info("URL: " + request.getRequestURL().toString());
            //记录请求方法
            log.info("HTTP_METHOD: " + request.getMethod());
            //记录请求IP
            log.info("IP: " + request.getRemoteAddr());
            //获取类的信息
            log.info("CLASS_METHOD: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            //参数信息
            log.info("ARGS: " + Arrays.toString(joinPoint.getArgs()));
        }

        @AfterReturning(returning = "res", pointcut = "webLog()")
        public void doAfterReturning(Object res) throws JsonProcessingException {
            //处理完请求,返回内容
            log.info("RESPONSE : " + new ObjectMapper().writeValueAsString(res));
        }
    }


API统一返回对象

  • 返回结果封装

    package com.hillky.imooc_mall.common;


    import com.hillky.imooc_mall.exception.ImoocMallExceptionEnum;

    /**
     * 描述:     通用返回对象
     */

    public class ApiRestResponse<T{

        private Integer status;

        private String msg;

        private T data;

        private static final int OK_CODE = 10000;

        private static final String OK_MSG = "SUCCESS";

        public ApiRestResponse(Integer status, String msg, T data) {
            this.status = status;
            this.msg = msg;
            this.data = data;
        }

        public ApiRestResponse(Integer status, String msg) {
            this.status = status;
            this.msg = msg;
        }

        public ApiRestResponse() {
            this(OK_CODE, OK_MSG);
        }

        public static <T> ApiRestResponse<T> success() {
            return new ApiRestResponse<>();
        }

        public static <T> ApiRestResponse<T> success(T result) {
            ApiRestResponse<T> response = new ApiRestResponse<>();
            response.setData(result);
            return response;
        }

        public static <T> ApiRestResponse<T> error(Integer code, String msg) {
            return new ApiRestResponse<>(code, msg);
        }

        public static <T> ApiRestResponse<T> error(ImoocMallExceptionEnum ex) {
            return new ApiRestResponse<>(ex.getCode(), ex.getMsg());
        }

        @Override
        public String toString() {
            return "ApiRestResponse{" +
                    "status=" + status +
                    ", msg='" + msg + '\'' +
                    ", data=" + data +
                    '}';
        }

        public Integer getStatus() {
            return status;
        }

        public void setStatus(Integer status) {
            this.status = status;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public static int getOkCode() {
            return OK_CODE;
        }

        public static String getOkMsg() {
            return OK_MSG;
        }
    }

  • 异常枚举

    package com.hillky.imooc_mall.exception;

    /**
     * 描述:     异常枚举
     */


    public enum ImoocMallExceptionEnum {
        NEED_USER_NAME(10001"用户名不能为空"),
        NEED_PASSWORD(10002"密码不能为空"),
        PASSWORD_TOO_SHORT(10003"密码长度不能小于8位"),
        NAME_EXISTED(10004"不允许重名"),
        INSERT_FAILED(10005"插入失败,请重试"),
        WRONG_PASSWORD(10006"密码错误"),
        NEED_LOGIN(10007"用户未登录"),
        UPDATE_FAILED(10008"更新失败"),
        NEED_ADMIN(10009"无管理员权限"),
        PARA_NOT_NULL(10010"参数不能为空"),
        CREATE_FAILED(10011"新增失败"),
        REQUEST_PARAM_ERROR(10012"参数错误"),
        DELETE_FAILED(10013"删除失败"),
        MKDIR_FAILED(10014"文件夹创建失败"),
        UPLOAD_FAILED(10015"图片上传失败"),
        NOT_SALE(10016"商品状态不可售"),
        NOT_ENOUGH(10017"商品库存不足"),
        CART_EMPTY(10018"购物车已勾选的商品为空"),
        NO_ENUM(10019"未找到对应的枚举"),
        NO_ORDER(10020"订单不存在"),
        NOT_YOUR_ORDER(10021"订单不属于你"),
        WRONG_ORDER_STATUS(10022"订单状态不符"),
        SYSTEM_ERROR(20000"系统异常,请从控制台或日志中查看具体错误信息");
        /**
         * 异常码
         */

        Integer code;
        /**
         * 异常信息
         */

        String msg;

        ImoocMallExceptionEnum(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }

        public Integer getCode() {
            return code;
        }

        public void setCode(Integer code) {
            this.code = code;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }
    }

注册接口开发

  • service层编写

    @Override
        public void register(String username,String password) throws ImoocMallException {
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("username",username);
            User user = getOne(userQueryWrapper);
            if(!ObjectUtil.isNull(user)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
            }
            User registerUser = new User();
            registerUser.setUsername(username);
            registerUser.setPassword(SecureUtil.md5(password));
            if(!this.save(registerUser)){
                throw new ImoocMallException(ImoocMallExceptionEnum.INSERT_FAILED);
            }
        }
  • controller层编写

    @PostMapping("/register")
        public ApiRestResponse register(@RequestParam("username")String username,@RequestParam("password")String password) throws ImoocMallException {
            if(StrUtil.isEmpty(username)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_USER_NAME);
            }
            if(StrUtil.isEmpty(password)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_PASSWORD);
            }
            if(password.length()<8){
                return ApiRestResponse.error(ImoocMallExceptionEnum.PASSWORD_TOO_SHORT);
            }
            userService.register(username, password);
            return ApiRestResponse.success();
        }
  • 统一异常

    package com.hillky.imooc_mall.exception;

    import lombok.Data;

    @Data
    public class ImoocMallException extends Exception{
        private Integer code;
        private String message;

        public ImoocMallException(ImoocMallExceptionEnum ex){
            this(ex.getCode(),ex.getMsg());
        }

        public ImoocMallException(Integer code, String msg) {
            this.code=code;
            this.message=msg;
        }
    }

统一异常全局处理

统一异常处理

package com.hillky.imooc_mall.exception;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.hillky.imooc_mall.common.ApiRestResponse;
import com.sun.org.slf4j.internal.LoggerFactory;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Log log = LogFactory.get();

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e)
{
        log.error("Default Exception",e);
        return ApiRestResponse.error(ImoocMallExceptionEnum.SYSTEM_ERROR);
    }

    @ResponseBody
    @ExceptionHandler(ImoocMallException.class)
    public Object handleImoocMallException(ImoocMallException e)
{
        log.error("ImoocMallException",e);
        return ApiRestResponse.error(e.getCode(),e.getMessage());
    }
}

登录接口开发

  • service层

    @Override
        public User login(String username, String password) throws ImoocMallException {
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("username",username);
            User user = getOne(userQueryWrapper);
            if(!user.getPassword().equals(SecureUtil.md5(password))){
                throw new ImoocMallException(ImoocMallExceptionEnum.WRONG_PASSWORD);
            }
            return user;
        }
  • controller层

    @PostMapping("/login")
        public ApiRestResponse login(@RequestParam("username")String username,@RequestParam("password")String password) throws ImoocMallException {
            if(StrUtil.isEmpty(username)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_USER_NAME);
            }
            if(StrUtil.isEmpty(password)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_PASSWORD);
            }
            User user = userService.login(username, password);
            return ApiRestResponse.success(user);
        }

更新用户签名接口开发

  • service层

    @Override
        public void updateSign(User user, String signature) {
            user.setPersonalizedSignature(signature);
            this.updateById(user);
        }
  • controller层

    @PostMapping("/user/update")
        public ApiRestResponse updateSign(@RequestParam("signature")String signature, HttpSession session) throws ImoocMallException {
            User currentUser = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(currentUser)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_LOGIN);
            }
            this.userService.updateSign(currentUser,signature);
            return ApiRestResponse.success();
        }

用户注销接口开发

  • controller层

    @PostMapping("/user/logout")
        public ApiRestResponse logout(HttpSession session) throws ImoocMallException {
            User currentUser = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(currentUser)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_LOGIN);
            }
            session.removeAttribute(Constant.IMOOC_MALL_USER);
            return ApiRestResponse.success();
        }

管理用户登录接口开发

  • service层

    @Override
        public boolean checkAdmin(User user) {
            return user.getRole().equals(2);
        }
  • controller层

    @PostMapping("/adminLogin")
        public ApiRestResponse adminLogin(@RequestParam("username")String username, @RequestParam("password")String password, HttpSession session) throws ImoocMallException {
            if(StrUtil.isEmpty(username)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_USER_NAME);
            }
            if(StrUtil.isEmpty(password)){
                return  ApiRestResponse.error(ImoocMallExceptionEnum.NEED_PASSWORD);
            }
            User user = userService.login(username, password);
            if(userService.checkAdmin(user)){
                user.setPassword(null);
                session.setAttribute(Constant.IMOOC_MALL_USER,user);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_ADMIN);
            }
            return ApiRestResponse.success();
        }

商品分类模块

添加商品分类

  • 请求参数封装

    package com.hillky.imooc_mall.entity.request;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class AddCategoryReq {
        private String name;
        private Integer type;
        private Integer parentId;
        private Integer orderNum;

    }

  • service层编写

    @Override
        public void addCategory(AddCategoryReq addCategoryReq) throws ImoocMallException {
            Category category = new Category();
            BeanUtils.copyProperties(addCategoryReq,category);
            QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("name",category.getName());
            Category dbCategory = this.getOne(queryWrapper);
            if(!ObjectUtil.isEmpty(dbCategory)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
            }
            boolean save = this.save(category);
            if(!save){
                throw new ImoocMallException(ImoocMallExceptionEnum.CREATE_FAILED);
            }

        }
  • controller层编写

    @PostMapping("admin/category/add")
        public ApiRestResponse addCategory(HttpSession session, @RequestBody AddCategoryReq addCategoryReq) throws ImoocMallException {
            User user = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(user)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_LOGIN);
            }
            if(userService.checkAdmin(user)){
                categoryService.addCategory(addCategoryReq);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_ADMIN);
            }
            return ApiRestResponse.success();
        }

@Valid注解优雅校验入参

  • 添加依赖,在springboot在2.3之后,spring-boot-starter-web的依赖项已经去除了validate依赖,推荐导入依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
  • 统一异常处理处理参数valid异常

    @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseBody
        public ApiRestResponse handleMethodArgumentNotValidException(
                MethodArgumentNotValidException e
    {
            log.error("MethodArgumentNotValidException: ", e);
            return handleBindingResult(e.getBindingResult());
        }

        private ApiRestResponse handleBindingResult(BindingResult result) {
            //把异常处理为对外暴露的提示
            List<String> list = new ArrayList<>();
            if (result.hasErrors()) {
                List<ObjectError> allErrors = result.getAllErrors();
                for (ObjectError objectError : allErrors) {
                    String message = objectError.getDefaultMessage();
                    list.add(message);
                }
            }
            if (list.size() == 0) {
                return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR);
            }
            return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
        }
  • 对实体类中加入注解规则

    package com.hillky.imooc_mall.entity.request;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    import javax.validation.constraints.Max;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class AddCategoryReq {
        @NotNull
        @Size(min = 3, max = 5,message = "name个数必须在3-5之间")
        private String name;
        @NotNull
        @Max(3)
        private Integer type;
        @NotNull(message = "parentId不能为空")
        private Integer parentId;
        @NotNull
        private Integer orderNum;

    }

  • 在controller层加入@valid

    @PostMapping("admin/category/add")
        public ApiRestResponse addCategory(HttpSession session, @RequestBody @Validated AddCategoryReq addCategoryReq) throws ImoocMallException {
            User user = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(user)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_LOGIN);
            }
            if(userService.checkAdmin(user)){
                categoryService.addCategory(addCategoryReq);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_ADMIN);
            }
            return ApiRestResponse.success();
        }

引入swagger2自动生成文档

  • 添加依赖

    <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
  • 配置

    package com.hillky.imooc_mall.config;

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;

    /**
     1. swagger配置类
     */

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    //是否开启 (true 开启  false隐藏。生产环境建议隐藏)
                    //.enable(false)
                    .select()
                    //扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api
                    .apis(RequestHandlerSelectors.basePackage("com.hillky.imooc_mall.controller"))
                    //指定路径处理PathSelectors.any()代表所有的路径
                    .paths(PathSelectors.any())
                    .build();
        }

        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    //设置文档标题(API名称)
                    .title("慕慕生鲜")
                    //文档描述
                    .description("接口说明")
                    //服务条款URL
                    .termsOfServiceUrl("http://localhost:9090/")
                    //版本号
                    .version("1.0.0")
                    .build();
        }
    }


  • 注解配置

    @Api(value = "测试接口", tags = "用户管理相关的接口", description = "用户测试接口")
    public class UserController {}

    @ApiOperation(value = "用户注册")
        public ApiRestResponse register(@RequestParam()...
  • 访问http://localhost:9090/swagger-ui.html

更新商品分类

  • 更新实体类请求

    package com.hillky.imooc_mall.entity.request;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    import javax.validation.constraints.Max;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class UpdateCategoryReq {
        @NotNull
        private Integer id;
        @NotNull
        @Size(min = 2, max = 5,message = "name个数必须在2-5之间")
        private String name;
        @NotNull
        @Max(3)
        private Integer type;
        @NotNull(message = "parentId不能为空")
        private Integer parentId;
        @NotNull
        private Integer orderNum;

    }

  • service层

    @Override
        public void updateCategory(Category category) throws ImoocMallException {
            if (category.getName() != null) {
                Category categoryOld = this.selectByName(category.getName());
                if (categoryOld != null && !categoryOld.getId().equals(category.getId())) {
                    throw new ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
                }
            }
            boolean b = this.updateById(category);
            if (!b) {
                throw new ImoocMallException(ImoocMallExceptionEnum.UPDATE_FAILED);
            }
        }
  • controller层

    @PostMapping("admin/category/update")
        public ApiRestResponse updateCategory(HttpSession session, @RequestBody @Validated UpdateCategoryReq updateCategoryReq) throws ImoocMallException {
            User user = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(user)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_LOGIN);
            }
            if(userService.checkAdmin(user)){
                Category category = new Category();
                BeanUtils.copyProperties(updateCategoryReq,category);
                categoryService.updateCategory(category);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.NEED_ADMIN);
            }
            return ApiRestResponse.success();
        }

统一校验管理员身份

  • 管理员校验过滤器

    package com.hillky.imooc_mall.filter;

    import cn.hutool.core.util.ObjectUtil;
    import com.hillky.imooc_mall.common.ApiRestResponse;
    import com.hillky.imooc_mall.common.Constant;
    import com.hillky.imooc_mall.entity.Category;
    import com.hillky.imooc_mall.entity.User;
    import com.hillky.imooc_mall.exception.ImoocMallException;
    import com.hillky.imooc_mall.exception.ImoocMallExceptionEnum;
    import com.hillky.imooc_mall.service.IUserService;
    import com.sun.deploy.net.HttpResponse;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;

    @Component
    public class AdminFilter implements Filter {

        @Autowired
        private IUserService userService;

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

            HttpServletRequest request= (HttpServletRequest) servletRequest;
            HttpServletResponse response= (HttpServletResponse) servletResponse;
            response.setCharacterEncoding("UTF-8");
            HttpSession session = request.getSession();

            User user = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(user)){
                response.getWriter().write("{\n" +
                        "    \"status\": 10007,\n" +
                        "    \"msg\": \"用户未登录\",\n" +
                        "    \"data\": null\n" +
                        "}");
                return;
            }
            if(userService.checkAdmin(user)){
                filterChain.doFilter(servletRequest, servletResponse);
            }else{
                response.getWriter().write("{\n" +
                        "    \"status\": 10009,\n" +
                        "    \"msg\": \"无管理员权限\",\n" +
                        "    \"data\": null\n" +
                        "}");
                return;
            }
        }

        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }

  • 配置过滤器

    package com.hillky.imooc_mall.config;

    import com.hillky.imooc_mall.filter.AdminFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    public class FilterConfig {

        @Autowired
        private AdminFilter adminFilter;

        @Bean
        public FilterRegistrationBean addFilter() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(adminFilter);
            registration.addUrlPatterns("/admin/category/*","/admin/product/*","/admin/order/*");
            registration.setName("adminFilter");
            //设置优先级别
            registration.setOrder(1);
            return registration;
        }
    }

删除分类接口

  • controller层

    @GetMapping("admin/category/delete")
        @ApiOperation(value = "后台删除目录")
        public ApiRestResponse deleteCategory(@RequestParam("id")Integer id) throws ImoocMallException {
            boolean b = this.categoryService.removeById(id);
            if(!b){
                throw new ImoocMallException(ImoocMallExceptionEnum.DELETE_FAILED);
            }
            return ApiRestResponse.success();
        }

分类列表(平铺)接口开发 (使用分页)

  • 添加分页插件的配置

    package com.hillky.imooc_mall.config;

    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    @MapperScan("com.hillky.imooc_mall.mapper")
    public class MyBatisPlusConfig {

        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }

  • controller层

    @GetMapping("admin/category/list")
        @ApiOperation(value = "后台查询目录")
        public ApiRestResponse listCategory(@RequestParam("pageNum")Integer pageNum,@RequestParam("pageSize")Integer pageSize) throws ImoocMallException {
            if(pageNum==null){
                pageNum=1;
            }
            if(pageSize==null){
                pageSize=10;
            }
            Page<Category>page=new Page<>(pageNum,pageSize);
            Page<Category> pageObject = this.categoryService.page(page);
            return ApiRestResponse.success(pageObject);
        }

用户分类列表接口 递归接口的开发

  • categoryVo对象

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class CategoryVO implements Serializable {

        private Integer id;
        private String name;
        private Integer type;
        private Integer parentId;
        private Integer orderNum;
        private Date updateTime;
        private List<CategoryVO> childCategory = new ArrayList<>();
    }

  • controller层

    @GetMapping("category/list")
        @ApiOperation(value = "后台查询目录")
        public ApiRestResponse frontListCategory() {
            List<CategoryVO> result = this.categoryService.frontListCategory();
            return ApiRestResponse.success(result);
        }
  • service层

     @Override
        public List<CategoryVO> frontListCategory() {
            ArrayList<CategoryVO> categoryVOList = new ArrayList<>();
            findChildList(categoryVOList, 0);
            return categoryVOList;
        }

        public void findChildList(List<CategoryVO> categoryVOList,Integer parentId){
            QueryWrapper<Category> categoryQueryWrapper = new QueryWrapper<>();
            categoryQueryWrapper.eq("parent_id",parentId);
            List<Category> categoryList = this.list(categoryQueryWrapper);
            //递归获取所有子类别,并组合成为一个“目录树”
            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);
                    findChildList(categoryVO.getChildCategory(), categoryVO.getId());
                }
            }

        }

集成redis缓存加速优化

  • 添加依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
  • 开启注解,添加

    @SpringBootApplication
    @MapperScan("com.hillky.imooc_mall.mapper")
    @EnableCaching
    public class ImoccMallApplication {

        public static void main(String[] args) {
            SpringApplication.run(ImoccMallApplication.classargs);
        }

    }

    @GetMapping("category/list")
        @Cacheable("frontListCategory")
        @ApiOperation(value = "后台查询目录")
        public ApiRestResponse frontListCategory() {
            List<CategoryVO> result = this.categoryService.frontListCategory();
            return ApiRestResponse.success(result);
        }

商品模块

添加商品接口

  • addproductReq实体类,添加参数校验

    package com.hillky.imooc_mall.entity.request;

    import java.util.Date;
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    import org.mapstruct.MapperConfig;

    public class AddProductReq {

        @NotNull(message = "商品名称不能为null")
        private String name;

        @NotNull(message = "商品图片不能为null")
        private String image;

        private String detail;

        @NotNull(message = "商品分类不能为null")
        private Integer categoryId;

        @NotNull(message = "商品价格不能为null")
        @Min(value = 1, message = "价格不能小于1分")
        private Integer price;

        @NotNull(message = "商品库存不能为null")
        @Max(value = 10000, message = "库存不能大于10000")
        private Integer stock;

        private Integer status;


        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name == null ? null : name.trim();
        }

        public String getImage() {
            return image;
        }

        public void setImage(String image) {
            this.image = image == null ? null : image.trim();
        }

        public String getDetail() {
            return detail;
        }

        public void setDetail(String detail) {
            this.detail = detail == null ? null : detail.trim();
        }

        public Integer getCategoryId() {
            return categoryId;
        }

        public void setCategoryId(Integer categoryId) {
            this.categoryId = categoryId;
        }

        public Integer getPrice() {
            return price;
        }

        public void setPrice(Integer price) {
            this.price = price;
        }

        public Integer getStock() {
            return stock;
        }

        public void setStock(Integer stock) {
            this.stock = stock;
        }

        public Integer getStatus() {
            return status;
        }

        public void setStatus(Integer status) {
            this.status = status;
        }

        @Override
        public String toString() {
            return "AddProductReq{" +
                    "name='" + name + '\'' +
                    ", image='" + image + '\'' +
                    ", detail='" + detail + '\'' +
                    ", categoryId=" + categoryId +
                    ", price=" + price +
                    ", stock=" + stock +
                    ", status=" + status +
                    '}';
        }
    }
  • controller层编写

    @PostMapping("/admin/product/add")
        public ApiRestResponse addProduct(@RequestBody @Validated AddProductReq addProductReq) throws ImoocMallException {
            Product product = new Product();
            BeanUtils.copyProperties(addProductReq,product);
            Product dbProduct = productService.findProductByName(product.getName());
            if(!ObjectUtil.isNull(dbProduct)){
                throw  new ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
            }
            productService.save(product);
            return ApiRestResponse.success();
        }
  • service层编写

    @Override
        public Product findProductByName(String name) {
            QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("name",name);
            return this.getOne(queryWrapper);
        }

图片上传接口

  • controller层

    @PostMapping("/admin/upload/file")
        public ApiRestResponse uploadFile(MultipartFile file) throws ImoocMallException, IOException {
            String originalFilename = file.getOriginalFilename();
            String type = FileUtil.extName(originalFilename);
            String newFileName= IdUtil.fastSimpleUUID()+ StrUtil.DOT +type;
            File destFile = new File(fileUploadPath+newFileName);
            File parentFile = destFile.getParentFile();
            if(!parentFile.exists()){
                boolean b = parentFile.mkdirs();
                if(!b){
                    throw new ImoocMallException(ImoocMallExceptionEnum.MKDIR_FAILED);
                }
            }
            file.transferTo(destFile);
            String result="http://"+serverIp+":9090/images/"+newFileName;
            return ApiRestResponse.success(result);
        }

    @Value("${file.upload.dir}")
        private String fileUploadPath;

        @Value(("${server.ip}"))
        private String serverIp;


  • 配置静态资源映射

    package com.hillky.imooc_mall.config;

    import com.hillky.imooc_mall.common.Constant;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    @Configuration
    public class MallWebConfig implements WebMvcConfigurer {

        @Value("${file.upload.dir}")
        private String fileUploadPath;

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/images/**").addResourceLocations("file:"+ fileUploadPath);
        }
    }

更新商品接口

  • UpdateProductReq实体类

    package com.hillky.imooc_mall.entity.request;

    import lombok.Data;

    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    @Data
    public class UpdateProductReq {
        @NotNull(message = "id不能为null")
        private Integer id;

        @NotNull(message = "商品名称不能为null")
        private String name;

        @NotNull(message = "商品图片不能为null")
        private String image;

        private String detail;

        @NotNull(message = "商品分类不能为null")
        private Integer categoryId;

        @NotNull(message = "商品价格不能为null")
        @Min(value = 1, message = "价格不能小于1分")
        private Integer price;

        @NotNull(message = "商品库存不能为null")
        @Max(value = 10000, message = "库存不能大于10000")
        private Integer stock;

        private Integer status;


    }
  • controller层

    @PostMapping("/admin/product/update")
        public ApiRestResponse updateProduct(@RequestBody @Validated UpdateProductReq updateProductReq) throws ImoocMallException {
            Product product = new Product();
            BeanUtils.copyProperties(updateProductReq,product);
            Product dbProduct = productService.getById(updateProductReq.getId());
            if(ObjectUtil.isNull(dbProduct)){
                throw  new ImoocMallException(ImoocMallExceptionEnum.UPDATE_FAILED);
            }
            productService.updateById(product);
            return ApiRestResponse.success();
        }

删除商品接口

  • controller层开发

     @GetMapping("/admin/product/delete")
        public ApiRestResponse deleteProduct(@RequestParam("id")Integer id) throws ImoocMallException {
            boolean b = this.productService.removeById(id);
            if(!b){
                throw new ImoocMallException(ImoocMallExceptionEnum.DELETE_FAILED);
            }
            return ApiRestResponse.success();
        }

批量上下架商品接口开发

  • controller层

     @PostMapping("/admin/product/batchUpdateSellStatus")
        public ApiRestResponse batchUpdateSellStatus(@RequestParam("ids")Integer[] ids,
                                                     @RequestParam("sellStatus") Integer sellStatus) 
    {
           this.productService.batchUpdateSellStatus(ids, sellStatus);
           return ApiRestResponse.success();
        }

  • service层

    @Override
        public void batchUpdateSellStatus(Integer[] ids, Integer sellStatus) {
            for (Integer id : ids) {
                this.productMapper.updateSellStatus(id,sellStatus);
            }
        }
  • mapper层

    @Update("update imooc_mall_product set status=#{sellStatus} where id=#{id}")
        void updateSellStatus(Integer id, Integer sellStatus);

后台商品列表接口开发,也用到递归

  • ProductListReq封装

    package com.hillky.imooc_mall.entity.request;

    import lombok.Data;

    import java.util.Date;
    @Data
    public class ProductListReq {

        private String keyword;

        private Integer categoryId;

        private String orderBy;

        private Integer pageNum = 1;

        private Integer pageSize = 10;

        public String getKeyword() {
            return keyword;
        }

        public void setKeyword(String keyword) {
            this.keyword = keyword;
        }

    }
  • controller层

    @GetMapping("/product/list")
        public ApiRestResponse frontListProduct(ProductListReq productListReq) throws ImoocMallException {
            IPage<Product> pageObject = this.productService.frontListProduct(productListReq);
            return ApiRestResponse.success(pageObject);
        }
  • service层

    @Override
        public IPage<Product> frontListProduct(ProductListReq productListReq) {
            QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
    //        if(productListReq.getOrderBy().equalsIgnoreCase("price desc")){
    //            queryWrapper.orderByDesc("price");
    //        }
    //        if(productListReq.getOrderBy().equalsIgnoreCase("price asc")){
    //            queryWrapper.orderByAsc("price");
    //        }
            if(Constant.ProductListOrderBy.PRICE_ASC_DESC.contains(productListReq.getOrderBy())){
                queryWrapper.last("order by "+productListReq.getOrderBy());
            }
            //查找目录要把子目录同样查出来
            ArrayList<Integer> ids = new ArrayList<>();
            if(!ObjectUtil.isNull(productListReq.getCategoryId())){
                List<CategoryVO> categoryVOList = categoryService.frontListCategory(productListReq.getCategoryId());
                ids.add(productListReq.getCategoryId());
                this.getCategoryIds(categoryVOList,ids);
            }
            if(ids.size()>0){
                queryWrapper.in("category_id",ids);
            }

            if(StrUtil.isNotBlank(productListReq.getKeyword())){
                queryWrapper.like("name",productListReq.getKeyword());
            }
            Page<Product> page=new Page<>(productListReq.getPageNum(),productListReq.getPageSize());
            Page<Product> pageObject = this.page(page,queryWrapper);
            return pageObject;
        }

        public void getCategoryIds(List<CategoryVO> categoryVOList,ArrayList<Integer> categoryIds){
            for (CategoryVO categoryVO : categoryVOList) {
                if(categoryVO!=null){
                    categoryIds.add(categoryVO.getId());
                    this.getCategoryIds(categoryVO.getChildCategory(),categoryIds);
                }
            }
        }

商品详情接口开发

  • controller层

    @GetMapping("/product/detail")
        public ApiRestResponse productDetail(@RequestParam("id")Integer id){
            Product result = this.productService.getById(id);
            return ApiRestResponse.success(result);
        }

购物车模块

用户过滤器开发

  • 用户过滤器

    package com.hillky.imooc_mall.filter;

    import cn.hutool.core.util.ObjectUtil;
    import com.hillky.imooc_mall.common.Constant;
    import com.hillky.imooc_mall.entity.User;
    import com.hillky.imooc_mall.service.IUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;

    @Component
    public class UserFilter implements Filter {

        @Autowired
        private IUserService userService;

        public static User currentUser;

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

            HttpServletRequest request= (HttpServletRequest) servletRequest;
            HttpServletResponse response= (HttpServletResponse) servletResponse;
            response.setCharacterEncoding("UTF-8");
            HttpSession session = request.getSession();

            User user = (User) session.getAttribute(Constant.IMOOC_MALL_USER);
            if(ObjectUtil.isNull(user)){
                response.getWriter().write("{\n" +
                        "    \"status\": 10007,\n" +
                        "    \"msg\": \"用户未登录\",\n" +
                        "    \"data\": null\n" +
                        "}");
                return;
            }
            currentUser=user;
            filterChain.doFilter(servletRequest, servletResponse);

        }

        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }

  • 配置过滤器

    @Bean
        public FilterRegistrationBean addUserFilter() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(userFilter);
            registration.addUrlPatterns("/cart/*","/order/*");
            registration.setName("userFilter");
            //设置优先级别 数字越小优先级越高
            registration.setOrder(1);
            return registration;
        }

添加商品接口开发

  • CartVo对象

    package com.hillky.imooc_mall.entity.vo;

    import lombok.Data;

    @Data
    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;
    }
  • controller层编写

    @GetMapping("/add")
        public ApiRestResponse cartAdd(@RequestParam Integer productId, @RequestParam Integer count) throws ImoocMallException {
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> cartVOList = this.cartService.cartAdd(productId,userId,count);
            return ApiRestResponse.success(cartVOList);
        }
  • service层编写

     @Override
        public List<CartVO> cartAdd(Integer productId, Integer userId, Integer count) throws ImoocMallException {
            validAddCart(productId,count);
            Cart cartDB = this.findCartByProductIdAndUserId(productId, userId);
            if(ObjectUtil.isNull(cartDB)){
                //没有记录则新增
                Cart cart = new Cart();
                cart.setProductId(productId);
                cart.setUserId(userId);
                cart.setQuantity(count);
                cart.setSelected(Constant.Cart.CHECKED);
                this.save(cart);
            }else{
                //有记录则更新,数量加count
                cartDB.setQuantity(cartDB.getQuantity()+count);
                cartDB.setSelected(Constant.Cart.CHECKED);
                this.updateById(cartDB);
            }
            return this.cartList(userId);
        }

购物车列表接口

  • controller层

    @GetMapping("/list")
        public ApiRestResponse cartList(){
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> result = this.cartService.cartList(userId);
            return ApiRestResponse.success(result);
        }
  • service层

    @Override
        public List<CartVO> cartList(Integer userId) {
            //这个方法也可用多表查询语句进行封装
            QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id",userId);
            List<Cart> cartList = this.list(queryWrapper);
            List<CartVO> cartVOList=new ArrayList<>();
            for (Cart cart : cartList) {
                CartVO cartVO = new CartVO();
                BeanUtils.copyProperties(cart,cartVO);
                Product product = this.productService.getById(cartVO.getProductId());
                cartVO.setPrice(product.getPrice());
                cartVO.setProductName(product.getName());
                cartVO.setProductImage(product.getImage());
                cartVO.setTotalPrice(cartVO.getPrice()*cartVO.getQuantity());
                cartVOList.add(cartVO);
            }
            return cartVOList;
        }

更新购物车接口

  • controller层

    @PostMapping("/update")
        public ApiRestResponse cartUpdate(@RequestParam("productId") Integer productId,@RequestParam("count") Integer count) throws ImoocMallException {
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> result = this.cartService.cartUpdate(userId,productId,count);
            return ApiRestResponse.success(result);
        }

  • service层

     @Override
        public List<CartVO> cartUpdate(Integer userId,Integer productId,Integer count) throws ImoocMallException {
            validAddCart(productId,count);
            Cart cart = this.findCartByProductIdAndUserId(productId, userId);
            cart.setQuantity(count);
            this.updateById(cart);
            return this.cartList(userId);
        }

删除购物车列表

  • controller层

    @PostMapping("/delete")
        public ApiRestResponse cartDeletee(@RequestParam("productId") Integer productId){
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> result = this.cartService.cartDelete(userId,productId);
            return ApiRestResponse.success(result);
        }
  • service层

    @Override
        public List<CartVO> cartDelete(Integer userId, Integer productId) {
            Cart cart = findCartByProductIdAndUserId(productId, userId);
            this.removeById(cart);
            return this.cartList(userId);
        }

选中/不选中购物车的某个商品

  • controller层

    @PostMapping("/select")
        public ApiRestResponse cartSelect(@RequestParam("productId") Integer productId,@RequestParam("selected")Integer selected) throws ImoocMallException {
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> result = this.cartService.cartSelect(userId,productId,selected);
            return ApiRestResponse.success(result);
        }
  • service层

    @Override
        public List<CartVO> cartSelect(Integer userId, Integer productId, Integer selected) throws ImoocMallException {
            Cart cart = this.findCartByProductIdAndUserId(productId, userId);
            if(ObjectUtil.isNull(cart)){
                throw new ImoocMallException(ImoocMallExceptionEnum.UPDATE_FAILED);
            }else{
                cart.setSelected(selected);
                this.updateById(cart);
            }
            return this.cartList(userId);
        }

全选/全不选购物车的某个商品

  • controller层

    @PostMapping("/selectAll")
        public ApiRestResponse cartSelectAll(@RequestParam("selected")Integer selected) throws ImoocMallException {
            Integer userId = UserFilter.currentUser.getId();
            List<CartVO> result = this.cartService.cartSelectAll(userId,selected);
            return ApiRestResponse.success(result);
        }
  • service层

     @Override
        public List<CartVO> cartSelectAll(Integer userId, Integer selected) throws ImoocMallException {
            QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id",userId);
            List<Cart> cartList = this.list(queryWrapper);
            for (Cart cart : cartList) {
                this.cartSelect(userId,cart.getProductId(),selected);
            }
            return this.cartList(userId);
        }

订单模块

创建订单接口

  • 请求类

    @Data
    public class CreateOrderReq {

        @NotNull
        private String receiverName;
        @NotNull
        private String receiverMobile;
        @NotNull
        private String receiverAddress;
        private Integer postage=0;
        private Integer payType=1;
    }
  • controller层编写

     @PostMapping("/create")
        public ApiRestResponse createOrder(@RequestBody CreateOrderReq createOrderReq) throws ImoocMallException {
            String orderNo=orderService.createOrder(createOrderReq);
            return ApiRestResponse.success(orderNo);
        }
  • service层编写 添加数据库事务

     @Override
        @Transactional
        public String createOrder(CreateOrderReq createOrderReq) throws ImoocMallException {
            //获取用户id
            Integer userId = UserFilter.currentUser.getId();
            //根据用户id查询购物车勾选商品的信息
            List<CartVO> cartVOList = cartService.cartList(userId);
            if (ObjectUtil.isNull(cartVOList)){
                throw new ImoocMallException(ImoocMallExceptionEnum.CART_EMPTY);
            }
            //校验已选商品的购买是否属于可买阶段
            for (CartVO cartVO : cartVOList) {
                validBuy(cartVO.getProductId(),cartVO.getQuantity());
            }
            //生成订单号
            long id = IdUtil.getSnowflakeNextId();
            String orderNo = String.valueOf(id);
            //将购物车对象转换为orderItem对象,并保存到order_item表中
            ArrayList<OrderItem> orderItemList = new ArrayList<>();
            for (CartVO cartVO : cartVOList) {
                OrderItem orderItem = new OrderItem();
                BeanUtils.copyProperties(cartVO,orderItem);
                orderItem.setOrderNo(orderNo);
                orderItem.setUnitPrice(cartVO.getPrice());
                orderItem.setProductImg(cartVO.getProductImage());
                orderItemList.add(orderItem);
            }
            //将orderItemList存入数据库中
            this.orderItemService.saveBatch(orderItemList);
            //对商品数量进行更新
            for (OrderItem orderItem : orderItemList) {
                Product product = productService.getById(orderItem.getProductId());
                product.setStock(product.getStock()-orderItem.getQuantity());
                this.productService.updateById(product);
            }

            //对购物车列表进行删除
            for (CartVO cartVO : cartVOList) {
                this.cartService.removeById(cartVO.getId());
            }

            //,新增订单
            Order order = new Order();
            order.setOrderNo(orderNo);
            order.setUserId(userId);
            order.setTotalPrice(getTotalPrice(orderItemList));
            order.setReceiverAddress(createOrderReq.getReceiverAddress());
            order.setReceiverMobile(createOrderReq.getReceiverMobile());
            order.setReceiverName(createOrderReq.getReceiverName());
            order.setPaymentType(createOrderReq.getPayType());
            order.setPostage(createOrderReq.getPostage());
            order.setOrderStatus(Constant.OrderStatusEnum.NOT_PAID.getCode());
            this.save(order);
            //返回订单号
            return orderNo;
        }

        private Integer getTotalPrice(ArrayList<OrderItem> orderItems) {
            Integer totalPrice=0;
            for (OrderItem orderItem : orderItems) {
                totalPrice+=orderItem.getTotalPrice();
            }
            return totalPrice;
        }

        private void validBuy(Integer productId, Integer count) throws ImoocMallException {
            Product product = this.productService.getById(productId);
            if(ObjectUtil.isNull(product)||product.getStatus().equals(Constant.SaleStatus.NOT_SALE)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NOT_SALE);
            }
            if(count>product.getStock()){
                throw new ImoocMallException(ImoocMallExceptionEnum.NOT_ENOUGH);
            }
        }

订单详情接口

  • OrderVO实体类

    package com.hillky.imooc_mall.entity.vo;

    import com.hillky.imooc_mall.entity.OrderItem;
    import lombok.Data;

    import java.util.Date;
    import java.util.List;

    @Data
    public class OrderVO {


        /**
         * 订单号(非主键id)
         */

        private String orderNo;

        /**
         * 用户id
         */

        private Integer userId;

        /**
         * 订单总价格
         */

        private Integer totalPrice;

        /**
         * 收货人姓名快照
         */

        private String receiverName;

        /**
         * 收货人手机号快照
         */

        private String receiverMobile;

        /**
         * 收货地址快照
         */

        private String receiverAddress;

        /**
         * 订单状态: 0用户已取消,10未付款(初始状态),20已付款,30已发货,40交易完成
         */

        private Integer orderStatus;

        /**
         * 运费,默认为0
         */

        private Integer postage;

        /**
         * 支付类型,1-在线支付
         */

        private Integer paymentType;

        /**
         * 发货时间
         */

        private Date deliveryTime;

        /**
         * 支付时间
         */

        private Date payTime;

        /**
         * 交易完成时间
         */

        private Date endTime;

        private String orderStatusName;

        private List<OrderItemVO> orderItemVOList;
    }

  • OrderItemVO实体类

    package com.hillky.imooc_mall.entity.vo;

    import lombok.Data;

    import java.util.Date;
    @Data
    public class OrderItemVO {
        /**
         * 归属订单id
         */

        private String orderNo;

        /**
         * 商品名称
         */

        private String productName;

        /**
         * 商品图片
         */

        private String productImg;

        /**
         * 单价(下单时的快照)
         */

        private Integer unitPrice;

        /**
         * 商品数量
         */

        private Integer quantity;

        /**
         * 商品总价
         */

        private Integer totalPrice;


    }

  • controller层

    @GetMapping("/detail")
        public ApiRestResponse orderDetail(@RequestParam String orderNo) throws ImoocMallException {
            OrderVO orderVo=orderService.orderDetail(orderNo);
            return ApiRestResponse.success(orderVo);
        }
  • service层

    @Override
        public OrderVO orderDetail(String orderNo) throws ImoocMallException {
            //根据orderNo查询order对象
            QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
            orderQueryWrapper.eq("order_no",orderNo);
            Order order = this.getOne(orderQueryWrapper);
            if(ObjectUtil.isNull(order)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NO_ORDER);
            }
            if(order.getUserId()!=UserFilter.currentUser.getId()){
                throw new ImoocMallException(ImoocMallExceptionEnum.NOT_YOUR_ORDER);
            }
            //oderVO值与order赋予
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(order,orderVO);
            //orderVO对象中statusName的封装
            orderVO.setOrderStatusName(Constant.OrderStatusEnum.codeOf(order.getOrderStatus()).getValue());
            //orderVO OrderItemVoList
            ArrayList<OrderItemVO> orderItemVOList = new ArrayList<>();
            QueryWrapper<OrderItem> orderItemQueryWrapper = new QueryWrapper<>();
            orderItemQueryWrapper.eq("order_no",orderNo);
            List<OrderItem> orderItemList = this.orderItemService.list(orderItemQueryWrapper);
            for (OrderItem orderItem : orderItemList) {
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(orderItem,orderItemVO);
                orderItemVOList.add(orderItemVO);
            }
            orderVO.setOrderItemVOList(orderItemVOList);
            return orderVO;
        }

前台订单列表

  • controller层

    @GetMapping("/list")
        public ApiRestResponse orderFrontList(@RequestParam Integer pageNum,@RequestParam Integer pageSize) throws ImoocMallException {
            Page<Order> orderPage = orderService.orderFrontList(pageNum, pageSize);
            return ApiRestResponse.success(orderPage);
        }
  • service层,mybatisplus分页泛型转换

    @Override
        public Page<Order> orderFrontList(Integer pageNum, Integer pageSize) throws ImoocMallException {
            Page<Order> page = new Page<Order>(pageNum,pageSize);
            Integer userId = UserFilter.currentUser.getId();
            QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
            orderQueryWrapper.eq("user_id",userId);
            Page<Order> orderPage = this.page(page, orderQueryWrapper);
            orderPage.convert(order -> {
                OrderVO orderVO = new OrderVO();
                try {
                    orderVO=getOrderVo(order);
                } catch (ImoocMallException e) {
                    e.printStackTrace();
                }
                return orderVO;
            });
            return orderPage;
        }

前台取消订单接口

  • controller层

    @GetMapping("/cancel")
        public ApiRestResponse orderCancel(@RequestParam String orderNo) throws ImoocMallException {
           orderService.orderCancel(orderNo);
            return ApiRestResponse.success();
        }
  • service层

     @Override
        public void orderCancel(String orderNo) throws ImoocMallException {
            //判断订单是否存在
            QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
            orderQueryWrapper.eq("order_no",orderNo);
            Order order = this.getOne(orderQueryWrapper);
            if(ObjectUtil.isNull(order)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NO_ORDER);
            }
            //判读订单身份归属
            Integer userId = UserFilter.currentUser.getId();
            if(!order.getUserId().equals(userId)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NOT_YOUR_ORDER);
            }
            //更改订单状态,要在订单状态为未支付状态下更改
            if(order.getOrderStatus().equals(Constant.OrderStatusEnum.NOT_PAID.getCode())){
                order.setOrderStatus(Constant.OrderStatusEnum.CANCELED.getCode());
                //更改订单的结束时间
                order.setEndTime(new Date());
                //更新数据
                this.updateById(order);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.WRONG_ORDER_STATUS);
            }

        }

二维码接口开发(hutool工具生成)

  • 引入依赖

    <dependency>
                <groupId>com.google.zxing</groupId>
                <artifactId>core</artifactId>
                <version>3.3.3</version>
            </dependency>
  • controller层

    @PostMapping("/qrcode")
        public ApiRestResponse orderQrcode(@RequestParam String orderNo) throws ImoocMallException {
            String payUrl=orderService.orderQrcode(orderNo);
            return ApiRestResponse.success(payUrl);
        }
  • service层

    @Override
        public String orderQrcode(String orderNo) {
            //解码结果:支付的url拼接
            String payUrl="http://"+ip+":"+port+"/pay?orderNo="+orderNo;
            //图片存放的文件
            String fileName=orderNo+".png";
            File file = new File(fileUploadPath + fileName);
            QrCodeUtil.generate(payUrl, 300300,file);
            //返回图片url
            String imgUrl="http://"+ip+":"+port+"/images/"+fileName;
            return imgUrl;
        }

前台支付订单窗口

  • controller层

    @GetMapping("/pay")
        public ApiRestResponse orderPay(@RequestParam String orderNo) throws ImoocMallException {
            orderService.orderPay(orderNo);
            return ApiRestResponse.success();
        }
  • service层

     @Override
        public void orderPay(String orderNo) throws ImoocMallException {
            //查询订单是否合法
            QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("order_no", orderNo);
            Order order = this.getOne(queryWrapper);
            if(ObjectUtil.isNull(order)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NO_ORDER);
            }
            //更改订单状态(要在初始状态下进行,取消的不能进行)
            if(order.getOrderStatus().equals(Constant.OrderStatusEnum.NOT_PAID.getCode())){
                order.setOrderStatus(Constant.OrderStatusEnum.PAID.getCode());
                //更改订单付款时间
                order.setPayTime(new Date());
                this.updateById(order);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.WRONG_ORDER_STATUS);
            }

        }

后台订单列表接口开发

  • controller层

    @GetMapping("/admin/order/list")
        public ApiRestResponse orderAdminList(@RequestParam Integer pageNum,@RequestParam Integer pageSize)  {
            return ApiRestResponse.success(orderService.orderAdminList(pageNum,pageSize));
        }
  • service层

     @Override
        public IPage<Order> orderAdminList(Integer pageNum, Integer pageSize) {
            //构建page对象,进行分页查询
            IPage<Order> orderIPage = new Page<Order>(pageNum,pageSize);
            IPage<Order> page = this.page(orderIPage);

            //convert order类型的转换
            page.convert(order -> {
                OrderVO orderVo = new OrderVO();
                try {
                    orderVo=getOrderVo(order);
                } catch (ImoocMallException e) {
                    e.printStackTrace();
                }
                return orderVo;
            });
            return page;

        }

后台:订单发货

  • controller层

    @PostMapping("/admin/order/delivered")
        public ApiRestResponse orderDeliver(@RequestParam String orderNo) throws ImoocMallException {
            orderService.orderDeliver(orderNo);
            return ApiRestResponse.success();
        }
  • service层

    @Override
        public void orderDeliver(String orderNo) throws ImoocMallException {
            //查找订单,校验合法
            Order order = getOrderByOrderNo(orderNo);
            if(ObjectUtil.isNull(order)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NO_ORDER);
            }
            //修改订单状态(只在付款状态后),设置发货时间,更新操作
            if(order.getOrderStatus().equals(Constant.OrderStatusEnum.PAID.getCode())){
                order.setOrderStatus(Constant.OrderStatusEnum.DELIVERED.getCode());
                order.setDeliveryTime(new Date());
                this.updateById(order);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.WRONG_ORDER_STATUS);
            }

        }

前后台通用订单完结

  • controller层

    @PostMapping("/order/finish")
        public ApiRestResponse orderFinish(@RequestParam String orderNo) throws ImoocMallException {
            orderService.orderFinish(orderNo);
            return ApiRestResponse.success();
        }
  • service层

    @Override
        public void orderFinish(String orderNo) throws ImoocMallException {
            //查询订单,校验合法性
            Order order = this.getOrderByOrderNo(orderNo);
            if(ObjectUtil.isNull(order)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NO_ORDER);
            }
            //校验订单所属,用户只能对自己的订单进行完结,管理员能对所有订单完结
            User user = UserFilter.currentUser;
            if(!order.getUserId().equals(user.getId())&&!user.getRole().equals(2)){
                throw new ImoocMallException(ImoocMallExceptionEnum.NOT_YOUR_ORDER);
            }
            //订单在发货状态下更新状态,更新时间,更新操作
            if(order.getOrderStatus().equals(Constant.OrderStatusEnum.DELIVERED.getCode())){
                order.setOrderStatus(Constant.OrderStatusEnum.FINISHED.getCode());
                order.setEndTime(new Date());
                this.updateById(order);
            }else{
                throw new ImoocMallException(ImoocMallExceptionEnum.WRONG_ORDER_STATUS);
            }
        }

分类:

后端

标签:

Java

作者介绍

H
Hillky
V1