微服务实战——注册功能

注册

1.1. 配置

复制代码
@Configuration
public class GulimallConfig implements WebMvcConfigurer {
 
    /**
     * 视图映射
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        /**
         *     @GetMapping("/login.html")
         *     public String loginPage(){
         *         return "login";
         *     }
         */
        registry.addViewController("/login.html").setViewName("login");
        registry.addViewController("/reg.html").setViewName("reg");
    }
}

1.2. 整合短信服务

compoent

复制代码
package com.cwh.gulimall.thirdparty.component;

import com.aliyun.teaopenapi.models.Config;
import com.aliyun.dysmsapi20170525.Client;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;



@Slf4j
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data //为这些方法生成getter,setter
@Component
public class SmsClient {
//    private static String accessKeyId;
//    private static String accessKeySecret;
    public static Client createClient() throws Exception {
        String accessKeyId = "LTAI5tGW4xdUd5pnea6WHFZw";
        String accessKeySecret = "itwGI52mYtcveb6nA5ltU2ZUvTHLGy";
        Config config = new Config()
                // 配置 AccessKey ID,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(accessKeyId)
                // 配置 AccessKey Secret,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(accessKeySecret);

        // 配置 Endpoint
        config.endpoint = "dysmsapi.aliyuncs.com";

        return new Client(config);
    }
}

controller

复制代码
package com.cwh.gulimall.thirdparty.controller;


import com.cwh.gulimall.thirdparty.utils.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.cwh.gulimall.thirdparty.component.SmsClient;
import com.aliyun.dysmsapi20170525.Client;

import static com.aliyun.teautil.Common.toJSONString;

@RestController
@RequestMapping("/sms")
public class SmsSendController {

    Client client = SmsClient.createClient();

    public SmsSendController() throws Exception {
    }

    /**
     * 提供给其他服务调用
     * @param phone
     * @param code
     * @return
     */
    @GetMapping("/sendcode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) throws Exception {

        // 构造请求对象,请填入请求参数值
        SendSmsRequest sendSmsRequest = new SendSmsRequest()
                .setPhoneNumbers("15866638892")
                .setSignName("ColinCode商城")
                .setTemplateCode("SMS_474140261")
                .setTemplateParam("{\"code\":\"" + code + "\"}");

        System.out.println("验证码: " + code);

        // 获取响应对象
        SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
        System.out.println(toJSONString(sendSmsResponse));
        return R.ok();
    }
}

1.3. 业务接口实现

