谷粒商城—分布式高级②.md

[复制链接]
发表于 2025-10-28 10:35:06 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
认证服务

1. 环境搭建

创建gulimall-auth-server模块,导依赖,引入login.html和reg.html,并把静态资源放到nginx的static目次下
2. 注册功能

(1) 验证码倒计时


  1. //点击发送验证码按钮触发下面函数
  2. $("#sendCode").click(function () {
  3.    
  4.    
  5.                 //如果有disabled,说明最近已经点过,则什么都不做
  6.                 if($(this).hasClass("disabled")){
  7.    
  8.    
  9.                 }else {
  10.    
  11.    
  12.             //调用函数使得当前的文本进行倒计时功能
  13.                         timeOutChangeStyle();
  14.                         //发送验证码
  15.                         var phone=$("#phoneNum").val();
  16.                         $.get("/sms/sendCode?phone="+phone,function (data){
  17.    
  18.    
  19.                                 if (data.code!=0){
  20.    
  21.    
  22.                                         alert(data.msg);
  23.                                 }
  24.                         })
  25.                 }
  26.         })
  27.         let time = 60;
  28.         function timeOutChangeStyle() {
  29.    
  30.    
  31.                 //开启倒计时后设置标志属性disable,使得该按钮不能再次被点击
  32.                 $("#sendCode").attr("class", "disabled");
  33.         //当时间为0时,说明倒计时完成,则重置
  34.                 if(time==0){
  35.    
  36.    
  37.                         $("#sendCode").text("点击发送验证码");
  38.                         time=60;
  39.                         $("#sendCode").attr("class", "");
  40.                 }else {
  41.    
  42.    
  43.             //每秒调用一次当前函数,使得time--
  44.                         $("#sendCode").text(time+"s后再次发送");
  45.                         time--;
  46.                         setTimeout("timeOutChangeStyle()", 1000);
  47.                 }
  48.         }
复制代码
(2) 整合短佩服务

在阿里云网页购买试用的短佩服务
在gulimall-third-party中编写发送短信组件,此中host、path、appcode可以在设置文件中使用前缀spring.cloud.alicloud.sms举行设置
  1. @Data
  2. @ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
  3. @Controller
  4. public class SmsComponent {
  5.    
  6.    
  7.     private String host;
  8.     private String path;
  9.     private String appcode;
  10.     public void sendCode(String phone,String code) {
  11.    
  12.    
  13. //        String host = "http://dingxin.market.alicloudapi.com";
  14. //        String path = "/dx/sendSms";
  15.         String method = "POST";
  16. //        String appcode = "你自己的AppCode";
  17.         Map<String, String> headers = new HashMap<String, String>();
  18.         //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
  19.         headers.put("Authorization", "APPCODE " + appcode);
  20.         Map<String, String> querys = new HashMap<String, String>();
  21.         querys.put("mobile",phone);
  22.         querys.put("param", "code:"+code);
  23.         querys.put("tpl_id", "TP1711063");
  24.         Map<String, String> bodys = new HashMap<String, String>();
  25.         try {
  26.    
  27.    
  28.             /**
  29.              * 重要提示如下:
  30.              * HttpUtils请从
  31.              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
  32.              * 下载
  33.              *
  34.              * 相应的依赖请参照
  35.              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
  36.              */
  37.             HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
  38.             System.out.println(response.toString());
  39.             //获取response的body
  40.             //System.out.println(EntityUtils.toString(response.getEntity()));
  41.         } catch (Exception e) {
  42.    
  43.    
  44.             e.printStackTrace();
  45.         }
  46.     }
  47. }
