[springboot 開發單體web shop] 3. 用戶註冊實現

用戶註冊

做爲一個現代化電商平臺,什麼最重要呢?of course 是用戶,廣大用戶羣體是支持咱們可持續發展的基石,顧客是上帝, 雖然在當今上帝已經不被重視了,特別是不少的平臺對於老用戶就是巴不得趕忙Out...可是用戶量是一切的基礎,那咱們就開始建立咱們的上帝吧!前端

## 建立數據庫

數據庫的部分,我在這裏就很少講了,你們須要的話能夠直接去傳送門 抓取腳本expensive-shop.sql.java

## 生成UserMapper

參考上節內容:傳送門git

## 編寫業務邏輯

首先,咱們先來分析一下要註冊一個用戶,咱們系統都須要作哪些動做?
user registergithub

  • validate
    • input string(校驗輸入咱們須要經過兩個角度處理)
      • FrontEnd validsql

        前端校驗是爲了下降咱們服務器端壓力而作的一部分校驗,這部分校驗能夠攔截大多數的錯誤請求。shell

      • Backend valid數據庫

        後端校驗是爲了防止某些不法小夥伴繞開前端從而直接訪問咱們的api形成數據請求服務器錯誤,或者前端小夥伴程序有bug...不管是哪種可能性,都有可能形成嚴重的後果。後端

    • email & mobile invalidapi

      由於本人沒有追求email / 短信發送服務器,因此這一步就pass,小夥伴們能夠自行研究哈。

  • control
    • create user

      校驗經過後,就能夠進行建立用戶的動做了。
      接下來,咱們就能夠來實際編碼實現業務了,咱們使用最基本的分層架構,在以前咱們已經經過Mybatis Generator工具生成了基本的pojo,mapper,對於簡單的操做咱們只須要再編寫servicecontroller層就能夠完成咱們的開發工做了。

## 編寫user service

mscx-shop-service中建立com.liferunner.service.IUserService接口,包含2個方法findUserByUserNamecreateUser,以下:

public interface IUserService {

    /**
     * 根據用戶名查詢用戶是否存在
     *
     * @param username
     * @return
     */
    Users findUserByUserName(String username);

    /**
     * 建立用戶
     *
     * @param userRequestDTO 用戶請求dto
     * @return 當前用戶
     */
    Users createUser(UserRequestDTO userRequestDTO) throws Exception;
}

接着,咱們須要具體實現這個接口類,以下:

@Service
@Slf4j
public class UserServiceImpl implements IUserService {
    private final String FACE_IMG = "https://avatars1.githubusercontent.com/u/4083152?s=88&v=4";

    // 構造器注入
    private final UsersMapper usersMapper;
    private final Sid sid;

    @Autowired
    public UserServiceImpl(UsersMapper usersMapper, Sid sid) {
        this.usersMapper = usersMapper;
        this.sid = sid;
    }

    @Override
    public Users findUserByUserName(String username) {
        // 構建查詢條件
        Example example = new Example(Users.class);
        val condition = example.createCriteria()
                .andEqualTo("username", username);
        return this.usersMapper.selectOneByExample(example);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Users createUser(UserRequestDTO userRequestDTO) throws Exception {
        log.info("======begin create user : {}=======", userRequestDTO);
        val user = Users.builder()
                .id(sid.next()) //生成分佈式id
                .username(userRequestDTO.getUsername())
                .password(MD5GeneratorTools.getMD5Str(userRequestDTO.getPassword()))
                .birthday(DateUtils.parseDate("1970-01-01", "yyyy-MM-dd"))
                .nickname(userRequestDTO.getUsername())
                .face(this.FACE_IMG)
                .sex(SexEnum.secret.type)
                .createdTime(new Date())
                .updatedTime(new Date())
                .build();
        this.usersMapper.insertSelective(user);
        log.info("======end create user : {}=======", userRequestDTO);
        return user;
    }
}

這裏有幾處地方有必要說明一下:

UserServiceImpl#findUserByUserName 說明

  • tk.mybatis.mapper.entity.Example 經過使用Example來構建mybatis的查詢參數,若是有多個查詢條件,能夠經過example.createCriteria().addxxx逐一添加。

UserServiceImpl#createUser 說明

