5.2 用户注册服务
注册一个账户主要是根据用户名、密码来注册的,用户注册这个行为有可能会失败,比如用户名重复、密码太简单等等。为了能够正确的描述返回数据,我们新增一个 Result 公共(通用)模型,用于处理 API 返回值,类似之前的 Paging 一样
通用返回模型
package com.youkeda.comment.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
/**
 * JSON 返回模型
 */
public class Result<D> implements Serializable {
    // 表示执行成功或失败
    @JsonProperty("isSuccess")
    private boolean success = false;
    // 返回消息短码,一般用于出错时,简短描述错误
    private String code;
    // 返回消息具体信息,一般用于出错时,比较详细的描述错误
    private String message;
    // 返回的具体数据
    private D data;
    // 省略 getter、setter
}
在企业级项目中,code 可以规定为一些错误枚举值。例如 “602” 表示 “用户名已存在”,在企业内部形成约定、大家都能理解就可以了。
上面的 Result 模型,我们在 success 字段上增加了一个注解@JsonProperty("isSuccess"),这个用于自定义 JSON 输出的时候的字段名称,待会看一下演示结果,感受一下
这些 JSON 的技巧请大家记住哦,比较常用的
泛型
这里哪里体现出公共、通用的含义呢?
特点就是泛型。
泛型实际上大家经常用到,例如很常见的 List 集合就支持泛型。
List<String> strings = new ArrayList<>();表示 strings 集合只能容纳字符串元素,而List<User> users = new ArrayList<>();表示 users 集合只能容纳用户实例元素。于是 Java 的集合是通用的,可以容纳任意对象,但通过泛型,程序明确的知道,一个集合类中容纳的元素的类型。
如果不使用泛型,集合就可以放入任意对象:
List listdata表示 listdata 集合可以同时放入“字符串”和“用户实例”,容易造成混乱,此时listdata.get(0)你也不知道获取的具体是什么类型的对象
同理,通过 Result<D> 的声明,让返回模型支持泛型(类名后书写尖括号<D>是声明泛型的语法),此时声明的属性 D data 是不确定什么类型的。
稍微有点抽象了,大家要开动脑筋理解
但是没有关系,大家不要焦躁。在实际使用 Result 进行实例化对象时,就可以确定 data 属性的类型了:
Result<User> result = new Result<>();
尖括号中不再是抽象的 D 了,而是具体的 User ,那么就表示 result 实例对象的 data 的属性的类型就是 User 。
如果 Result 不是用于用户注册,而是用于其它项目场景,例如:
Result<String> result = new Result<>();
就表示 result 实例对象的 data 的属性的类型就是“字符串”,返回了一个简单的字符串数据。
所以 Result 返回值类是公共的、做到了通用性。
声明泛型的语法
<D>其中字母 D 并不是硬性规定,你可以随意用其它 大写字母 ,一般不用单词(为了简短)。只是习惯上用了 data 属性的首字母 D 而已
getAll() 方法改造
我们改造一下 UserController.getAll() 方法看看
package com.youkeda.comment.control;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.youkeda.comment.dao.UserDAO;
import com.youkeda.comment.dataobject.UserDO;
import com.youkeda.comment.model.Paging;
import com.youkeda.comment.model.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
@Controller
public class UserController {
    @Autowired
    private UserDAO userDAO;
    @GetMapping("/users")
    @ResponseBody
    public Result<Paging<UserDO>> getAll(@RequestParam(value = "pageNum", required = false) Integer pageNum,
                                         @RequestParam(value = "pageSize", required = false) Integer pageSize) {
        Result<Paging<UserDO>> result = new Result();
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 15;
        }
        // 设置当前页数为1,以及每页3条记录
        Page<UserDO> page = PageHelper.startPage(pageNum, pageSize).doSelectPage(() -> userDAO.findAll());
        result.setSuccess(true);
        result.setData(
            new Paging<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), page.getResult()));
        return result;
    }
}
我想你应该看到了如上的数据,Result 的 success 属性输出 JSON 的时候变成了 isSuccess,符合我们的预期。
这里还有一个新的问题,code、message 字段为null,但是还是输出到 JSON 中去了,这样很浪费流量。我们需要把 null 字段的 JSON 给直接过滤掉,还有一些 JSON 的配置可以集中优化一下,打开 application.properties 文件,新增如下配置
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.default-property-inclusion=non_null
上面2个配置分别的作用是:
- 允许序列化未知的字段,可以兼容 Java 模型和 JSON 数据不一致的情况
- 忽略 null 字段
代码演示:
style.youkeda.com/coursevideo/j6/j6-5-2-1_play.mp4
{
  "code": null,
  "message": null,
  "data": {},
  "isSuccess": true
}
- 作业2
完成了模型创建后,我们开始分步骤开始开发服务,首先我们完成用户注册服务
我们需要创建一个 UserService 接口,完整的路径是
com.youkeda.comment.service.UserService
注册一个账户主要是根据用户名、密码来注册的,我们创建一个 register 方法
package com.youkeda.comment.service;
import com.youkeda.comment.model.Result;
import com.youkeda.comment.model.User;
public interface UserService {
    /**
     * 注册用户
     * @param userName
     * @param pwd
     * @return
     */
    public Result<User> register(String userName, String pwd);
}
这里,我们把返回类型设置为 Result<User> 这是为了传递错误信息,如果创建失败,就可以通过 Result 模型的 isSuccess 来确定啦,而错误信息可以通过 code 和 message 属性来获取
根据前面的提示请完成 UserService 的创建
- 作业3
 现在需要我们完成 UserService 接口的实现类