复制代码
@ResponseBody
    @GetMapping("/sms/sendcode")
    public R sendCode(@RequestParam("phone") String phone) throws Exception {

        // 1.接口防刷

        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (StringUtils.isNotEmpty(redisCode)) {
            // 60s内不能重发
            if ((System.currentTimeMillis() - Long.parseLong(redisCode.split("_")[1])) < 60000) {
                return R.error(10002, "验证码频率过高,稍后再试");
            }
        }
        // 2.验证码再次校验 redis:key:phone, value:code
        String code = String.valueOf(RandomUtils.nextInt(10000, 100000));
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, code, 10, TimeUnit.MINUTES);

        System.out.println("验证码:" + code);

        thirdPartFeignService.sendCode(phone, code);
        return R.ok();
    }

    /**
     * 重定向携带数据,利用Session原理,将数据放入session中,只要跳到下一个页面,取出数据后,session中的数据就会被删掉
     * // TODO 分布式下的session问题
     * RedirectAttributes redirectAttributes:模拟重定向携带数据
     *
     * @param vo
     * @param bindingResult
     * @param redirectAttributes
     * @return
     */
    @Transactional
    @PostMapping("/register")
    public String registry(@Valid UserRegistVo vo, BindingResult bindingResult, RedirectAttributes redirectAttributes) {

        if (bindingResult.hasErrors()) {
            Map<String, String> errors = bindingResult.getFieldErrors()
                    .stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
//            model.addAttribute("errors", errors);
            // 校验出错,转发到注册页
            redirectAttributes.addFlashAttribute("errors", errors);
            // Request method 'POST' not supported:只是Get请求能映射
            return "redirect:http://auth.gulimall.com/reg.html";
        }

        // 调用远程服务
        // 1.校验验证码
        String code = vo.getCode();
        String redisKey = AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone();
        String s = redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isNotEmpty(s) && code.equals(s.split("_")[0])) {
            // 删除验证码;令牌机制
            redisTemplate.delete(redisKey);
            // 验证码校验通过
            R r = memberFeignService.registry(vo);
            if (r.getCode() == 0) {
                // 注册成功回到登录页(重定向)
                return "redirect:http://auth.gulimall.com/login.html";
            } else {
                Map<String, String> errors = new HashMap<>();
                errors.put("msg", r.getData("msg", new TypeReference<String>() {
                }));
                redirectAttributes.addFlashAttribute("errors", errors);
                return "redirect:http://auth.gulimall.com/reg.html";
            }


        } else {
            Map<String, String> errors = new HashMap<>();
            errors.put("code", "验证码错误");
            // 校验出错,转发到注册页
            redirectAttributes.addFlashAttribute("errors", errors);
            // Request method 'POST' not supported:只是Get请求能映射
            return "redirect:http://auth.gulimall.com/reg.html";
        }

    }

    @PostMapping("/login")
    public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session){
        R r = memberFeignService.login(vo);
        if(r.getCode() == 0){
            MemberRespVo data = r.getData("data", new TypeReference<MemberRespVo>() {
            });
            session.setAttribute("loginUser", data);
            return "redirect:http://gulimall.com";
        }else {
            Map<String, String> errors = new HashMap<>();
            errors.put("msg", r.getData("msg", new TypeReference<String>(){}));
            redirectAttributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }

@Override
    public void registry(MemberRegistVo vo){

        MemberEntity memberEntity = new MemberEntity();
        // 检查用户名与手机号是否唯一,为了让controller感知异常,异常机制
        checkPhoneUnique(vo.getPhone());
        checkUserNameUnique(vo.getUserName());
        memberEntity.setUsername(vo.getUserName());
        memberEntity.setMobile(vo.getPhone());
        memberEntity.setNickname(vo.getUserName());
        // 设置默认等级
        MemberLevelEntity levelEntity = memberLevelService.getDefaultLevel();
        memberEntity.setLevelId(levelEntity.getId());
        // 密码加密
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(vo.getPassword());
        memberEntity.setPassword(encode);


        this.save(memberEntity);
    }

    @Override
    public void checkPhoneUnique(String phone) throws PhoneExistException {
        int count = this.count(new QueryWrapper<MemberEntity>().eq("mobile", phone));
        if(count == 1){
            throw new PhoneExistException();
        }
    }

    @Override
    public void checkUserNameUnique(String name) throws UserNameExistExcept {
        int count = this.count(new QueryWrapper<MemberEntity>().eq("username", name));
        if(count == 1){
            throw new PhoneExistException();
        }
    }
相关推荐
爱学习的小可爱卢2 分钟前
JavaEE进阶——SpringBoot统一功能处理实战指南
java·spring boot·java-ee
小单于PRO18 分钟前
Spring Boot 实现构建一个轻量级地图瓦片服务
java·spring boot·后端
西格电力科技20 分钟前
光伏四可“可观”功能:光伏电站全景数字化的底层支撑技术
大数据·人工智能·架构·能源
Selegant20 分钟前
Spring Boot 3 + Java 21 全新特性实战:虚拟线程、结构化并发与 Record 类型
java·spring boot·后端
木风小助理31 分钟前
Flink CDC:构建实时数据入湖架构的核心引擎
大数据·架构·flink
Jinkxs1 小时前
Java 架构 02:DDD 领域模型设计实战(限界上下文划分)
java·开发语言·架构
木易 士心1 小时前
MVC、MVP 与 MVVM:Android 架构演进之路
android·架构·mvc
小毅&Nora1 小时前
【后端】【诡秘架构】 序列7:魔术师 - API网关与协议转换的艺术:用Kong编织系统的幻象
架构·kong
GGBondlctrl1 小时前
【Redis】从单机架构到分布式,回溯架构的成长设计美学
分布式·缓存·架构·微服务架构·单机架构
百锦再1 小时前
国产数据库的平替亮点——关系型数据库架构适配
android·java·前端·数据库·sql·算法·数据库架构