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.class, args); } } @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, 300, 300,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); } }