复制代码
编写controller,给别的服务提供远程调用发送验证码的接口
  1. @Controller
  2. @RequestMapping(value = "/sms")
  3. public class SmsSendController {
  4.    
  5.    
  6.     @Resource
  7.     private SmsComponent smsComponent;
  8.     /**
  9.      * 提供给别的服务进行调用
  10.      * @param phone 电话号码
  11.      * @param code 验证码
  12.      * @return
  13.      */
  14.     @ResponseBody
  15.     @GetMapping(value = "/sendCode")
  16.     public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {
  17.    
  18.    
  19.         //发送验证码
  20.         smsComponent.sendCode(phone,code);
  21.         System.out.println(phone+code);
  22.         return R.ok();
  23.     }
  24. }
复制代码
(3) 接口防刷

由于发送验证码的接口袒露,为了防止恶意攻击,我们不能随意让接口被调用。

      
  • 在redis中以phone-code将电话号码和验证码举行存储并将当前时间与code一起存储
         
    • 如果调用时以当前phone取出的v不为空且当前时间在存储时间的60s以内,分析60s内该号码已经调用过,返回错误信息   
    • 60s以后再次调用,必要删除之前存储的phone-code   
    • code存在一个逾期时间,我们设置为10min,10min内验证该验证码有效   
      
  1. @GetMapping("/sms/sendCode")
  2. @ResponseBody
  3. public R sendCode(@RequestParam("phone")String phone) {
  4.    
  5.    
  6.    //接口防刷,在redis中缓存phone-code
  7.     ValueOperations<String, String> ops = redisTemplate.opsForValue();
  8.     String prePhone = AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone;
  9.     String v = ops.get(prePhone);
  10.     if (!StringUtils.isEmpty(v)) {
  11.    
  12.    
  13.         long pre = Long.parseLong(v.split("_")[1]);
  14.         //如果存储的时间小于60s,说明60s内发送过验证码
  15.         if (System.currentTimeMillis() - pre < 60000) {
  16.    
  17.    
  18.             return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
  19.         }
  20.     }
  21.     //如果存在的话,删除之前的验证码
  22.     redisTemplate.delete(prePhone);
  23.     //获取到6位数字的验证码
  24.     String code = String.valueOf((int)((Math.random() + 1) * 100000));
  25.     //在redis中进行存储并设置过期时间
  26.     ops.set(prePhone,code+"_"+System.currentTimeMillis(),10, TimeUnit.MINUTES);
  27.     thirdPartFeignService.sendCode(phone, code);
  28.     return R.ok();
  29. }
复制代码
(4) 注册接口编写

在gulimall-auth-server服务中编写注册的主体逻辑

      
  • 若JSR303校验未通过,则通过BindingResult封装错误信息,并重定向至注册页面  
  • 若通过JSR303校验,则必要从redis中取值判断验证码是否准确,准确的话通过会员服务注册  
  • 会员服务调用乐成则重定向至登录页,否则封装远程服务返回的错误信息返回至注册页面
注: RedirectAttributes可以通过session生存信息并在重定向的时间携带已往
  1. @PostMapping("/register")
  2.     public String register(@Valid UserRegisterVo registerVo, BindingResult result, RedirectAttributes attributes) {
  3.    
  4.    
  5.         //1.判断校验是否通过
  6.         Map<String, String> errors = new HashMap<>();
  7.         if (result.hasErrors()){
  8.    
  9.    
  10.             //1.1 如果校验不通过,则封装校验结果
  11.             result.getFieldErrors().forEach(item->{
  12.    
  13.    
  14.                 errors.put(item.getField(), item.getDefaultMessage());
  15.                 //1.2 将错误信息封装到session中
  16.                 attributes.addFlashAttribute("errors", errors);
  17.             });
  18.             //1.2 重定向到注册页
  19.             return "redirect:http://auth.gulimall.com/reg.html";
  20.         }else {
  21.    
  22.    
  23.             //2.若JSR303校验通过
  24.             //判断验证码是否正确
  25.             String code = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());
  26.             //2.1 如果对应手机的验证码不为空且与提交上的相等-》验证码正确
  27.             if (!StringUtils.isEmpty(code) && registerVo.getCode().equals(code.split("_")[0])) {
  28.    
  29.    
  30.                 //2.1.1 使得验证后的验证码失效
  31.                 redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());
  32.                 //2.1.2 远程调用会员服务注册
  33.                 R r = memberFeignService.register(registerVo);
  34.                 if (r.getCode() == 0) {
  35.    
  36.    
  37.                     //调用成功,重定向登录页
  38.                     return "redirect:http://auth.gulimall.com/login.html";
  39.                 }else {
  40.    
  41.    
  42.                     //调用失败,返回注册页并显示错误信息
  43.                     String msg = (String) r.get("msg");
  44.                     errors.put("msg", msg);
  45.                     attributes.addFlashAttribute("errors", errors);
  46.                     return "redirect:http://auth.gulimall.com/reg.html";
  47.                 }
  48.             }else {
  49.    
  50.    
  51.                 //2.2 验证码错误
  52.                 errors.put("code", "验证码错误");
  53.                 attributes.addFlashAttribute("errors", errors);
  54.                 return "redirect:http://auth.gulimall.com/reg.html";
  55.             }
  56.         }
  57.     }