package com.youkeda.comment.service.impl;
import com.youkeda.comment.model.Result;
import com.youkeda.comment.model.User;
import com.youkeda.comment.service.UserService;
import org.springframework.stereotype.Component;
@Component
public class UserServiceImpl implements UserService {
    @Override
    public Result<User> register(String userName, String pwd) {
        return null;
    }
}
UserServiceImpl 是一个 Spring Bean,所以需要添加 @Component 注解
任务要求
- 引入 UserDAO Bean
- 完成 register 逻辑
注册逻辑
补充说明
判断非空非 null
在企业中,我们一般会结合 commons-lang3 库来处理字符串,你需要在 pom.xml 中添加依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.10</version>
</dependency>
完成配置后,可以使用 StringUtils.isEmpty(str) 来判断是否为空,StringUtils 的完整路径是
org.apache.commons.lang3.StringUtils
判断用户名是否存在
我们可以使用 UserDAO.findByUserName 查询数据是否存在,如果存在记录则说明用户名已经存在了
加密
一般来说,我们会使用 md5 算法进行加密,建议使用 commons-codec 库进行加密处理,同样需要先添加依赖
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.14</version>
</dependency>
看一下加密的例子:
// 密码加自定义盐值,确保密码安全
String saltPwd = pwd + "_ykd2050";
// 生成md5值,并转为大写字母
String md5Pwd = DigestUtils.md5Hex(saltPwd).toUpperCase();
DigestUtils 的完整包路径是
org.apache.commons.codec.digest.DigestUtils
Result 返回处理
如果当我们执行成功的时候,我们需要正确的返回 Result 对象,我们需要
Result<User> result = new Result<>();
UserDO userDO1 = new UserDO();
userDO1.setUserName(userName);
//初始昵称等于用户名
userDO1.setNickName(userName);
userDO1.setPwd(md5Pwd);
userDAO.add(userDO1);
//设置执行成功
result.setSuccess(true);
//将 UserDO 对象转化为 User 对象
User user = new User();
user.setId(userDO1.getId());
user.setUserName(userDO1.getUserName());
user.setNickName(userDO1.getNickName());
result.setData(user);
return result;
由于我们在register方法定义的返回模型是 Result<User> ,所以API层是完全感知不到 UserDO 模型的,这就需要我们手工转化一下对象类型
DO 到 Model 的互转在 Java 工程是最常见的行为,大家习惯就好,这也是 Java 语言不太优雅的地方,相比其他语言繁琐了点,有利有弊
完整的业务逻辑还是比较复杂的,大家请仔细思考完成本方法的业务逻辑哦

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号