最近工作比較忙,上個月幾乎整個月都在加班,在大家都在討論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功能越來越中意。大部分程序員應該都差不太多,可以參考一下。