复制代码
通过gulimall-member会员服务注册逻辑

      
  • 通过非常机制判断当前注册会员名和电话号码是否已经注册,如果已经注册,则抛出对应的自界说非常,并在返回时封装对应的错误信息  
  • 如果没有注册,则封装通报过来的会员信息,并设置默认的会员品级、创建时间
  1. @RequestMapping("/register")
  2.     public R register(@RequestBody MemberRegisterVo registerVo) {
  3.    
  4.    
  5.         try {
  6.    
  7.    
  8.             memberService.register(registerVo);
  9.             //异常机制:通过捕获对应的自定义异常判断出现何种错误并封装错误信息
  10.         } catch (UserExistException userException) {
  11.    
  12.    
  13.             return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(), BizCodeEnum.USER_EXIST_EXCEPTION.getMsg());
  14.         } catch (PhoneNumExistException phoneException) {
  15.    
  16.    
  17.             return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg());
  18.         }
  19.         return R.ok();
  20.     }
复制代码
  1. public void register(MemberRegisterVo registerVo) {
  2.    
  3.    
  4.     //1 检查电话号是否唯一
  5.     checkPhoneUnique(registerVo.getPhone());
  6.     //2 检查用户名是否唯一
  7.     checkUserNameUnique(registerVo.getUserName());
  8.     //3 该用户信息唯一,进行插入
  9.     MemberEntity entity = new MemberEntity();
  10.     //3.1 保存基本信息
  11.     entity.setUsername(registerVo.getUserName());
  12.     entity.setMobile(registerVo.getPhone());
  13.     entity.setCreateTime(new Date());
  14.     //3.2 使用加密保存密码
  15.     BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
  16.     String encodePassword = passwordEncoder.encode(registerVo.getPassword());
  17.     entity.setPassword(encodePassword);
  18.     //3.3 设置会员默认等级
  19.     //3.3.1 找到会员默认登记
  20.     MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));
  21.     //3.3.2 设置会员等级为默认
  22.     entity.setLevelId(defaultLevel.getId());
  23.     // 4 保存用户信息
  24.     this.save(entity);
  25. }
  26. private void checkUserNameUnique(String userName) {
  27.    
  28.    
  29.     Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", userName));
  30.     if (count > 0) {
  31.    
  32.    
  33.         throw new UserExistException();
  34.     }
  35. }
  36. private void checkPhoneUnique(String phone) {
  37.    
  38.    
  39.     Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
  40.     if (count > 0) {
  41.    
  42.    
  43.         throw new PhoneNumExistException();
  44.     }
  45. }
复制代码
3. 用户名暗码登录

在gulimall-auth-server模块中的主体逻辑

      
  • 通过会员服务远程调用登录接口
         
    • 如果调用乐成,重定向至首页   
    • 如果调用失败,则封装错误信息并携带错误信息重定向至登录页   
      
  1. @RequestMapping("/login")
  2. public String login(UserLoginVo vo,RedirectAttributes attributes){
  3.    
  4.    
  5.     R r = memberFeignService.login(vo);
  6.     if (r.getCode() == 0) {
  7.    
  8.    
  9.         return "redirect:http://gulimall.com/";
  10.     }else {
  11.    
  12.    
  13.         String msg = (String) r.get("msg");
  14.         Map<String, String> errors = new HashMap<>();
  15.         errors.put("msg", msg);
  16.         attributes.addFlashAttribute("errors", errors);
  17.         return "redirect:http://auth.gulimall.com/login.html";
  18.     }
  19. }
复制代码
在gulimall-member模块中完成登录

      
  • 当数据库中含有以当前登录名为用户名或电话号且暗码匹配时,验证通过,返回查询到的实体  
  • 否则返回null,并在controller返回用户名或暗码错误
  1. @RequestMapping("/login")
  2. public R login(@RequestBody MemberLoginVo loginVo) {
  3.    
  4.    
  5.     MemberEntity entity=memberService.login(loginVo);
  6.     if (entity!=null){
  7.    
  8.    
  9.         return R.ok();
  10.     }else {
  11.    
  12.    
  13.         return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(), BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMsg());
  14.     }
  15. }
  16.         @Override
  17.     public MemberEntity login(MemberLoginVo loginVo) {
  18.    
  19.    
  20.         String loginAccount = loginVo.getLoginAccount();
  21.         //以用户名或电话号登录的进行查询
  22.         MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", loginAccount).or().eq("mobile", loginAccount));
  23.         if (entity!=null){
  24.    
  25.    
  26.             BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
  27.             boolean matches = bCryptPasswordEncoder.matches(loginVo.getPassword(), entity.getPassword());
  28.             if (matches){
  29.    
  30.    
  31.                 entity.setPassword("");
  32.                 return entity;
  33.             }
  34.         }
  35.         return null;
  36.     }
复制代码
4. 外交登录

(1) oauth2.0


(2) 在微博开放平台创建应用


(3) 在登录页引导用户至授权页

  1. GET
  2. https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
复制代码

      
  • client_id: 创建网站应用时的app key  
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(必要清静台高级设置划一)

如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE
code是我们用来调换令牌的参数
(4) 调换token

  1. POST
  2. https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
复制代码

      
  • client_id: 创建网站应用时的app key  
  • client_secret: 创建网站应用时的app secret  
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(必要清静台高级设置划一)  
  • code:调换令牌的认证码
返回数据如下

(5) 获取用户信息


结果返回json
(6) 代码编写

认证接口

      
  • 通过HttpUtils发送哀求获取token,并将token等信息交给member服务举行外交登录  
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页
  1. @Controller
  2. public class OauthController {
  3.    
  4.    
  5.     @Autowired
  6.     private MemberFeignService memberFeignService;
  7.     @RequestMapping("/oauth2.0/weibo/success")
  8.     public String authorize(String code, RedirectAttributes attributes) throws Exception {
  9.    
  10.    
  11.         //1. 使用code换取token,换取成功则继续2,否则重定向至登录页
  12.         Map<String, String> query = new HashMap<>();
  13.         query.put("client_id", "2144***074");
  14.         query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");
  15.         query.put("grant_type", "authorization_code");
  16.         query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");
  17.         query.put("code", code);
  18.         //发送post请求换取token
  19.         HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());
  20.         Map<String, String> errors = new HashMap<>();
  21.         if (response.getStatusLine().getStatusCode() == 200) {
  22.    
  23.    
  24.             //2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页
  25.             String json = EntityUtils.toString(response.getEntity());
  26.             SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {
  27.    
  28.    
  29.             });
  30.             R login = memberFeignService.login(socialUser);
  31.             //2.1 远程调用成功,返回首页并携带用户信息
  32.             if (login.getCode() == 0) {
  33.    
  34.    
  35.                 String jsonString = JSON.toJSONString(login.get("memberEntity"));
  36.                 MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {
  37.    
  38.    
  39.                 });
  40.                 attributes.addFlashAttribute("user", memberResponseVo);
  41.                 return "redirect:http://gulimall.com";
  42.             }else {
  43.    
  44.    
  45.                 //2.2 否则返回登录页
  46.                 errors.put("msg", "登录失败,请重试");
  47.                 attributes.addFlashAttribute("errors", errors);
  48.                 return "redirect:http://auth.gulimall.com/login.html";
  49.             }
  50.         }else {
  51.    
  52.    
  53.             errors.put("msg", "获得第三方授权失败,请重试");
  54.             attributes.addFlashAttribute("errors", errors);
  55.             return "redirect:http://auth.gulimall.com/login.html";
  56.         }
  57.     }
