小熊家务帮day5-day7 客户管理模块1 (小程序认证,手机验证码认证,账号密码认证,修改密码,找回密码等)

客户管理模块

  • 1.认证模块
    • 1.1 认证方式介绍
      • 1.1.1 小程序认证
      • 1.1.2 手机验证码登录
      • 1.1.3 账号密码认证
    • 1.2 小程序认证
      • 1.2.1 小程序申请
      • 1.2.2 创建客户后端工程jzo2o-customer
      • 1.2.3 开发部署前端
      • 1.2.4 小程序认证流程
        • 1.2.4.1 customer小程序认证接口设计
          • Controller层
          • Service层
          • 调用Publics模块获取openid
          • 查询数据库构建token
          • 保存token到微服务的ThreadLocal中
      • 1.2.5 手机验证码认证
        • 1.2.5.1 开发与部署app前端
        • 1.2.5.2 customer短信认证接口设计
          • 需求分析
          • Controller层
          • Service层
      • 1.2.6 账号密码验证
        • 1.2.6.1 前端设计和部署
        • 1.2.6.2 customer账号密码验证接口设计
          • Controller设计
          • Service层设计
          • 顺便复习下BCrypt加密
      • 1.2.7 注册功能实现
        • 1.2.7.1 需求
        • 1.2.7.2 Costomer注册功能接口实现
          • Controller开发
          • Service开发
          • 演示
      • 1.2.8 找回密码功能
        • 1.2.8.1 需求
        • 1.2.8.2 Costomer找回密码功能接口实现
          • Controller开发
          • Service开发
          • 演示

1.认证模块

一般情况有用户交互的项目都有认证授权功能,首先要搞清楚两个概念:认证和授权。
认证: 就是校验用户的身份是否合法,常见的认证方式有账号密码登录、手机验证码登录等。
授权:则是该用户登录系统成功后当用户去点击菜单或操作数据时系统判断该用户是否有权限,有权限则允许继续操作,没有权限则拒绝访问。

本项目包括四个端:用户端(小程序)、服务端(app)、机构端(PC)、运营管理端(PC).
分别对应四类用户角色:家政需求方即c端用户,家政服务人员、家政服务公司(机构)、平台运营人员。

1.1 认证方式介绍

1.1.1 小程序认证

用户端通过小程序使用平台,初次使用小程序会进行认证,具体流程如下:
在这里插入图片描述

1.1.2 手机验证码登录

服务人员通过app登录采用手机验证码认证方式,输入手机号、发送验证码,验证码校验通过则认证通过,初次认证通过将自动注册服务人员信息。
在这里插入图片描述

1.1.3 账号密码认证

机构端认证方式是账号密码认证方式,通过pc浏览器进入登录界面输入账号和密码登录系统
机构端提供单独的注册页面,输入手机号,接收验证码进行注册

1.2 小程序认证

下边测试用户端小程序的认证流程,先参考微信官方提供的小程序登录流程先大概知道小程序认证流程需要几部分,如下图:
从图上可以看出小程序认证流程需要三部分:
小程序:即前端程序
开发者服务器:后端微服务程序。
微信接口服务:即微信服务器。
在这里插入图片描述
1.前端调用wx.login()获取登录凭证code
2.前端请求后端进行认证,发送code
3.后端请求微信获取openid,发送appid、app密钥、code参数,微信返回openid
4.后端生成认证成功凭证返回给前端。
5.前端存储用户认证成功凭证

1.2.1 小程序申请

开发小程序首先要申请小程序账号,参考官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/getstart.html#%E7%94%B3%E8%AF%B7%E8%B4%A6%E5%8F%B7
通过阅读上述文档并且完成操作,本人已经完成小程序注册并保存好了密匙

1.2.2 创建客户后端工程jzo2o-customer

此处不展开了
具体就是新建工程,同步Gitee,修改nacos配置文件的小程序认证部分等等

1.2.3 开发部署前端

这部分也不再展开
在这里插入图片描述

1.2.4 小程序认证流程