  • @Transactional(propagation = Propagation.REQUIRED),開啓事務,選擇事務傳播級別爲REQUIRED,表示必需要有一個事務存在,若是調用者不存在事務,那本方法就本身開啓一個新的事物,若是調用方自己存在一個活躍的事務,那本方法就加入到它裏面(同生共死)。
  • org.n3r.idworker.Sid, 這個是一個開源的 分佈式ID生成器組件,傳送門, 後期有機會的話,會專門寫一個id生成器文章。
  • MD5GeneratorTools 是用來對數據進行MD5加密的工具類,你們能夠在源碼中下載。也能夠直接使用java.security.MessageDigest 直接加密實現,總之密碼不能明文存儲就好了。
  • SexEnum 這個是一個表述性別類型的枚舉,在咱們編碼的規範中,儘可能要求不要出現Magic number,就是開發界常說的魔術數字(即1,2,300...)
  • 這裏的日誌打印,可能有人會問爲何你沒有聲明相似:private final static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); ,這是由於咱們在開始的時候,咱們引入了lombok依賴,不記得的同窗能夠參考傳送門。在這裏依賴中,它繼承了不少的日誌組件,咱們只須要使用一個註解lombok.extern.slf4j.Slf4j來開啓日誌,使用log.info..就能夠了。
  • UserRequestDTO 又是個什麼鬼?在咱們開發的過程當中,極可能會有大批量的參數須要傳遞,這時咱們若是使用xxx#(String aa,Integer bb,Boolean cc...)會讓咱們煩不勝數,並且看着也不美觀,這時候咱們就能夠選擇建立一個新對象來幫助咱們傳遞數據,那麼也就是咱們的UserRequestDTO對象,所謂的DTO就是Data Transfer Object的首字母縮寫,顧名思義,它是用來傳遞數據對象用的。

## 編寫user controller

一樣在mscx-shop-api中,建立com.liferunner.api.controller.UserController,實現用戶建立。

@RestController
@RequestMapping(name = "/users")
@Slf4j
@Api(tags="用戶管理")
public class UserController {

    @Autowired
    private IUserService userService;

    @ApiOperation("校驗是否重名")
    @GetMapping("/validateUsername")
    public JsonResponse validateUsername(@RequestParam String username) {
        // 判斷用戶名是否非法
        if (StringUtils.isBlank(username))
            return JsonResponse.errorMsg("用戶名不能爲空!");
        if (null != userService.findUserByUserName(username))
            return JsonResponse.errorMsg("用戶名已存在!");
        // 用戶名可用
        return JsonResponse.ok();
    }

    @ApiOperation("建立用戶")
    @PostMapping("/create")
    public JsonResponse createUser(@RequestBody UserRequestDTO userRequestDTO) {
        try {
            if (StringUtils.isBlank(userRequestDTO.getUsername()))
                return JsonResponse.errorMsg("用戶名不能爲空");
            if (null != this.userService.findUserByUserName(userRequestDTO.getUsername())) {
                return JsonResponse.errorMsg("用戶名已存在!");
            }
            if (StringUtils.isBlank(userRequestDTO.getPassword()) ||
                    StringUtils.isBlank(userRequestDTO.getConfimPassword()) ||
                    userRequestDTO.getPassword().length() < 8) {
                return JsonResponse.errorMsg("密碼爲空或長度小於8位");
            }
            if (!userRequestDTO.getPassword().equals(userRequestDTO.getConfimPassword()))
                return JsonResponse.errorMsg("兩次密碼不一致!");
            val user = this.userService.createUser(userRequestDTO);
            if (null != user)
                return JsonResponse.ok(user);
        } catch (Exception e) {
            log.error("建立用戶失敗,{}", userRequestDTO);
        }
        return JsonResponse.errorMsg("建立用戶失敗");
    }
}

