在商品分类上需要继续做归类操作
分类设置成三级
层级太深的弊端:对用户不友好,不利于查找;后台管理部方便
分类模块主要功能分类数据的设置
根据分类的父级目录递归查找
接口与表设计https://blog.csdn.net/a2272062968/article/details/123385857
跟用户接口类似
参数校验| 注解 | 说明 |
|---|---|
| @Valid | 需要验证 |
| @NotNull | 非空 |
| @Max(Value) | 最大值 |
| @Size(max,min) | 字符串长度范围限制 |
在前台传入的实体类定义中参数上增加对应规则的注解即可,可以增加多个规则
package com.imooc.mall.model.request;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Data
public class AddCategoryReq {
@Size(min = 2,max = 5)
private String name;
@NotNull
@Max(3)
private Integer type;
@NotNull(message = "parentId不能为null")
private Integer parentId;
@NotNull(message = "orderNum不能为null")
private Integer orderNum;
}
增加校验后即可拦截不规范的数据,我们捕捉注解抛出的异常就可以获取到提示信息
去之前的GloalExceptionHandler拦截异常类中编写异常过滤器
package com.learn2333.mall.exception;
//拦截异常
@ControllerAdvice
@Slf4j
public class GloalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(GloalExceptionHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public Object handleException(Exception e) {
logger.error("Default Exception:", e);
return ApiRestResponse.error(MallExceptionEnum.SYSTEM_ERROR);
}
@ExceptionHandler(MallException.class)
@ResponseBody
public Object handleImoocMallException(MallException e) {
logger.error("MallException:", e);
return ApiRestResponse.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiRestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
logger.error("MethodArgumentNotValidException:", e);
return handleBindingResult(e.getBindingResult());
}
private ApiRestResponse handleBindingResult(BindingResult result) {
//把异常处理为对外暴露的提示
List list = new ArrayList<>();
if (result.hasErrors()) {
List allErrors = result.getAllErrors();
// itli
for (ObjectError objectError : allErrors) {
String message = objectError.getDefaultMessage();
list.add(message);
}
}
if (list.size() == 0) {
return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_ERROR);
}
return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
}
异常枚举所有错误码
package com.learn2333.mall.exception;
public enum MallExceptionEnum {
NEED_USER_NAME(10001,"用户名不能为空"),
NEED_PASSWORD(10002,"密码不能为空"),
NEED_TOO_SHORT(10003,"密码长度不能小于8位"),
NAME_EXISTED(10004,"不允许重名"),
INSERT_FAILED(10005,"插入失败,请重试"),
WRONG_PASSWORD(10006,"登录密码错误,请重试"),
NEED_LOGIN(10007,"用户未登录,请登录"),
UPDATE_FAILD(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,"订单不存在"),
NO_YOUR_ORDER(10021,"订单不属于你"),
WRONG_ORDER_STATUS(10021,"订单不符"),
SYSTEM_ERROR(20000,"系统异常");
Integer code;
String msg;
MallExceptionEnum(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;
}
}
Swagger自动生成API文档
引入依赖
pom
开启io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2
入口类上增加@EnableSwagger2注解开启自动生成api文档
修改配置config包下修改配置文件
package com.learn2333.mall.config;
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;
@Configuration
public class SpringFoxConfig {
//访问http://localhost:8083/swagger-ui.html可以看到API文档
@Bean
public Docket api() {
return new Docket(documentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("生鲜网站")
.description("")
.termsOfServiceUrl("")
.build();
}
}
配置MVC Config Swagger地址映射
package com.learn2333.mall.config;
import com.learn2333.mall.common.Constant;
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 MallWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/images
public class AdminFilter implements Filter {
@Autowired
UserService userService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute(Constant.MALL_USER);
if (currentUser == null) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{n"
+ " "status": 10007,n"
+ " "msg": "NEED_LOGIN",n"
+ " "data": nulln"
+ "}");
out.flush();
out.close();
return;
}
//校验是否是管理员
boolean adminRole = userService.checkAdminRole(currentUser);
if (adminRole) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{n"
+ " "status": 10009,n"
+ " "msg": "NEED_ADMIN",n"
+ " "data": nulln"
+ "}");
out.flush();
out.close();
}
}
@Override
public void destroy() {
}
}
配置过滤器生效范围
这样,当请求路径带有以下范围时,过滤器拦截生效,进行上面的管理员身份校验
package com.learn2333.mall.config;
import com.learn2333.mall.filter.AdminFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AdminFilterConfig {
@Bean
public AdminFilter adminFilter() {
return new AdminFilter();
}
@Bean(name = "adminFilterConf")
public FilterRegistrationBean adminFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(adminFilter());
filterRegistrationBean.addUrlPatterns("/admin/category
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(connectionFactory);
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration = cacheConfiguration.entryTtl(Duration.ofSeconds(30)); //超时时间30s
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, cacheConfiguration);
return redisCacheManager;
}
}
需要缓存的实体类实现接口
要想让分类写入缓存还必须为对应的实体类实现Serializable接口
public class CategoryVO implements Serializable验证缓存生效
**方法一:**在30秒内调用的缓存方法都会返回之前查询的缓存内容;可以在需要缓存的内容方法中打断电测试,调用方法时没有执行方法而是直接从缓存中读取,速度显著提升
**方法二:**在终端使用keys查看redis中的内容
IDEA调试技巧 统一开关让所有断点失效
条件断点指定条件成立时生效
可以输入任意表达式查询内部的内容
总结知识点:参数校验、Swagger、统一鉴权、Redis整合、调试功能
开发常见问题:参数手动校验、项目不用缓存、不善用调试



