衢州建筑裂縫加固seo推廣軟件排行榜
對(duì)于開發(fā)一個(gè)Web項(xiàng)目來說,無論是電商還是其他品類的項(xiàng)目,注冊(cè)與登錄模塊都是必不可少的;注冊(cè)登錄功能也是我們?cè)谌粘I钪凶铋L接觸的,對(duì)于這個(gè)業(yè)務(wù)場景的需求與邏輯大概是沒有什么需要詳細(xì)介紹的,市面上常見的郵箱注冊(cè)、手機(jī)注冊(cè)、獨(dú)立站賬號(hào)密碼注冊(cè),其處理方式基本相同,我們這里使用賬號(hào)密碼注冊(cè)的方式,實(shí)現(xiàn)整個(gè)平臺(tái)的注冊(cè)/登錄功能;
讓我們開始!
判斷用戶名是否存在
?實(shí)現(xiàn)注冊(cè)部分的代碼,首先想到的就是,我們要對(duì)前端所發(fā)送過來的請(qǐng)求參數(shù)做驗(yàn)證,在有些項(xiàng)目中,會(huì)將請(qǐng)求參數(shù)的格式驗(yàn)證和合法性驗(yàn)證只寫在前端校驗(yàn),而后端只實(shí)現(xiàn)業(yè)務(wù)邏輯,我認(rèn)為這是極其危險(xiǎn)的編碼習(xí)慣;當(dāng)我們的項(xiàng)目放在線上的時(shí)候,就會(huì)有惡意用戶繞過前端驗(yàn)證,直接訪問我們的服務(wù)器,對(duì)線上業(yè)務(wù)造成破壞,因此前端驗(yàn)證是為了減輕一部分請(qǐng)求直接到達(dá)后端,但是相應(yīng)的驗(yàn)證后端也要去做
????那么,首先我們要實(shí)現(xiàn)的就是驗(yàn)證用戶注冊(cè)的用戶名是否存在;我們首先來看一下用戶表的結(jié)構(gòu)及設(shè)計(jì):
從圖上我們可以看到用戶表中常用的字段,我們用戶表以用戶的Id為主鍵,但注意:ID并不是自增長的,這與傳統(tǒng)的Id設(shè)計(jì)不同,這里不是自增的原因是:當(dāng)系統(tǒng)達(dá)到一定的體量時(shí),用戶數(shù)量激增,我們需要去做分布式集群,需要分庫分表,這時(shí)自增的ID會(huì)給分庫分表帶來極大的困難,因?yàn)?#xff0c;出于日后系統(tǒng)優(yōu)化的考慮,我們這里的數(shù)據(jù)庫主鍵,不是自增的。
電商接口開發(fā)
????理清業(yè)務(wù)邏輯,看完數(shù)據(jù)庫結(jié)構(gòu),我們著手開始編寫業(yè)務(wù)代碼;在整個(gè)項(xiàng)目代碼的編寫和接口的實(shí)現(xiàn)我們都遵循自底向上的方式,從數(shù)據(jù)庫開始,實(shí)現(xiàn)數(shù)據(jù)的映射,業(yè)務(wù)實(shí)現(xiàn),結(jié)果推送的流程,對(duì)應(yīng)pojo映射---Service編寫---Controller控制的過程。
????那么,我們開始啦:首先,我們創(chuàng)建一個(gè)UserService接口,在接口中,我們編寫我們第一個(gè)業(yè)務(wù)方法:
/**
* 判斷用戶名是否存在
*/
public boolean queryUsernameIsExist(String username);
我們傳入一個(gè)userName,返回一個(gè)布爾值;有了接口之后,我們?nèi)?shí)現(xiàn)這個(gè)方法:
我們?cè)趕ervice工程中,新建一個(gè)Impl的包,在里面新建一個(gè)類UserServiceImpl,去實(shí)現(xiàn)UserService接口,并實(shí)現(xiàn)其中的方法;我們?cè)谶@個(gè)方法中,需要操作User這個(gè)實(shí)體類,那么我們先把UserMapper引入進(jìn)來:
@Autowired
public UsersMapper usersMapper;
在方法中,我們使用Example這種使用條件查詢的方式去做查詢:
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public boolean queryUsernameIsExist(String username) {
Example userExample = new Example(Users.class);
Example.Criteria userCriteria = userExample.createCriteria();
userCriteria.andEqualTo("username",username);
Users result = usersMapper.selectOneByExample(userExample);
return result == null ? false : true;
}
????在這個(gè)方法的實(shí)現(xiàn)中,我們使用了Example這種方式,Example映射一個(gè)實(shí)體類,獲得一個(gè)example對(duì)象,為這個(gè)對(duì)象去添加相應(yīng)的條件,Criteria對(duì)應(yīng)的方法有很多,可以判斷等于,大于,相似等各類條件,使用起來很方便,感興趣的同學(xué)可以去閱讀他的源碼;這個(gè)方法返回一個(gè)Users對(duì)象,我們?nèi)ヅ锌?#xff0c;若為空,則用戶名可用,若false,則用戶名存在;
????實(shí)現(xiàn)了Service之后,我們來編寫Controller,我們?cè)赼pi工程中,新建一個(gè)Controller類。命名為PassportController,我們?yōu)樗由蟁estController注解,并加上路由地址,在這個(gè)Controller中,我們需要操作UserService來進(jìn)行查詢,那么,我們先將UserService注入進(jìn)來:
@Autowired
private UserService userService;
并定義一個(gè)方法,聲明方法的路由地址:
@GetMapping("/usernameIsExist")
public IMOOCJSONResult usernameIsExist(@RequestParam String username) {
//判斷用戶名不能為空
if (StringUtils.isBlank(username)) {
return IMOOCJSONResult.errorMsg("用戶名不能為空");
}
//查找注冊(cè)的用戶名是否存在
boolean isExist = userService.queryUsernameIsExist(username);
if (isExist) {
return IMOOCJSONResult.errorMsg("用戶名已經(jīng)存在");
}
// 請(qǐng)求成功,用戶名沒有重復(fù)
return IMOOCJSONResult.ok();
}
????在這個(gè)方法中,我們?cè)谂袛鄒sername為空的時(shí)候使用了一個(gè)字符串的工具類,他是Apache提供的,我們需要首先引入他的依賴:
<!-- apache 工具類 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
引入之后,我們便可以操作StringUtils,他提供了一個(gè)isBlank的方法,來判斷字符串是否為空;
????同時(shí)我們?cè)诜椒ㄖ?#xff0c;可以看到一個(gè)IMOOCJSONResult對(duì)象,這是一個(gè)結(jié)果集對(duì)象,因?yàn)樵诖a中,我們需要返回的結(jié)果值類型很多,很不確定,并且,我們需要返回自定義的響應(yīng)碼來告訴前端請(qǐng)求成功與否,前端對(duì)約定好的各類返回值做統(tǒng)一處理,避免前端出現(xiàn)代碼報(bào)錯(cuò),影響用戶體驗(yàn),同時(shí),自定義的結(jié)果響應(yīng)能提高我們定位錯(cuò)誤的速度,規(guī)范接口;因此我們先定義一個(gè)結(jié)果返回類:
public class IMOOCJSONResult {
// 定義jackson對(duì)象
private static final ObjectMapper MAPPER = new ObjectMapper();
// 響應(yīng)業(yè)務(wù)狀態(tài)
private Integer status;
// 響應(yīng)消息
private String msg;
// 響應(yīng)中的數(shù)據(jù)
private Object data;
...
}
完整的類代碼請(qǐng)大家去看源碼,不方便在這里粘貼全部代碼,只提供類中的屬性供大家參考,大家可以參考對(duì)應(yīng)的實(shí)現(xiàn)思路去封裝自己的結(jié)果集,我們使用這樣統(tǒng)一的結(jié)果類便可以在返回?cái)?shù)據(jù)的同時(shí),返回請(qǐng)求狀態(tài)碼給前端,提高接口可讀性和穩(wěn)定性。
接口測試
????當(dāng)我們第一個(gè)接口編寫完成后,我們使用PostMan進(jìn)行接口測試,postman是一款開源免費(fèi)的接口調(diào)試工具,可以模擬客戶端發(fā)出請(qǐng)求,是后端開發(fā)必備工具:
我們可以看到,我們的請(qǐng)求是成功的,當(dāng)然圖示中,我請(qǐng)求的是我的生產(chǎn)服務(wù)器接口,大家在本機(jī)調(diào)試的時(shí)候URL應(yīng)該是localhost開頭的,我們可以看到請(qǐng)求結(jié)果的響應(yīng)也是我們封裝的結(jié)果集的響應(yīng),有響應(yīng)碼和數(shù)據(jù)體組成,大家可以認(rèn)識(shí)更換請(qǐng)求數(shù)據(jù),測試接口功能的完整性。
用戶注冊(cè)
完成了第一個(gè)判斷用戶名是否存在的接口后,我們著手開始進(jìn)行用戶注冊(cè)的邏輯編寫,首先我們?cè)谧?cè)的時(shí)候,依然是操作UserMapper,所以數(shù)據(jù)層是已經(jīng)準(zhǔn)備好的,我們?cè)赨serService中,定義第二個(gè)創(chuàng)建用戶的方法:
/**
* 創(chuàng)建用戶
* @param userBO
* @return
*/
public Users createUser(UserBO userBO);
這個(gè)方法會(huì)接收一個(gè)表單數(shù)據(jù),數(shù)據(jù)包中包含密碼,用戶名,甚至更多的信息,如果我們?cè)诤蠖艘粋€(gè)一個(gè)的接收,顯然是不合理的,當(dāng)請(qǐng)求參數(shù)過多時(shí),我們便將請(qǐng)求參數(shù)封裝成一個(gè)請(qǐng)求的實(shí)體類,在這里我們封裝一個(gè)UserBO:
public class UserBO {
@ApiModelProperty(value = "用戶名",name = "username",example ="張三",required =true)
private String username;
@ApiModelProperty(value = "密碼",name = "password",example ="123456",required =true)
private String password;
@ApiModelProperty(value = "確認(rèn)密碼",name = "confirmPassword",example ="123456",required =false)
private String confirmPassword;
????...
}
get/set方法大家自行生成,方法定義好后,我們?nèi)?shí)現(xiàn)類中,實(shí)現(xiàn)他:
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Users createUser(UserBO userBO) {
//使用工具類生成唯一id
String userId = sid.nextShort();
Users user = new Users();
user.setId(userId);
user.setUsername(userBO.getUsername());
try {
user.setPassword(MD5Utils.getMD5Str(userBO.getPassword()));
} catch (Exception e) {
e.printStackTrace();
}
//默認(rèn)用戶昵稱同用戶名
user.setNickname(userBO.getUsername());
//默認(rèn)頭像
user.setFace(USER_FACE);
//默認(rèn)生日
user.setBirthday(DateUtil.stringToDate("1970-01-01"));
//設(shè)置性別(使用枚舉操作)默認(rèn)為:保密
user.setSex(Sex.secret.type);
user.setCreatedTime(new Date());
user.setUpdatedTime(new Date());
usersMapper.insert(user);
return user;
}
我們?cè)谶@里存儲(chǔ)密碼時(shí),使用了MD5加密機(jī)制,防止數(shù)據(jù)庫資源泄露,導(dǎo)致用戶數(shù)據(jù)泄露,保證數(shù)據(jù)的安全性,MD5的工具類大家可以在源碼中獲取,我就不貼在這里了;同時(shí)使用的工具類還有日期格式化工具類,同樣的,大家在源碼中獲取;我們?cè)谛陆ㄓ脩粼O(shè)置性別時(shí),我們可以使用枚舉的形式去定義用戶的性別,增強(qiáng)代碼的可讀性:
/**
* @Desc:性別枚舉
*/
public enum Sex {
woman(0,"女"),
man(1,"男"),
secret(2,"保密");
public final Integer type;
public final String value;
Sex(Integer type, String value) {
this.type = type;
this.value = value;
}
}
我們?cè)谏晌ㄒ籌D的時(shí)候,我們也會(huì)使用工具類,注入Sid對(duì)象,使用org.n3r.idworker中的方法為了使用其他包中的方法,我們需要SpringBoot在啟動(dòng)時(shí),掃描到idworker,我們需要在Application中配置: