赞
踩
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = { IsMobileValidator.class } ) public @interface IsMobile { //该字段必填 boolean required() default true; String message() default "手机号码格式错误"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
package com.example.seckilldemo.validator; import com.example.seckilldemo.utils.ValidatorUtil; import org.thymeleaf.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 手机号码校验规则 * * @author: LC * @date 2022/3/2 3:08 下午 * @ClassName: IsMobileValidator */ public class IsMobileValidator implements ConstraintValidator<IsMobile, String> { private boolean required = false; @Override public void initialize(IsMobile constraintAnnotation) { // ConstraintValidator.super.initialize(constraintAnnotation); required = constraintAnnotation.required(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { if (required) { return ValidatorUtil.isMobile(s); } else { if (StringUtils.isEmpty(s)) { return true; } else { return ValidatorUtil.isMobile(s); } } } }
package com.example.seckilldemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; /** * MVC配置类 * * @author: LC * @date 2022/3/3 2:37 下午 * @ClassName: WebConfig */ @Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Autowired private UserArgumentResolver userArgumentResolver; @Autowired private AccessLimitInterceptor accessLimitInterceptor; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { // WebMvcConfigurer.super.addArgumentResolvers(resolvers); resolvers.add(userArgumentResolver); } //静态资源展示 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); //swagger 和 knife4j registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessLimitInterceptor); } }
package com.example.seckilldemo.config; import com.example.seckilldemo.entity.TUser; import com.example.seckilldemo.service.ITUserService; import com.example.seckilldemo.utils.CookieUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.thymeleaf.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 自定义用户参数 * * @author: LC * @date 2022/3/3 4:46 下午 * @ClassName: UserArgumentResolver */ @Component public class UserArgumentResolver implements HandlerMethodArgumentResolver { @Autowired private ITUserService itUserService; @Override public boolean supportsParameter(MethodParameter parameter) { Class<?> parameterType = parameter.getParameterType(); return parameterType == TUser.class; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return UserContext.getUser(); // HttpServletRequest nativeRequest = webRequest.getNativeRequest(HttpServletRequest.class); // HttpServletResponse nativeResponse = webRequest.getNativeResponse(HttpServletResponse.class); // String userTicket = CookieUtil.getCookieValue(nativeRequest, "userTicket"); // if (StringUtils.isEmpty(userTicket)) { // return null; // } // return itUserService.getUserByCookie(userTicket, nativeRequest, nativeResponse); } }
处理controller抛出的异常。
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public RespBean ExceptionHandler(Exception e) { if (e instanceof GlobalException) { GlobalException exception = (GlobalException) e; return RespBean.error(exception.getRespBeanEnum()); } else if (e instanceof BindException) { BindException bindException = (BindException) e; RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR); respBean.setMessage("参数校验异常:" + bindException.getBindingResult().getAllErrors().get(0).getDefaultMessage()); return respBean; } System.out.println("异常信息" + e); return RespBean.error(RespBeanEnum.ERROR); } }
boolean seckillGoodsResult = itSeckillGoodsService.update(new UpdateWrapper<TSeckillGoods>()
.setSql("stock_count = " + "stock_count-1")
.eq("goods_id", goodsVo.getId())
.gt("stock_count", 0)
);
用户进行登录输入用户名和密码,在密码在前端用固定salt加密,传到后端,后端对密码,用户名进行验证。验证通过,为用户生成cookie信息,cookie返回,并且将用户信息存储到redis中。
登陆成功,到秒杀商品列表页面,选择秒杀的商品,进入到商品详情页面,点击秒杀商品进行秒杀。
正在秒杀显示正在进行中
秒杀成功进入到商品订单详情页
商品详情页
captcha+userID+goodsID
HashMap goodsStockMap = new HashMap<Long, Boolean>();
private void render(HttpServletResponse response, RespBeanEnum respBeanEnum) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter printWriter = response.getWriter();
RespBean bean = RespBean.error(respBeanEnum);
printWriter.write(new ObjectMapper().writeValueAsString(bean));
printWriter.flush();
printWriter.close();
}
前端传递的数据可能会被修改
<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>
生成5000用户,将用户的cookie存入redis。
不使用mq,不使用内存标记,不使用redis预减库存
不使用mq,不使用内存标记,使用redis预减库存
不使用mq,使用内存标记,使用redis预减库存
将页面渲染好后缓存到redis中,设置过期时间T,这样用户得到的就是T内的页面。这样在T时间内不用每次都渲染页面,减少了时间。T不能太长,也不能太短。
和页面缓存差不多,只不过url会有动态参数,所以缓存的多一点。根据不同动态参数进行缓存。
后端只需传递数据到前端,此时后端不需要返回整个html页面了。
最大qps的70%~80%
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
int second();
int maxCount();
boolean needLogin() default true;
}
在redis中初始化生成2份相同的商品信息会导致商品少卖。但是库存依然到0???
DELETE FROM t_order WHERE goods_id = 1;
DELETE FROM `t_seckill_order` WHERE goods_id = 1;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。