复制代码
登录接口

      
  • 登录包罗两种流程,实际上包罗了注册和登录  
  • 如果之前未使用该外交账号登录,则使用token调用开放api获取外交账号相干信息,注册并将结果返回  
  • 如果之前已经使用该外交账号登录,则更新token并将结果返回
  1. @RequestMapping("/oauth2/login")
  2. public R login(@RequestBody SocialUser socialUser) {
  3.    
  4.    
  5.     MemberEntity entity=memberService.login(socialUser);
  6.     if (entity!=null){
  7.    
  8.    
  9.         return R.ok().put("memberEntity",entity);
  10.     }else {
  11.    
  12.    
  13.         return R.error();
  14.     }
  15. }
  16. @Override
  17.     public MemberEntity login(SocialUser socialUser){
  18.    
  19.    
  20.         MemberEntity uid = this.getOne(new QueryWrapper<MemberEntity>().eq("uid", socialUser.getUid()));
  21.         //1 如果之前未登陆过,则查询其社交信息进行注册
  22.         if (uid == null) {
  23.    
  24.    
  25.             Map<String, String> query = new HashMap<>();
  26.             query.put("access_token",socialUser.getAccess_token());
  27.             query.put("uid", socialUser.getUid());
  28.             //调用微博api接口获取用户信息
  29.             String json = null;
  30.             try {
  31.    
  32.    
  33.                 HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);
  34.                 json = EntityUtils.toString(response.getEntity());
  35.             } catch (Exception e) {
  36.    
  37.    
  38.                 e.printStackTrace();
  39.             }
  40.             JSONObject jsonObject = JSON.parseObject(json);
  41.             //获得昵称,性别,头像
  42.             String name = jsonObject.getString("name");
  43.             String gender = jsonObject.getString("gender");
  44.             String profile_image_url = jsonObject.getString("profile_image_url");
  45.             //封装用户信息并保存
  46.             uid = new MemberEntity();
  47.             MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));
  48.             uid.setLevelId(defaultLevel.getId());
  49.             uid.setNickname(name);
  50.             uid.setGender("m".equals(gender)?0:1);
  51.             uid.setHeader(profile_image_url);
  52.             uid.setAccessToken(socialUser.getAccess_token());
  53.             uid.setUid(socialUser.getUid());
  54.             uid.setExpiresIn(socialUser.getExpires_in());
  55.             this.save(uid);
  56.         }else {
  57.    
  58.    
  59.             //2 否则更新令牌等信息并返回
  60.             uid.setAccessToken(socialUser.getAccess_token());
  61.             uid.setUid(socialUser.getUid());
  62.             uid.setExpiresIn(socialUser.getExpires_in());
  63.             this.updateById(uid);
  64.         }
  65.         return uid;
  66.     }
复制代码
5. SpringSession

(1) session 原理

jsessionid相称于银行卡,存在服务器的session相称于存储的现金,每次通过jsessionid取出生存的数据
标题:但是正常环境下session不可跨域,它有自己的作用范围

(2) 分布式下session共享标题


(3) 办理方案

1) session复制


2) 客户端存储


3) hash划一性


4) 同一存储