整个过程包括三部分:
小程序:即前端程序
开发者服务器:后端微服务程序。
微信接口服务:即微信服务器。

具体的流程如下:
1.前端调用wx.login()获取登录凭证code
2.前端请求后端进行认证,发送code
3.后端请求微信获取openid
4.后端生成认证成功凭证返回给前端。
在这里插入图片描述
根据官方的认证流程我们定义本项目小程序认证的交互流程:
customer工程提供认证接口,publics工程作为一个公共服务提供与微信通信的接口。(抽取通用服务)
前端与cutomer交互不与publics交互。
在这里插入图片描述

1.2.4.1 customer小程序认证接口设计

首先认证的前端请求如下:
在这里插入图片描述

Controller层
@RestController("openLoginController")
@RequestMapping("/open/login")
@Api(tags = "白名单接口 - 客户登录相关接口")
public class LoginController {@Resourceprivate ILoginService loginService;@PostMapping("/worker")@ApiOperation("服务人员/机构人员登录接口")public LoginResDTO loginForWorker(@RequestBody LoginForWorkReqDTO loginForWorkReqDTO) {if(UserType.INSTITUTION == loginForWorkReqDTO.getUserType()){return loginService.loginForPassword(loginForWorkReqDTO);}else{return loginService.loginForVerify(loginForWorkReqDTO);}}/*** c端用户登录接口*/@PostMapping("/common/user")@ApiOperation("c端用户登录接口")public LoginResDTO loginForCommonUser(@RequestBody LoginForCustomerReqDTO loginForCustomerReqDTO) {return loginService.loginForCommonUser(loginForCustomerReqDTO);}}
Service层
    @Overridepublic LoginResDTO loginForCommonUser(LoginForCustomerReqDTO loginForCustomerReqDTO) {// code换openIdOpenIdResDTO openIdResDTO = wechatApi.getOpenId(loginForCustomerReqDTO.getCode());if(ObjectUtil.isEmpty(openIdResDTO) || ObjectUtil.isEmpty(openIdResDTO.getOpenId())){// openid申请失败throw new CommonException(ErrorInfo.Code.LOGIN_TIMEOUT, ErrorInfo.Msg.REQUEST_FAILD);}CommonUser commonUser = commonUserService.findByOpenId(openIdResDTO.getOpenId());//如果未从数据库查到,需要新增数据if (ObjectUtil.isEmpty(commonUser)) {commonUser = BeanUtil.toBean(loginForCustomerReqDTO, CommonUser.class);long snowflakeNextId = IdUtil.getSnowflakeNextId();commonUser.setId(snowflakeNextId);commonUser.setOpenId(openIdResDTO.getOpenId());commonUser.setNickname("普通用户"+ RandomUtil.randomInt(10000,99999));commonUserService.save(commonUser);}else if(CoCummonStatusConstants.USER_STATUS_FREEZE == commonUser.getStatus()) {// 被冻结throw new CommonException(ErrorInfo.Code.ACCOUNT_FREEZED, commonUser.getAccountLockReason());}//构建tokenString token = jwtTool.createToken(commonUser.getId(), commonUser.getNickname(), commonUser.getAvatar(), UserType.C_USER);return new LoginResDTO(token);}
调用Publics模块获取openid

其中customer模块的OpenIdResDTO openIdResDTO = wechatApi.getOpenId(loginForCustomerReqDTO.getCode());代码就是调用publics模块的接口从而获取openid:

    @Resourceprivate WechatService wechatService;@Override@GetMapping("/getOpenId")@ApiOperation("获取openId")@ApiImplicitParams({@ApiImplicitParam(name = "code", value = "登录凭证", required = true, dataTypeClass = String.class)})public OpenIdResDTO getOpenId(@RequestParam("code") String code) {String openId = wechatService.getOpenid(code);return new OpenIdResDTO(openId);}

而在其中调用了第三方模块的wechatService.getOpenid接口,其中内容如下:

    public String getOpenid(String code) {Map<String, Object> requestUrlParam = this.getAppConfig();requestUrlParam.put("js_code", code);String result = HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code", requestUrlParam);log.info("getOpenid result:{}", result);JSONObject jsonObject = JSONUtil.parseObj(result);if (ObjectUtil.isNotEmpty(jsonObject.getInt("errcode"))) {throw new ServerErrorException(jsonObject.getStr("errmsg"));} else {return jsonObject.getStr("openid");}}

功能很简单,通过调用微信官方api获取到数据,从数据中解析出openid返回publics模块,最后返回到customer模块

查询数据库构建token

customer模块拿到openid后开始查询数据库:

CommonUser commonUser = commonUserService.findByOpenId(openIdResDTO.getOpenId());

在这里插入图片描述
假如数据库没查到,说明第一次使用,那么就会自动注册:

        if (ObjectUtil.isEmpty(commonUser)) {commonUser = BeanUtil.toBean(loginForCustomerReqDTO, CommonUser.class);long snowflakeNextId = IdUtil.getSnowflakeNextId();commonUser.setId(snowflakeNextId);commonUser.setOpenId(openIdResDTO.getOpenId());commonUser.setNickname("普通用户"+ RandomUtil.randomInt(10000,99999));commonUserService.save(commonUser);}

那么如果查到了,说明注册过了,就会生成token:

        //构建tokenString token = jwtTool.createToken(commonUser.getId(), commonUser.getNickname(), commonUser.getAvatar(), UserType.C_USER);return new LoginResDTO(token);
保存token到微服务的ThreadLocal中

通过上述流程,前端就获得了token:
在这里插入图片描述
当拿到了token后,就会携带token去访问网关:

        // 2.获取token解析工具JwtTool工具// 2.1.获取tokenString token = GatewayWebUtils.getRequestHeader(exchange, HEADER_TOKEN);if (StringUtils.isEmpty(token)) {return GatewayWebUtils.toResponse(exchange,HttpStatus.FORBIDDEN.value(),ErrorInfo.Msg.REQUEST_FORBIDDEN);}// 2.2.获取tokenKeyString tokenKey = applicationProperties.getTokenKey().get(JwtTool.getUserType(token) + "");if (StringUtils.isEmpty(token)) {return GatewayWebUtils.toResponse(exchange,HttpStatus.FORBIDDEN.value(),ErrorInfo.Msg.REQUEST_FORBIDDEN);}

如果网关校验token通过,就会放行到微服务,微服务就会将token放入ThreadLocal中,由于所有的微服务都需要走这个流程(将token放入ThreadLocal中),因此抽取这部分到MVC模块的拦截器:

    @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.尝试获取头信息中的用户信息String userInfo = request.getHeader(HeaderConstants.USER_INFO);// 2.判断是否为空if (userInfo == null) {return true;}try {// 3.base64解码用户信息String decodeUserInfo = Base64Utils.decodeStr(userInfo);CurrentUserInfo currentUserInfo = JsonUtils.toBean(decodeUserInfo, CurrentUserInfo.class);// 4.转为用户id并保存UserContext.set(currentUserInfo);return true;} catch (NumberFormatException e) {log.error("用户身份信息格式不正确,{}, 原因:{}", userInfo, e.getMessage());return true;}}

其中ThreadLocal定义如下:

public class UserContext {private static final ThreadLocal<CurrentUserInfo> THREAD_LOCAL_USER = new ThreadLocal<>();/*** 获取当前用户id** @return 用户id*/public static Long currentUserId() {return THREAD_LOCAL_USER.get().getId();}public static CurrentUserInfo currentUser() {return THREAD_LOCAL_USER.get();}/*** 设置当前用户id** @param currentUserInfo 当前用户信息*/public static void set(CurrentUserInfo currentUserInfo) {THREAD_LOCAL_USER.set(currentUserInfo);}/*** 清理当前线程中的用户信息*/public static void clear(){THREAD_LOCAL_USER.remove();}
}

那么微服务之后获取token只需要从ThreadLocal获取即可
之后相信大家对于本项目的认证流程很熟悉了:
在这里插入图片描述

1.2.5 手机验证码认证

1.2.5.1 开发与部署app前端

开发和部署细节省略
在这里插入图片描述

1.2.5.2 customer短信认证接口设计
需求分析

首先散户打开app,会输入手机号,点击获取验证码
在这里插入图片描述
之后会发出这个请求:
在这里插入图片描述
散户输入验证码后,点击登录即可进入主界面
在这里插入图片描述
短信验证流程如下:
在这里插入图片描述
那么下面我去实现这个功能

Controller层

发送验证码:

@RestController
@RequestMapping("/sms-code")
@Api(tags = "验证码相关接口")
public class SmsCodeController {@Resourceprivate ISmsCodeService smsCodeService;@PostMapping("/send")@ApiOperation("发送短信验证码")public void smsCodeSend(@RequestBody SmsCodeSendReqDTO smsCodeSendReqDTO) {smsCodeService.smsCodeSend(smsCodeSendReqDTO);}
}

验证验证码:

    @Resourceprivate ILoginService loginService;@PostMapping("/worker")@ApiOperation("服务人员/机构人员登录接口")public LoginResDTO loginForWorker(@RequestBody LoginForWorkReqDTO loginForWorkReqDTO) {if(UserType.INSTITUTION == loginForWorkReqDTO.getUserType()){return loginService.loginForPassword(loginForWorkReqDTO);}else{return loginService.loginForVerify(loginForWorkReqDTO);}}
Service层

当用户点击了发送验证码后,首先会调用Public模块的服务,生成一个随机的验证码存储在redis中为验证做准备:

    @Overridepublic void smsCodeSend(SmsCodeSendReqDTO smsCodeSendReqDTO) {if(StringUtils.isEmpty(smsCodeSendReqDTO.getPhone()) || StringUtils.isEmpty(smsCodeSendReqDTO.getBussinessType())) {log.debug("不能发送短信验证码,phone:{},bussinessType:{}", smsCodeSendReqDTO.getPhone(), smsCodeSendReqDTO.getBussinessType());return;}String redisKey = String.format(CommonRedisConstants.RedisKey.VERIFY_CODE, smsCodeSendReqDTO.getPhone(), smsCodeSendReqDTO.getBussinessType());// 取6位随机数
//        String verifyCode = (int)(Math.random() * 1000000) + "";String verifyCode = "123456";//为方便测试固定为123456log.info("向手机号{}发送验证码{}",smsCodeSendReqDTO.getPhone(),verifyCode);//todo调用短信平台接口向指定手机发验证码...//短信验证码有效期5分钟redisTemplate.opsForValue().set(redisKey, verifyCode, 300, TimeUnit.SECONDS);}

当用户输入验证码后,点击登录按钮则进行校验:
这次是调用Customer模块的服务
在这里插入图片描述
发送的请求参数是手机号,验证码以及验证身份的代码,收到请求后,先判空,若不为空,则交由Public模块去验证验证码的正确性,假如验证码没错,那么检查用户是否冻结或者没有注册,若没有注册,则自动注册,存入数据库,生成并返回token:

    @Overridepublic LoginResDTO loginForVerify(LoginForWorkReqDTO loginForWorkReqDTO) {// 数据校验if(StringUtils.isEmpty(loginForWorkReqDTO.getVeriryCode())){throw new BadRequestException("验证码错误,请重新获取");}//远程调用publics服务校验验证码是否正确boolean verifyResult = smsCodeApi.verify(loginForWorkReqDTO.getPhone(), SmsBussinessTypeEnum.SERVE_STAFF_LOGIN, loginForWorkReqDTO.getVeriryCode()).getIsSuccess();if(!verifyResult) {throw new BadRequestException("验证码错误,请重新获取");}// 登录校验// 根据手机号和用户类型获取服务人员或机构信息ServeProvider serveProvider = serveProviderService.findByPhoneAndType(loginForWorkReqDTO.getPhone(), loginForWorkReqDTO.getUserType());// 账号禁用校验if(serveProvider != null && CommonStatusConstants.USER_STATUS_FREEZE == serveProvider.getStatus()) {throw new CommonException(ErrorInfo.Code.ACCOUNT_FREEZED, serveProvider.getAccountLockReason());}// 自动注册if(serveProvider == null) {serveProvider = serveProviderService.add(loginForWorkReqDTO.getPhone(), UserType.WORKER, null);}// 生成登录tokenString token = jwtTool.createToken(serveProvider.getId(), serveProvider.getName(), serveProvider.getAvatar(), loginForWorkReqDTO.getUserType());return new LoginResDTO(token);}

Public模块校验代码如下:
首先根据手机号和人员类型拼接出key,从Redis取出相应的验证码,之后进行一个校验,由于验证码只能用一次,通过校验则立马删除Redis的该条记录

    @Overridepublic boolean verify(String phone, SmsBussinessTypeEnum bussinessType, String verifyCode) {// 1.验证前准备String redisKey = String.format(CommonRedisConstants.RedisKey.VERIFY_CODE, phone, bussinessType.getType());String verifyCodeInRedis = redisTemplate.opsForValue().get(redisKey);// 2.短验验证,验证通过后删除code,code只能使用一次boolean verifyResult = StringUtils.isNotEmpty(verifyCode) && verifyCode.equals(verifyCodeInRedis);if(verifyResult) {redisTemplate.delete(redisKey);}return verifyResult;}

所以大家应该理解了下图:
在这里插入图片描述

1.2.6 账号密码验证

1.2.6.1 前端设计和部署

在这里插入图片描述

1.2.6.2 customer账号密码验证接口设计
Controller设计
    @Resourceprivate ILoginService loginService;@PostMapping("/worker")@ApiOperation("服务人员/机构人员登录接口")public LoginResDTO loginForWorker(@RequestBody LoginForWorkReqDTO loginForWorkReqDTO) {if(UserType.INSTITUTION == loginForWorkReqDTO.getUserType()){return loginService.loginForPassword(loginForWorkReqDTO);}else{return loginService.loginForVerify(loginForWorkReqDTO);}}
Service层设计

首先会进行判空,若不为空,则根据账号去查数据库,以及进行密码校验(BCrypt加密),若通过则生成并返回token

    @Overridepublic LoginResDTO loginForPassword(LoginForWorkReqDTO loginForWorkReqDTO) {// 1.数据校验if(StringUtils.isEmpty(loginForWorkReqDTO.getPassword())) {throw new BadRequestException("请输入密码");}// 2.登录校验// 2.1.根据手机号和用户类型获取服务人员或机构信息ServeProvider serveProvider = serveProviderService.findByPhoneAndType(loginForWorkReqDTO.getPhone(), loginForWorkReqDTO.getUserType());// 账号禁用校验if(serveProvider != null && CommonStatusConstants.USER_STATUS_FREEZE == serveProvider.getStatus()) {throw new CommonException(ErrorInfo.Code.ACCOUNT_FREEZED, serveProvider.getAccountLockReason());}//密码校验if(serveProvider == null || !passwordEncoder.matches(loginForWorkReqDTO.getPassword(), serveProvider.getPassword())){throw new BadRequestException("账号或密码错误,请重新输入");}// 3.生成登录tokenString token = jwtTool.createToken(serveProvider.getId(), serveProvider.getName(), serveProvider.getAvatar(), loginForWorkReqDTO.getUserType());return new LoginResDTO(token);}
顺便复习下BCrypt加密

BCrypt是一种密码哈希函数,通常用于存储用户密码的安全性。它是基于 Blowfish 密码算法的一种单向哈希函数
使用很简单,如下:

public static void main(String[] args) {BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();/**$2a$10$1sp7I0OdYH3Azs/2lK8YYeuiaGZzOGshGT9j.IYArZftsGNsXqlma$2a$10$m983E2nmJ7ITlesbXzjbzO/M7HL2wP8EgpgX.pPACDm1wG38Lt.na$2a$10$rZvathrW98vVPenLhOnl0OMpUtRTdBkWJ45IkIsTebITS9AFgKqGK$2a$10$2gaMKWCRoKdc42E0jsq7b.munjzOSPOM4yr3GG9M6194E7dOH5LyS$2a$10$I/n93PIKpKL8m4O3AuT5kuZncZhfqV51bfx5sJrplnYoM7FimdboC*/for (int i = 0; i < 5; i++) {//对密码进行哈希String encode = passwordEncoder.encode("11111");System.out.println(encode);}//校验哈希串和密码是否匹配boolean matches = passwordEncoder.matches("11111", "$2a$10$m983E2nmJ7ITlesbXzjbzO/M7HL2wP8EgpgX.pPACDm1wG38Lt.na");System.out.println(matches);
}

1.2.7 注册功能实现

1.2.7.1 需求

用户点击注册按钮:
在这里插入图片描述
输入手机号获取验证码,再输入密码进行注册,接口设计如下:
在这里插入图片描述
接口地址:POST/customer/open/serve-provider/institution/register

1.2.7.2 Costomer注册功能接口实现
Controller开发
@RestController("openServeProviderController")
@RequestMapping("/open/serve-provider")
@Api(tags = "白名单接口 - 服务人员或机构相关接口")
public class ServeProviderController {@Autowiredprivate IServeProviderService iServeProviderService;@PostMapping("/register")public void institutionRegister(@RequestBody InstitutionRegisterReqDTO institutionRegisterReqDTO){iServeProviderService.registerInstitution(institutionRegisterReqDTO);}
}
Service开发

首先校验验证码,假如验证码没问题就进行注册:

    /*** 机构注册* @param institutionRegisterReqDTO*/@Overridepublic void registerInstitution(InstitutionRegisterReqDTO institutionRegisterReqDTO) {//1.验证验证码boolean verifyResult = smsCodeApi.verify(institutionRegisterReqDTO.getPhone(),SmsBussinessTypeEnum.INSTITION_REGISTER,institutionRegisterReqDTO.getVerifyCode()).getIsSuccess();if(verifyResult){throw new BadRequestException("验证码校验失败");}//2.新增机构owner.add(institutionRegisterReqDTO.getPhone(),UserType.INSTITUTION,institutionRegisterReqDTO.getPassword());}

其中注册接口如下,先验证手机号是否已经被注册,没有则写入数据库:

    @Override@Transactional(rollbackFor = Exception.class)public ServeProvider add(String phone, Integer type, String password) {// 校验手机号是否存在ServeProvider existServeProvider = lambdaQuery().eq(ServeProvider::getPhone, phone).one();if (existServeProvider != null) {if(existServeProvider.getType().equals(UserType.WORKER)){throw new BadRequestException("该账号已被服务人员注册");}else {throw new BadRequestException("该账号已被机构注册");}}//新增服务人员/机构信息ServeProvider serveProvider = new ServeProvider();serveProvider.setPhone(phone);serveProvider.setPassword(password);serveProvider.setType(type);serveProvider.setStatus(CommonStatusConstants.USER_STATUS_NORMAL);serveProvider.setCode(IdUtils.getSnowflakeNextIdStr());baseMapper.insert(serveProvider);//新增服务人员/机构配置信息同步表信息,方便后期对配置项进行配置serveProviderSettingsService.add(serveProvider.getId(), type);return serveProvider;}
    @Override@Transactional(rollbackFor = {Exception.class})public void add(Long id, Integer serveProviderType) {ServeProviderSettings serveProviderSettings = new ServeProviderSettings();serveProviderSettings.setId(id);if(baseMapper.insert(serveProviderSettings) <= 0 || serveProviderSyncService.add(id, serveProviderType) <= 0){throw new DBException("请求失败");}}
演示

在这里插入图片描述

1.2.8 找回密码功能

1.2.8.1 需求

首先用户忘记密码,点击找回密码:
在这里插入图片描述
全部输入后发出请求,接口如下:
在这里插入图片描述
POST/customer/agency/serve-provider/institution/resetPassword
首先校验验证码是否正确。
校验手机号是否存在数据库。
通过校验最后修改密码,密码的加密方式参考机构注册接口。

1.2.8.2 Costomer找回密码功能接口实现
Controller开发
@RestController("agencyServeProviderController")
@RequestMapping("/agency/serve-provider")
@Api(tags = "机构端 - 服务人员或机构相关接口")
public class ServeProviderController {@Resourceprivate IServeProviderService serveProviderService;@GetMapping("/currentUserInfo")@ApiOperation("获取当前用户信息")public ServeProviderInfoResDTO currentUserInfo() {return serveProviderService.currentUserInfo();}@PostMapping("/institution/resetPassword")@ApiOperation("机构登录密码重置接口")public void resetPassword(@RequestBody InstitutionResetPasswordReqDTO institutionResetPasswordReqDTO){serveProviderService.resetPassword(institutionResetPasswordReqDTO);}
}
Service开发
    /*** c机构重置密码* @param institutionResetPasswordReqDTO*/@Overridepublic void resetPassword(InstitutionResetPasswordReqDTO institutionResetPasswordReqDTO) {//1.校验验证码Boolean verifyResult = smsCodeApi.verify(institutionResetPasswordReqDTO.getPhone(),SmsBussinessTypeEnum.INSTITUTION_RESET_PASSWORD,institutionResetPasswordReqDTO.getVerifyCode()).getIsSuccess();if(!verifyResult){throw new BadRequestException("验证码校验错误");}//2.校验数据库是否已经存在这个手机号ServeProvider serveProvider = lambdaQuery().eq(ServeProvider::getPhone, institutionResetPasswordReqDTO.getPhone()).one();if(serveProvider == null){throw new BadRequestException("手机号不存在");}//3.修改密码lambdaUpdate().eq(ServeProvider::getPhone, institutionResetPasswordReqDTO.getPhone()).set(ServeProvider::getPassword,passwordEncoder.encode(institutionResetPasswordReqDTO.getPassword())).update();}
演示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【人工智能】第一部分:ChatGPT的基本概念和技术背景

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

STM32作业实现(八)触摸按键TPAD

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

26 _ 虚拟DOM:虚拟DOM和实际的DOM有何不同?

虚拟DOM是最近非常火的技术&#xff0c;两大著名前端框架React和Vue都使用了虚拟DOM&#xff0c;所以我觉得非常有必要结合浏览器的工作机制对虚拟DOM进行一次分析。当然了&#xff0c;React和Vue框架本身所蕴含的知识点非常多&#xff0c;而且也不是我们专栏的重点&#xff0c…

VBA字典与数组第十五讲:多行多列数组与同列数单行数组间的运算规则

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

LabVIEW齿轮调制故障检测系统

LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统&#xff0c;实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力&#xff0c;结合Hilbert包络解调技术&#xff0c;对齿轮的振动信号进行精确分析…

高考后的赚钱新路径:千行赏金助你开启财富之旅

一、兼职背景与需求 随着高考的落幕&#xff0c;众多学子迎来了人生中的一大转折点。在迈向大学校园的门槛之前&#xff0c;许多学生希望利用这段空闲时间做些兼职&#xff0c;既能够充实自己的暑假生活&#xff0c;又能为家庭减轻经济负担&#xff0c;甚至为自己积累一些宝贵…

基于django | 创建app,并启动django

1、删除系统默认的目录路径&#xff1a;BASE_DIR / templetes 2、在终端输入命令&#xff1a; python manage.py startapp app01 # 这里的app01是我创建app的名称 3、如果没有创建成功&#xff0c;手动点击 Creat App , 4、在 setting.py 中找到 INSTALLED_APPS ,添加 ap…

pycharm简易使用码云gitee

文章目录 参考文献官网地址安装插件第一个选项报错了不可&#xff0c;第二个选项&#xff0c;可以了新库上传到主分支&#xff0c;push改进实验新建分支&#xff0c;上传为新分支&#xff1a;做另一种改进&#xff0c;选择回退主分支&#xff0c;另建一个分支 使用对于一个新项…

非线性系统:相平面法

非线性系统&#xff1a;相平面法 非线性系统的相平面法是一种重要的分析工具&#xff0c;用于研究系统的动力学行为。通过相平面法&#xff0c;可以直观地观察系统状态变量的变化&#xff0c;分析系统的稳定性、周期性和其他动力学特性。本文将详细介绍相平面法的基本思想、步…

[NOIP2015 提高组] 子串

题目背景 NOIP2015 Day2T2 题目描述 有两个仅包含小写英文字母的字符串 A A A 和 B B B。 现在要从字符串 A A A 中取出 k k k 个互不重叠的非空子串&#xff0c;然后把这 k k k 个子串按照其在字符串 A A A 中出现的顺序依次连接起来得到一个新的字符串。请问有多少…

Tree——输出项目的文件结构(Linux)

输出项目中的文件结构可以使用tree命令。tree是一个用于以树状结构显示目录内容的命令行工具。它非常适合快速查看项目的文件结构。安装&#xff1a; sudo apt-get install tree 使用&#xff1a; 在命令行中导航到项目的根目录&#xff0c;输出文件结构。 tree 也可以将结构输…

【30天精通Prometheus:一站式监控实战指南】第13天:graphite_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…

Vue3中的常见组件通信之props和自定义事件

Vue3中的常见组件通信 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $refs4. 默认…

Python课设-学生信息管理系统

一、效果展示图 二、前端代码 1、HTML代码 <1>index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

安卓模拟鼠标,绘图板操作电脑PC端,卡卡罗特也说好,儿童节快乐

家人们&#xff0c;上链接了&#xff1a;https://download.csdn.net/download/jasonhongcn/89387887

B站如何屏蔽短视频:成都鼎茂宏升文化传媒公司

B站如何屏蔽短视频&#xff1a;优化你的观看体验 在当今数字化时代&#xff0c;B站&#xff08;哔哩哔哩&#xff09;作为国内领先的弹幕视频网站&#xff0c;以其丰富的视频资源和独特的弹幕文化吸引了大量用户。然而&#xff0c;随着短视频的兴起&#xff0c;B站也引入了短视…

intel深度相机D455的使用

一、D455介绍 Intel RealSense D455 是RealSense D400系列的一部分&#xff0c;这个系列的设备以其高精度和可靠性而闻名。D455相比于之前的型号&#xff08;如D415和D435&#xff09;&#xff0c;提供了更远的感知范围和更高的精度。 二、使用代码 我们先定义一下相关的函数…

鸿蒙OS初识

学习官网&#xff1a;https://www.harmonyos.com/cn/develop 准备 注册&#xff0c;安装软件&#xff08;node:12, DevEco Studio&#xff09;&#xff1a; https://developer.harmonyos.com/cn/docs/documentation/doc-guides/software_install-0000001053582415#ZH-CN_TOP…

MFC3d立体按钮制作

1、本程序基于前期我的博客文章MFC用CButtonST类实现图片透明按钮(免费源码下载) 2、添加CeXDib.cpp CeXDib.h ShadeButtonST.cpp ShadeButtonST.h到项目文件夹下&#xff0c;和FileView中如图。 3、在ButtonShadeDlg.h中添加代码 #include "ShadeButtonST.h" #in…

Qml开发的两种方法

一.Qml开发的两种方法 1.Qt Creator 开发,手动编写qml代码 这种方法开发很方便&#xff0c;适合对qml语言非常熟悉的开发人员。 2.用Qt Design Studio 设计qml界面 这种方法更适合对qml不太熟悉的人&#xff0c;可以实现qml控件的拖拉拽&#xff0c;类似与widget界面开发&…