????????1、通过形参接收前端传递的参数(原始密码,新密码,确认密码)
????????2、调用request对象,获取设置在cookie中的用户Id
????????3、调用Service层修改密码的功能,得到ResultInfo对象
? ? ? ? 4、返回ResultInfo对象
? ? ? ? 1、接收controller层传来的四个参数(用户ID、原始密码、新密码、确认密码)
? ? ? ? 2、通过用户ID查询用户记录,service层拿到用户对象
? ? ? ? 3、参数校验
? ? ? ? ? ? ? ?3.1、?待更新用户记录是否存在(用户对象是否为空)
? ? ? ? ? ? ? ? 3.2、若用户存在,判断原始密码是否为空
????????????????3.3、若原始密码不空,判断原始密码是否正确
? ? ? ? ? ? ? ? 3.4、原始密码正确,判断新密码是否为空
? ? ? ? ? ? ? ? 3.5、新密码不为空,判断新密码与原始密码是否一致(不允许这两个一致)
? ? ? ? ? ? ? ? 3.6、新密码与原始密码不一致,判断确认密码是否为空
? ? ? ? ? ? ? ? 3.7、确认密码不为空,判断确认密码与新密码是否一致
? ? ? ? 4、设置用户密码
? ? ? ? ? ? ? ? 将用户新密码通过指定算法进行加密(md5加密)
? ? ? ? 5、执行更新操作,判断受影响的行数
? ? ? ? ? ? ? ? ?通过用户ID修改用户密码
????????该层代码在前文逆向工程中已经帮助我们生成,不需要自己写,下面展示代码仅为文章完整性
? ? ? ? 1、UserMapper类继承BaseMapper类,BaseMapper类文件里有我们需要的更新单条记录的方法
package com.msb.crm.dao;
import com.msb.crm.base.BaseMapper;
import com.msb.crm.vo.User;
public interface UserMapper extends BaseMapper<User,Integer> {
public User queryUserByName(String userName);
}
? ? ? ? 2、BaseMapper类文件
/**
* 更新单条记录
* @param entity
* @return
*/
public Integer updateByPrimaryKeySelective(T entity) throws DataAccessException;
? ? ? ? 3、mapper.xml文件里的update语句(该文件在之前的文章里讲过是用逆向工程自动生成的)
<update id="updateByPrimaryKeySelective" parameterType="com.msb.crm.vo.User" >
update t_user
<set >
<if test="userName != null" >
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="userPwd != null" >
user_pwd = #{userPwd,jdbcType=VARCHAR},
</if>
<if test="trueName != null" >
true_name = #{trueName,jdbcType=VARCHAR},
</if>
<if test="email != null" >
email = #{email,jdbcType=VARCHAR},
</if>
<if test="phone != null" >
phone = #{phone,jdbcType=VARCHAR},
</if>
<if test="isValid != null" >
is_valid = #{isValid,jdbcType=INTEGER},
</if>
<if test="createDate != null" >
create_date = #{createDate,jdbcType=TIMESTAMP},
</if>
<if test="updateDate != null" >
update_date = #{updateDate,jdbcType=TIMESTAMP},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
?在前文的UserService类中新增下面的方法
/**
* 定义用户修改密码具体方法
* 1、接收controller层传来的四个参数(用户ID、原始密码、新密码、确认密码)
* 2、通过用户ID查询用户记录,service层拿到用户对象
* 3、参数校验(借助判断工具AssertUtil.isTrue)
* 4、设置用户密码
* 将用户新密码通过指定算法进行加密(md5加密)
* 5、执行更新操作,判断受影响的行数
* @param userId 用户ID
* @param oldPwd 原始密码
* @param newPwd 新密码
* @param repeatPwd 确认密码
*/
//添加事务注解(除了select方法,其余dao层操作都要添加事务注解)
@Transactional(propagation = Propagation.REQUIRED)
public void updatePassword(Integer userId,String oldPwd,String newPwd,String repeatPwd){
//调用dao层方法:根据用户ID查询用户信息,拿到用户对象
User user = userMapper.selectByPrimaryKey(userId);
//待更新用户记录是否存在
AssertUtil.isTrue(null == user,"待更新记录不存在");
//参数校验:执行项较多,在这里留一个调用方法,在下面定义具体方法
checkPasswordParams(user,oldPwd,newPwd,repeatPwd);
//过滤完之后,把新密码加密后设置到用户对象里,完成service层的用户密码更新(这一步的更新操作后得到一个更新后的user,把这个整体对象作为实参传给dao层)
user.setUserPwd(Md5Util.encode(newPwd));
//dao层接收user实参调用update方法完成数据库里的user更新,
//通过返回的受影响行数判断dao层操作是否成功(若是出现系统崩溃等情况可能导致数据更新不成功)
AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user)<1,"修改密码失败!!");
}
/**
* 参数校验具体方法
* @param user
* @param oldPwd
* @param newPwd
* @param repeatPwd
* @return void
*/
private void checkPasswordParams(User user, String oldPwd, String newPwd, String repeatPwd) {
//判断原始密码是否为空
AssertUtil.isTrue(StringUtils.isBlank(oldPwd),"原始密码不能为空");
//判断原始密码是否正确
AssertUtil.isTrue(!user.getUserPwd().equals(Md5Util.encode(oldPwd)),"原始密码不正确");
//判断新密码是否为空
AssertUtil.isTrue(StringUtils.isBlank(newPwd),"新密码不能为空");
//判断新密码与原始密码是否一致
AssertUtil.isTrue(oldPwd.equals(newPwd),"原始密码与新密码不能相同");
//判断确认密码是否为空
AssertUtil.isTrue(StringUtils.isBlank(repeatPwd),"确认密码不能为空");
//判断确认密码与新密码是否一致
AssertUtil.isTrue(!newPwd.equals(repeatPwd),"确认密码与新密码必须相同");
}
在前文基础上,给UserController类添加下面的方法。
补充说明:由于这层调用的 userService对象有可能抛出异常(???在哪里抛出??),所以controller层要进行异常捕获和处理。
@PostMapping("updatePwd")
@ResponseBody
public ResultInfo updateUserPassword(HttpServletRequest request,String oldPassword,String newPassword,String repeatPassword){
ResultInfo resultInfo = new ResultInfo();
try{
//获取cookie中的userId
Integer userId = LoginUserUtil.releaseUserIdFromCookie(request);
//调用service层修改密码方法
userService.updatePassword(userId,oldPassword,newPassword,repeatPassword);
}catch(ParamsException p){
resultInfo.setCode(p.getCode());
resultInfo.setMsg(p.getMsg());
p.printStackTrace();
}catch (Exception e){
resultInfo.setCode(500);
resultInfo.setMsg("修改密码失败!");
e.printStackTrace();
}
return resultInfo;
}
1、测试工具:postman
2、测试步骤:
????????2.1、先确定自己的view包下的user包里有password.ftl文件(前面的文章里有发过资料),然后删除target文件夹
????????2.2、点击绿色三角按钮启动服务器
????????2.3、打开postman新建一个post请求,输入访问路径(我本地服务端口为8089)
?????????????????localhost:8089/mycrm/user/updatePwd?
? ? ? ? ? ? ? ? 小试牛刀点击send查看结果:
????????2.4、模拟浏览器进行post访问(主要关注浏览器的Cookie):
????????????????2.4.1、先在浏览器?输入路径:??????http://localhost:8089/mycrm/main访问我们的主页面然后点击按照下图点击修改密码
? ? ? ? ? ? ? ? 2.4.2、快捷键F12(或者Fn+F12)打开浏览器控制台,在Application选项下找到Cookies项点开,然后就找到我们要关注的cookie信息,把userIdStr后面的那一串复制
????????????????2.4.3、返回postman点击"send"按钮下面的"Cookies"按钮创建cookie
????????????????2.4.4、在弹出框里填写自己项目域名,我的是localhost
????????????????2.4.5、再点击后面的Add domain,接着点击+Add Cookie
????????????????2.4.6、在弹出框里修改信息:
????????????????????????Cookie_1=value? 改为:
????????????????????????userIdStr=%23ATM0czM%23%23gMyIzM4MjM0czMwcTM(刚刚复制的那一串)
????????????????2.4.7、然后点击save,就保存好了Cookie,关闭当前窗口
????????????????????2.4.8、? 再次点击send,观察结果
????????2.5、现在我们可以在Query Params下面的表里添加旧密码,新密码,确认密码的具体信息
????????2.6、密码修改成功,尝试用相信密码登录一下,查看结果? ? ?
? ? ? ? 1、后端实现了密码修改功能,并且功能测试也已经通过,到此后端的事情暂时完成,接下来我们编写修改密码模块的前端代码。
? ? ? ? 2、前端代码要完成的任务:
????????????????2.1、呈现用户操作的界面(静态部分,可以用framework框架编写在.ftl文件中)
? ? ? ? ? ? ? ? 2.2、与后台交互数据(动态部分:相当于机动组,可以编写在.js文件中)
? ? ? ? ? ? ? ? 2.3、关联:静态页面包含动态机动页面
? ? ? ? 3、注意事项:与用户交互的界面其实是一个请求结果,也就是说当用户请求某个页面时不是直接得到这个页面,是先通过.js文件将请求发送到后台controller层的控制器,控制器接收数据后向service层转发,然后再到dao 层处理,然后逐级返回处理信息到controller层的控制器,判断用户操作是合法的话,控制器就会向用户返回请求资源(返回资源为两类:view视图/JSON数据)。??
? ? ? ? 4、具体代码实现
????????目标:希望点击“修改密码”这个选项后页面跳转到修改密码页面
????????1、修改密码选项是主页面的一个超链接,我们找到主页面的代码,查看这个超链接的访问路径
????????2、我们根据超链接的访问路径在UserController类里创建该访问路径的方法,返回值为:请求页面
/**
* 接收修改密码界面的请求并执行资源返回操作
* @return
*/
@RequestMapping("toPasswordPage")
public String toPasswordPage(){
return "user/password";
}
总的原则:参数来源和去向找到以后要保持一致性
1、数据库里表字段与实体类名还有属性名保持规定形变后一致?
2、service层方法名与dao层映射文件里的id属性值一致
3、前端index.js、index.ftl与controller层三一致
????????caontroller层接收前端页面参数,要保持参数名字与name属性值一致
????????对外暴露的方法用public修饰,仅限于内部成员调用的方法用private修饰
????????方法?变量?常量?类?对象?接口?
????????IndexController、UserController.....
? ? ? ? 为了便于分模块编程,后期也便于排查错误和管理代码。
????????????????问题背景:我在Controller层中注入了ResultInfo类型的对象,以供该层的两个方法调用该对象,但实际操作中却发现:后端测试时单独调用Controller层的登录方法和修改密码方法(不去new ResultInfo)都能正常运行,但是利用前端界面请求登录方法时,即使输入的账号密码正确,登陆页面也会报错找不到资源???
? ? ? ? 1、原因分析:
????????????????因为spring容器中默认生成的对象是单例,而某类中多个方法都要使用某对象进行任务处理时就会产生并发问题。(并发问题简单理解: 给某对象下达任务:让一个人哄孩子,还让这个人去做手术,任务都要做,但做事的人就一个,所以第一个解决办法:自然而然想到多分配个人来做事情,也就是使用多例对象,或者第二个解决办法:给这个人每个时段分配单个任务)?
????????????????关于单例与多例的理解和使用时机及原理可以观摩这篇博客
????????????????单例与多例
????????下面介绍第一个解决方法详细步骤:?
????????2、解决步骤?????????
? ? ? ? ? ? ? 2.1、先搞清楚自己在哪一层有Bean多例的需求,然后再着手处理
? ? ? ? ? ? ? ? ? ? 因为Controller层和Sservice层处理方式有差异。
? ? ? ? ? ? ? 2.2、分情况处理:
????????????????2.2.1、若要给Controller层注入多例对象:
? ? ? ? ? ? ? ? ? ? ? ? (1)找到该对象所在类(鼠标选中目标类名,按“ctrl+B”进入该类)
? ? ? ? ? ? ? ????????? (2)在类级别加上注解:
???????@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
???????或者@Scope(
"prototype")
? ? ? ? ? ? ? ? ? ? ? ? (3)添加配置类(自己新建一个文件夹config放置该类ResultInfoConfig)
? ? ? ? ? ? ? ? ?????????(4)修改后重新启动项目进行业务测试,看到方法正常运行。🆗问题解决。
登陆成功
????????????????2.2.2、若是Service层, 请移步到该文章查看解决办法,本文不做重点介绍:spring多例如何实现