开始实现功能(一)——用户模块,从注册登录开始
最近工作比较忙,上个月几乎整个月都在加班,在大家都在讨论996的那几天,自己和同事们几乎每天都是10点以后才下班,有一次到家已经是第二天凌晨一点钟了。虽然说这种时候很疲惫,每天总有改不完的bug,但是当看到自己写的代码吭哧吭哧跑起来,看着每一行日志被打印出来的时候,真的会有激动地留下眼泪的冲动。
写这篇文章的时候正好是母亲节,同时又是5·12,祝所有的母亲节日快乐,同时也希望那些在11年前那场灾难中收到伤害的人们走出阴霾,重新燃起斗志。
书归正传,现如今互联网发展的依然如日中天,形形色色的网站无数,非要说这些网站最核心的东西是什么,我觉得是用户。所以软件开发也要从用户入手,从用户的每一个行为入手,从用户注册、登录、完善信息、修改信息、用户行为···,等等,一个功能一个功能的去实现,最终构成整个用户模块。
(一)用户的注册。
用户注册对网站来说是是新增了一个用户,对整个程序来讲,就是在数据库中插入了一条之前不存在的数据,更准确的说,应该是在数据库的用户表中新增了一条数据。当然在到数据库之前需要对这条数据进行一系列的验证、审核、加密等等操作,但这些都不是重点,重点是这条数据还要被送到数据库中,在数据库中需要保存什么信息,用户注册的时候应该保存什么信息,这些信息哪些是需要用户手动输入的,哪些是需要程序自动生成的,哪些需要随着用户的进一步使用一点一点添加的……,这些事作为一个开发者或者说对于做一个产品需要考虑的问题。
在平时的使用过程中,能想到的大概有以下这些信息:
其中,用户名、密码、邮箱、生日是需要用户在注册的时候就要输入的;id,创建时间、修改时间数据需要程序自动生成;昵称需要用户使用过程中去修改,而角色、用户状态等信息,需要后期根据用户相应的操作进行更改,这些状态数据在这里保存的实际上只是一个代码或编号,具体编号代表什么含义,需要在一个字典表中进行定义,像这样:
准备工作完成,下面可以开始根据业务逻辑实现功能了。首先需要创建用户相关的逻辑代码,新建几个类,这时候整个项目包视图下长这个样子:
首先实现用户注册中的数据传递代码,页面接收用户获取的数据,传递给后台服务器,服务器接收数据,进行验证补充后传入数据库。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HIHI-用户注册</title> </head> <body> <div> <form action="http://localhost:8080/user/regist" method="post" datatype="application/json"> <table> <tr> <td><p>用户名:</p></td> <td><input name="username" type="text"></td> </tr> <tr> <td><p>密 码:</p></td> <td><input name="password" type="password"></td> </tr> <tr> <td><p>确 认:</p></td> <td><input type="password"></td> </tr> <tr> <td><p>邮 箱:</p></td> <td><input name="email" type="email"></td> </tr> <tr> <td><p>生 日:</p></td> <td><input name="birthday" type="date"></td> </tr> </table> <input type="submit" value="注册"> <input type="button" onclick="verifyUsername()" value="验证"> </form> </div> </body> </html>
@Data @Accessors(chain = true) public class UserRegister { private String userId; private String username; private String password; private String email; private String birthday; private Date createDate; }
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 用户注册 * @param user */ @PostMapping("/regist") public void userRegist(UserRegister user){ userService.userRegist(user); } /** * 用户名可用性验证 * @param username * @return */ @PostMapping("/verify/username") public SysResultVo verifyUsername(@RequestParam String username){ boolean exist=userService.verifyUsername(username); if (!exist){ return SysResultVo.ok("恭喜!!!用户名"+username+"可用"); } return SysResultVo.fail("用户名"+username+"不可用!!!"); }
@Service public class UserService { @Autowired private UserDao userDao; /** * 注册用户 * @param user */ @Transactional public void userRegist(UserRegister user){ String userId= UUID.randomUUID().toString().replace("-",""); String password=user.getPassword(); password=DigestUtils.md5DigestAsHex(password.getBytes()); user.setUserId(userId).setPassword(password).setCreateDate(new Date()); userDao.createUser(user); } /** * 验证用户名是否可用 * @param username * @return */ public boolean verifyUsername(String username) { String usernameDB=userDao.verifyUsername(username); if (usernameDB!=null||"".equals(usernameDB)) return false; return true; } }
@Component public interface UserDao { @Insert("insert into sys_user(user_id,username,password,email,birthday,create_date) " + "values(#{userId},#{username},#{password},#{email},#{birthday},#{createDate})") int createUser(UserRegister user); @Select("select username from sys_user where username=#{username}") String verifyUsername(@Param("username") String username); }
当然还需要进行页面跳转,进入到注册页面代码如下:
@Controller public class PageController { @RequestMapping("/regist") public String pageJump(/*@PathVariable String pageName*/){ return "/page/regist.html"; } }
以上代码只是用户注册功能最简答的实现,具体工作中验证条件会更加丰富、数据库字段也更多。有时候因为开发框架的不同具体的实现代码也会有所不同,但总体上来说都遵循一个步骤。
(二)用户登录
和用户注册相对应,用户登录功能涉及到对数据库的查询,需要将用户发送的数据和数据库中的数据进行匹配,匹配一致表示登录成功,不一致表示登录失败。这里需要注意的是:
1.用户密码的加密方式要和用户注册时的加密方式一致;
2.检验用户的状态是否为封号状态,封号状态不能登录;
3.对于大量用户的网站,需要考虑增加缓存和数据库索引等方式提高查询效率,这里不讨论;
4.实际开发过程中,出于安全方面的考虑,还需要获取用户登录Id,验证信息等数据,保证账号安全;
主要代码如下:
@Data @Accessors(chain = true) public class User { private String userId; private String username; private String password; private String email; private String birthday; private String createDate; private String updateLastDate; private String nickname; private String roleId; private String userStatus; private String headImgPath; }
@PostMapping("/login") public SysResultVo userLogin(String username, String password) { User user = userService.userLogin(username, password); if (user != null) { return new SysResultVo(user); } return SysResultVo.fail("用户不存在或已被禁用"); }
public User userLogin(String username, String password) { User user=userDao.selectUser(null,username); String passSource=DigestUtils.md5DigestAsHex(password.getBytes()); if ("3".equals(user.getUserStatus())) return null; if (passSource.equals(user.getUserId())){ return user; } return null; }
<mapper namespace="com.hatten.hihivideo.dao.UserDao"> <select id="selectUser" resultType="com.hatten.hihivideo.entity.User"> select * from sys_user where <if test="userId != null">user_id=#{userId}</if> <if test="username != null">username=#{username}</if> </select> </mapper>
代码过于简单,这里不做测试。
对于开发过程中的cotroller、service、dao层次分工这里说一下,每个人有每个人的习惯,我的习惯是,在controller层进行数据获取、简单的验证、以及数据回传时的状态验证等操作;而service层对数据进行精细化处理,包括二次验证、数据转换、查询结果的状态数据补充等等操作,在dao层主要进行数据库的crud操作,有时对于过于复杂的逻辑或者数据库数据过于分散需要进行很多表的操作会通过dao层调用plsql存储过程和函数等等。
而对于一些额外的操作,比如用户的操作状态,操作习惯等数据和逻辑可以通过spring的AOP功能进行添加额外操作,随着工作时间越来越长,接触的各种奇葩业务逻辑越来越多,个人对spring的AOP功能越来越中意。大部分程序员应该都差不太多,可以参考一下。