(4) SpringSession整合redis

通过SpringSession修改session的作用域

1) 环境搭建

导入依赖
  1.     <dependency>
  2.         <groupId>org.springframework.session</groupId>
  3.         <artifactId>spring-session-data-redis</artifactId>
  4.     </dependency>
  5.     <dependency>
  6.         <groupId>org.springframework.boot</groupId>
  7.         <artifactId>spring-boot-starter-data-redis</artifactId>
  8.     </dependency>
复制代码
修改设置
  1. spring:
  2.   redis:
  3.     host: 192.168.56.102
  4.   session:
  5.     store-type: redis
复制代码
添加注解
  1. @EnableRedisHttpSession
  2. public class GulimallAuthServerApplication {
  3.    
  4.    
复制代码
2) 自界说设置


      
  • 由于默认使用jdk举行序列化,通过导入RedisSerializer修改为json序列化
      
  • 而且通过修改CookieSerializer扩大session的作用域至**.gulimall.com

  1. @Configuration
  2. public class GulimallSessionConfig {
  3.    
  4.    
  5.     @Bean
  6.     public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
  7.    
  8.    
  9.         return new GenericJackson2JsonRedisSerializer();
  10.     }
  11.     @Bean
  12.     public CookieSerializer cookieSerializer() {
  13.    
  14.    
  15.         DefaultCookieSerializer serializer = new DefaultCookieSerializer();
  16.         serializer.setCookieName("GULISESSIONID");
  17.         serializer.setDomainName("gulimall.com");
  18.         return serializer;
  19.     }
  20. }
复制代码
(5) SpringSession焦点原理 - 装饰者模式


      
  • 原生的获取session时是通过HttpServletRequest获取的  
  • 这里对request举行包装,而且重写了包装request的getSession()方法
  1. @Override
  2. protected void doFilterInternal(HttpServletRequest request,
  3.       HttpServletResponse response, FilterChain filterChain)
  4.       throws ServletException, IOException {
  5.    
  6.    
  7.    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
  8.     //对原生的request、response进行包装
  9.    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
  10.          request, response, this.servletContext);
  11.    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
  12.          wrappedRequest, response);
  13.    try {
  14.    
  15.    
  16.       filterChain.doFilter(wrappedRequest, wrappedResponse);
  17.    }
  18.    finally {
  19.    
  20.    
  21.       wrappedRequest.commitSession();
  22.    }
  23. }
复制代码
购物车

1. 数据模子分析

(1) 数据存储

购物车是一个读多写多的场景,因此放入数据库并不符合,但购物车又是必要恒久化,因此这里我们选用redis存储购物车数据。
(2) 数据布局


一个购物车是由各个购物项构成的,但是我们用List举行存储并不符合,由于使用List查找某个购物项时必要挨个遍历每个购物项,会造成大量时间斲丧,为包管查找速率,我们使用hash举行存储

(3) VO编写

购物项vo

  1. public class CartItemVo {
  2.    
  3.    
  4.     private Long skuId;
  5.        
  6.     //是否选中
  7.     private Boolean check = true;
  8.     //标题
  9.     private String title;
  10.        
  11.     //图片
  12.     private String image;
  13.     //商品套餐属性
  14.     private List<String> skuAttrValues;
  15.     //价格
  16.     private BigDecimal price;
  17.     //数量
  18.     private Integer count;
  19.     //总价
  20.     private BigDecimal totalPrice;
  21.    
  22.       /**
  23.      * 当前购物车项总价等于单价x数量
  24.      * @return
  25.      */
  26.     public BigDecimal getTotalPrice() {
  27.    
  28.    
  29.         return price.multiply(new BigDecimal(count));
  30.     }
  31.     public void setTotalPrice(BigDecimal totalPrice) {
  32.    
  33.    
  34.         this.totalPrice = totalPrice;
  35.     }
复制代码
购物车vo

  1. public class CartVo {
  2.    
  3.    
  4.    
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表