用户模块逻辑
用户自动登录逻辑,用户登录后产生一个loginToken,我们把它存在前台的Cookie里,并把user的信息以json形式存到redis服务器里去,loginToken作为key,下次再访问后台的时候,我们就去Cookie里读loginToken,然后通过loginToken去redis服务器读出user信息了。user信息存redis的时候我们会设一个超时时间,我在拦截器里进行过滤,判断到user不为空,则调用expire命令去刷新延长redis里loginToken对应的信息的有效期。
代码如下:
public class SessionExpireFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String loginToken = CookieUtil.readLoginToken(httpServletRequest);
if (StringUtils.isNotEmpty(loginToken)) {
//判断logintoken是否为空或者"";
//如果不为空的话,符合条件,继续拿user信息
String userJsonStr = RedisShardedPoolUtil.get(loginToken);
User user = JsonUtil.stringToObj(userJsonStr, User.class);
if (user != null) {
//如果user不为空,则调用expire命令
RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
用org.springframework.web.servlet.HandlerInterceptor去对一些接口进行拦截,判断有没有用户信息,没有的话就不会进入controller里的方法
@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle");
//请求中Controller中的方法名
HandlerMethod handlerMethod = (HandlerMethod) handler;
//解析HandlerMethod
String methodName = handlerMethod.getMethod().getName();
String className = handlerMethod.getBean().getClass().getSimpleName();//类名
//解析参数,具体的参数key以及value是什么,我们打印日志
StringBuffer requestParamBuffer = new StringBuffer();
Map paramMap = request.getParameterMap();
Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String mapKey = (String) entry.getKey();
String mapValue = "";
//request这个参数的map,里面的value返回的是一个String[]
Object obj = entry.getValue();
if (obj instanceof String[]) {
String[] strs = (String[]) obj;
mapValue = Arrays.toString(strs);
}
requestParamBuffer.append(mapKey).append("=").append(mapValue);
}
if (StringUtils.equals(className, "UserManageController") && StringUtils.equals(methodName, "login")) {
log.info("权限拦截器拦截到请求,className:{},methodName:{}", className, methodName);
//如果是拦截到登录请求,不打印参数,因为参数里面有密码,全部会打印到日志中,防止日志泄露
return true;
}
log.info("权限拦截器拦截到请求,className:{},methodName:{},param:{}", className, methodName, requestParamBuffer.toString());
User user = null;
String loginToken = CookieUtil.readLoginToken(request);
if (StringUtils.isNotEmpty(loginToken)) {
String userJsonStr = RedisShardedPoolUtil.get(loginToken);
user = JsonUtil.string2Obj(userJsonStr, User.class);
}
if (user == null || (user.getRole().intValue() != Const.Role.ROLE_ADMIN)) {
//返回false.即不会调用controller里的方法
response.reset();//note 这里要添加reset,否则报异常 getWriter() has already been called for this response.
response.setCharacterEncoding("UTF-8");//note 这里要设置编码,否则会乱码
response.setContentType("application/json;charset=UTF-8");//note 这里要设置返回值的类型,因为全部是json接口。
PrintWriter out = response.getWriter();
//上传由于富文本的控件要求,要特殊处理返回值,这里面区分是否登录以及是否有权限
if (user == null) {
if (StringUtils.equals(className, "ProductManageController") &&
StringUtils.equals(methodName, "richtextImgUpload")) {
Map resultMap = Maps.newHashMap();
resultMap.put("success", false);
resultMap.put("msg", "请登录管理员");
out.print(JsonUtil.obj2String(resultMap));
} else {
out.print(JsonUtil.obj2String(ServerResponse.createByErrorMessage("拦截器拦截,用户未登录")));
}
} else {
if (StringUtils.equals(className, "ProductManageController") &&
StringUtils.equals(methodName, "richtextImgUpload")) {
Map resultMap = Maps.newHashMap();
resultMap.put("success", false);
resultMap.put("msg", "无权限操作");
out.print(JsonUtil.obj2String(resultMap));
} else {
out.print(JsonUtil.obj2String(ServerResponse.createByErrorMessage("拦截器拦截,用户无权限操作")));
}
}
out.flush();
out.close();//geelynote 这里要关闭
return false;//不会再进入controller
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
log.info("afterCompletion");
}
}
定时关闭订单
Spring Schedule定时关单
Cron表达式的格式:秒 分 时 日 月 周 年(可选)
Spring Schedule Cron生成器,百度搜索下就有了
Spring Schedule Cron配置
在applicationContext.xml里加上
写一个CloseOrderTask类加上@Component注解,在需要定时的方法上加上@Scheduled(...)即可
MySQL行锁、表锁
Row-Level Lock(明确的主键)
Table-Level Lock (无明确的主键)
select ... for update 是悲观锁
购物车
今天来开始写一下关于购物车的东西, 这里首先抛出四个问题:
1)用户没登陆用户名和密码,添加商品, 关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗?
2)用户登陆了用户名密码,添加商品,关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗?
3)用户登陆了用户名密码,添加商品, 关闭浏览器,然后再打开,登陆用户名和密码 问:购物车商品还在吗?
4)用户登陆了用户名密码,添加商品, 关闭浏览器 外地老家打开浏览器 登陆用户名和密码 问:购物车商品还在吗?
答案
1)用户没有登录, 添加商品, 此时的商品是被添加到了浏览器的Cookie中, 所以当再次访问时(不登录),商品仍然在Cookie中, 所以购物车中的商品还是存在的.
2)用户登录了,添加商品, 此时会将Cookie中和用户选择的商品都添加到购物车中, 然后删除Cookie中的商品. 所以当用户再次访问(不登录),此时Cookie中的购物车商品已经被删除了, 所以此时购物车中的商品不在了.
3)用户登录, 添加商品,此时商品被添加到数据库做了持久化存储, 再次打开登录用户名和密码, 该用户选择的商品肯定还是存在的, 所以购物车中的商品还是存在的.
4)理由3)
这里再说下 没登录 保存商品到Cookie的优点以及保存到Session和数据库的对比:
1:Cookie: 优点: 保存用户浏览器(不用浪费我们公司的服务器) 缺点:Cookie禁用,不提供保存
2:Session:(Redis : 浪费大量服务器内存:实现、禁用Cookie) 速度很快
3:数据库(Mysql、Redis、SOlr) 能持久化的就数据库 速度太慢
那么我今天要讲的就是:
用户没登陆:购物车添加到Cookie中
用户登陆: 保存购物车到Redis中 (不用数据库)
添加购物车需要买家id、商品数量、颜色尺码不同对应不同的sku
先用买家id数据库查询BuyerCart,如果没有这个BuyerCart就创建个新的,有的话就去BuyerCart里判断是否包含同款,有的话就追加数量,没有再创建新的BuyerItem
public class BuyerCart implements Serializable{
2
3 /**
4 * 购物车
5 */
6 private static final long serialVersionUID = 1L;
7
8 //商品结果集
9 private List items = new ArrayList();
10
11 //添加购物项到购物车
12 public void addItem(BuyerItem item){
13 //判断是否包含同款
14 if (items.contains(item)) {
15 //追加数量
16 for (BuyerItem buyerItem : items) {
17 if (buyerItem.equals(item)) {
18 buyerItem.setAmount(item.getAmount() + buyerItem.getAmount());
19 }
20 }
21 }else {
22 items.add(item);
23 }
24
25 }
26
27 public List getItems() {
28 return items;
29 }
30
31 public void setItems(List items) {
32 this.items = items;
33 }
34
35
36 //小计
37 //商品数量
38 @JsonIgnore
39 public Integer getProductAmount(){
40 Integer result = 0;
41 //计算
42 for (BuyerItem buyerItem : items) {
43 result += buyerItem.getAmount();
44 }
45 return result;
46 }
47
48 //商品金额
49 @JsonIgnore
50 public Float getProductPrice(){
51 Float result = 0f;
52 //计算
53 for (BuyerItem buyerItem : items) {
54 result += buyerItem.getAmount()*buyerItem.getSku().getPrice();
55 }
56 return result;
57 }
58
59 //运费
60 @JsonIgnore
61 public Float getFee(){
62 Float result = 0f;
63 //计算
64 if (getProductPrice() < 79) {
65 result = 5f;
66 }
67
68 return result;
69 }
70
71 //总价
72 @JsonIgnore
73 public Float getTotalPrice(){
74 return getProductPrice() + getFee();
75 }
76
77 }
public class BuyerItem implements Serializable{
2
3 private static final long serialVersionUID = 1L;
4
5 //SKu对象
6 private Sku sku;
7
8 //是否有货
9 private Boolean isHave = true;
10
11 //购买的数量
12 private Integer amount = 1;
13
14 public Sku getSku() {
15 return sku;
16 }
17
18 public void setSku(Sku sku) {
19 this.sku = sku;
20 }
21
22 public Boolean getIsHave() {
23 return isHave;
24 }
25
26 public void setIsHave(Boolean isHave) {
27 this.isHave = isHave;
28 }
29
30 public Integer getAmount() {
31 return amount;
32 }
33
34 public void setAmount(Integer amount) {
35 this.amount = amount;
36 }
37
38 @Override
39 public int hashCode() {
40 final int prime = 31;
41 int result = 1;
42 result = prime * result + ((sku == null) ? 0 : sku.hashCode());
43 return result;
44 }
45
46 @Override
47 public boolean equals(Object obj) {
48 if (this == obj) //比较地址
49 return true;
50 if (obj == null)
51 return false;
52 if (getClass() != obj.getClass())
53 return false;
54 BuyerItem other = (BuyerItem) obj;
55 if (sku == null) {
56 if (other.sku != null)
57 return false;
58 } else if (!sku.getId().equals(other.sku.getId()))
59 return false;
60 return true;
61 }
62 }
购物车中商品必须有库存 且购买大于库存数量时视为无货. 提示: 购物车原页面不动. 有货改为无货, 加红提醒.
利用springmvc的过滤功能, 用户点击结算的时候必须要先登录, 如果没有登录的话就提示用户需要登录.