UserController#validateUsername(username) 說明

  • JsonResponse對象是爲了方便返回給客戶端一個統一的格式而封裝的數據對象。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JsonResponse {

    // 定義jackson對象
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 響應業務狀態
    private Integer status;
    // 響應消息
    private String message;
    // 響應中的數據
    private Object data;

    public static JsonResponse build(Integer status, String msg, Object data) {
        return new JsonResponse(status, msg, data);
    }

    public static JsonResponse ok(Object data) {
        return new JsonResponse(data);
    }

    public static JsonResponse ok() {
        return new JsonResponse(null);
    }

    public static JsonResponse errorMsg(String msg) {
        return new JsonResponse(500, msg, null);
    }

    public static JsonResponse errorMap(Object data) {
        return new JsonResponse(501, "error", data);
    }

    public static JsonResponse errorTokenMsg(String msg) {
        return new JsonResponse(502, msg, null);
    }

    public static JsonResponse errorException(String msg) {
        return new JsonResponse(555, msg, null);
    }

    public static JsonResponse errorUserQQ(String msg) {
        return new JsonResponse(556, msg, null);
    }

    public JsonResponse(Object data) {
        this.status = 200;
        this.message = "OK";
        this.data = data;
    }

    public Boolean isOK() {
        return this.status == 200;
    }
}

UserController#createUser(UserRequestDTO) 說明

  • 如上文所講,須要先作各類校驗
  • 成功則返回JsonResponse
  • 細心的同窗可能看到了上文中有幾個註解@Api(tags="用戶管理"),@ApiOperation("建立用戶"),這個是Swagger 的註解,咱們會在下一節和你們詳細探討,以及如何生成off-line docs

測試API


在咱們每次修改完成以後,都儘量的mvn clean install一次,由於咱們隸屬不一樣的project,若是不從新安裝一次,偶爾遇到的問題會讓人懷疑人生的。

...
[INFO] expensive-shop ..................................... SUCCESS [  1.220 s]
[INFO] mscx-shop-common ................................... SUCCESS [  9.440 s]
[INFO] mscx-shop-pojo ..................................... SUCCESS [  2.020 s]
[INFO] mscx-shop-mapper ................................... SUCCESS [  1.564 s]
[INFO] mscx-shop-service .................................. SUCCESS [  1.366 s]
[INFO] mscx-shop-api ...................................... SUCCESS [  4.614 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  20.739 s
[INFO] Finished at: 2019-11-06T14:53:55+08:00
[INFO] ------------------------------------------------------------------------

當看到上述運行結果以後,就能夠啓動咱們的應用就行測試啦~

UserController#validateUsername(username) 測試

測試API的方式有不少種,好比curl localhost:8080/validateUsername,在好比使用超級流行的Postman也是徹底ok的,我這裏用的是以前在第一篇中和你們所說的一個插件Restful Toolkit(能夠實現和postman同樣的簡單效果,同時還能幫助咱們生成一部分測試信息),當咱們應用啓動以後,效果以下圖,
rest plugin

咱們能夠看到,插件幫咱們生成了幾個測試方法,好比咱們點擊validateUsername,下方就會生成當前方法是一個包含username參數的GET方法,demoData是插件默認給咱們生成的測試數據。能夠隨意修改。
點擊Send:
result
能夠看到請求成功了,而且返回咱們自定義的JSON格式數據。

UserController#createUser(UserRequestDTO) 測試

接着咱們繼續測試用戶註冊接口,請求以下:
send
能夠看到,當咱們選擇create方法時,插件自動幫咱們設置請求類型爲POST,而且RequestBody的默認值也幫助咱們生成了,我只修改了默認的usernamepassword值,confimPassword的默認值我沒有變更,那按照咱們的校驗邏輯,它應該返回的是return JsonResponse.errorMsg("兩次密碼不一致!");這一行,點擊Send:
result
修改confimPassword12345678,點擊Send:
result2
能夠看到,建立用戶成功,而且將當前建立的用戶返回到了咱們請求客戶端。那麼咱們繼續重複點擊建立,會怎麼樣呢?繼續Send:
result3
能夠看到,咱們的驗證重複用戶也已經生效啦。

下節預告


下一節咱們將學習如何使用Swagger自動生成API接口文檔給前端,以及若是沒有外部網絡的狀況下,或者須要和第三方平臺對接的時候,咱們如何生成離線文檔給到第三方。 gogogo!

相關文章
相關標籤/搜索