业务流程
- 用户注册服务
- 用户登录服务
- 用户发表评论
- 用户回复评论
-
模型
在model包下根据下图UML类图创建模型。
User里有个pwd,我们知道密码是不能够泄露的,需要过滤掉这个属性。使用jackson注解@JsonSerialize(using = NullSerializer.class)private String pwd;
API返回JSON结果时就不存在这个字段了。
格式化时间:@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime gmtCreated;@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime gmtModified;
用户注册服务
注册一个账户是根据用户名,密码来注册,这个注册行为可能会失败,比如用户名重复,密码太简单等。为了能够正确描述返回数据,我们新增一个Result公共模型。用于处理API返回值,类似之前的Paging。 ```java package com.lzx.comment.model;
import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data;
import java.io.Serializable;
/**
- @ClassName Result
- @Author 刘正星
@Date 2020/7/16 13:11 **/ @Data public class Result
implements Serializable { @JsonProperty(“isSuccess”) private boolean success = false; private String code; private String message;
private D data; }
`@JsonProperty("isSuccess")`这个注解用于自定义JSON输出的时候字段名称。效果如下:<br /><br />这些JSON技巧要记住,比较常用。<br />改造getAll()```java@GetMapping("/users")@ResponseBodyprivate Result<Paging<UserDO>> getAll(@RequestParam(value = "pageNum" ,defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize",defaultValue = "15") Integer pageSize) {Result<Paging<UserDO>> result = new Result<>();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;}
我们发现 这里的 code,和 message 字段为 null,还是输出到 JSON 中去了,这样很浪费流量,我们需要把null的字段过滤掉,还有一些JSON的配置可以集中优化一下。
在application.properties文件中:
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.default-property-inclusion=non_null
上述配置作用:
import com.lzx.comment.model.Result; import com.lzx.comment.model.User;
/**
- @InterfaceName UserService
- @Author 刘正星
- @Date 2020/7/16 13:26
/
public interface UserService {
/
- 用户注册
- @param userName
- @param pwd
- @return
*/
public Result
register(String userName,String pwd); }
返回值类型设置为 Result<User> 为了传递错误信息,如果创建失败,就可以通过Result 模型的 isSuccess来确定,而错误信息可以通过code 和message属性来获取。
<a name="Pkx72"></a>
### 实现类
注册逻辑<br />
<a name="QaUDy"></a>
#### 补充
- **判断非空非null**
企业中使用 commons-lang3库来处理字段串:
```xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
在豆瓣APP项目中也用过~
- 加密
使用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();
包路劲
org.apache.commons.codec.digest.DigestUtils
package com.lzx.comment.service.impl;
import com.lzx.comment.service.UserService;
import com.lzx.comment.dao.UserDAO;
import com.lzx.comment.dataobject.UserDO;
import com.lzx.comment.model.Result;
import com.lzx.comment.model.User;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @ClassName UserServiceImpl
* @Author 刘正星
* @Date 2020/7/16 13:32
**/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Override
public Result<User> register(String userName, String pwd) {
Result<User> result = new Result<>();
//用户名为空
if (StringUtils.isEmpty(userName)){
result.setCode("600");
result.setMessage("用户名不能为空!");
}else{
//用户名不为空,密码为空
if (StringUtils.isEmpty(pwd)){
result.setCode("601");
result.setMessage("密码不能为空");
}else {
//用户名不为空,密码不为空
//判断用户是否存在
UserDO userDO = userDAO.findByUserName(userName);
//用户不存在
if (userDO == null){
String saltPwd = pwd+"_lzx0203";
String md5Pwd = DigestUtils.md5Hex(saltPwd).toUpperCase();
userDO = new UserDO();
userDO.setUserName(userName);
userDO.setNickName(userName);
userDO.setPwd(md5Pwd);
//储存用户记录
userDAO.add(userDO);
result.setSuccess(true);
//将 UserDO 对象转化为 User 对象
User user = new User();
user.setId(userDO.getId());
user.setUserName(userDO.getUserName());
user.setNickName(userDO.getNickName());
result.setData(user);
}else{
//用户名已经存在
result.setCode("602");
result.setMessage("用户名已经存在");
}
}
}
return result;
}
}
思考:注册逻辑答案只用 if 去判断,相比于自己用的 if - else两者有什么区别?
- if-else 多层嵌套可阅读性较差。
- 只用if,如果有一个if满足,直接return,下面的代码不会在执行。
开发UserAPI
企业开发模式
设计领域模型——开发基础DAO——开发Service——开发API
package com.lzx.comment.api;
import com.lzx.comment.model.Result;
import com.lzx.comment.model.User;
import com.lzx.comment.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @ClassName UserAPI
* @Author 刘正星
* @Date 2020/7/16 14:31
**/
@Controller
public class UserAPI {
@Autowired
private UserService userService;
@PostMapping("/api/user/reg")
@ResponseBody
public Result<User> register(@RequestParam("userName")String userName,@RequestParam("pwd")String pwd){
return userService.register(userName,pwd);
}
}
用户登录服务
用户可能很多,所以一般我们把userId(user表的id值)存储在session里,竟可能节省session空间。
接口:
/**
* 用户登录
* @param userName
* @param pwd
* @return
*/
public Result<User> login(String userName,String pwd);
登录逻辑
实现类:
@Override
public Result<User> login(String userName, String pwd) {
Result<User> result = new Result<>();
if (StringUtils.isEmpty(userName)){
result.setSuccess(false);
result.setCode("600");
result.setMessage("用户名不能为空");
return result;
}
if (StringUtils.isEmpty(pwd)){
result.setCode("601");
result.setSuccess(false);
result.setMessage("密码不能为空");
return result;
}
UserDO userDO = userDAO.findByUserName(userName);
if (userDO == null){
result.setCode("602");
result.setMessage("用户名不存在");
return result;
}
String saltPwd = pwd +"_lzx0203";
String md5Pwd = DigestUtils.md5Hex(saltPwd).toUpperCase();
if (!md5Pwd.equals(userDO.getPwd())){
result.setCode("603");
result.setMessage("密码不对");
return result;
}
User user = userDO.toModel();
result.setSuccess(true);
result.setData(user);
return result;
}
API:注意保存userId到session中。
@PostMapping("/api/user/login")
@ResponseBody
public Result<User> login(@RequestParam("userName")String userName, @RequestParam("pwd")String pwd, HttpServletRequest request, HttpServletResponse response){
Result<User> result = userService.login(userName,pwd);
if (result.isSuccess()){
request.getSession().setAttribute("userId",result.getData().getId());
}
return result;
}
用户退出
API
@GetMapping ("/api/user/logout")
@ResponseBody
public Result<User> logout(HttpServletRequest request){
request.getSession().removeAttribute("userId");
Result<User> result = new Result<>();
result.setMessage("退出成功");
result.setSuccess(true);
return result;
}
优化DO和Model互转
// 将 UserDO 对象转化为 User 对象
User user = new User();
user.setId(userDO.getId());
user.setUserName(userDO.getUserName());
user.setNickName(userDO.getNickName());
user.setAvatar(userDO.getAvatar());
user.setGmtCreated(userDO.getGmtCreated());
user.setGmtModified(userDO.getGmtModified());
我们一般吧转换代码抽象成公共方法,放在DO对象中,命名为toModel。
import com.youkeda.comment.model.User;
import java.time.LocalDateTime;
public class UserDO {
/**
* DO 转换为 Model
*
* @return
*/
public User toModel() {
User user = new User();
user.setId(getId());
user.setUserName(getUserName());
user.setNickName(getNickName());
user.setAvatar(getAvatar());
user.setGmtCreated(getGmtCreated());
user.setGmtModified(getGmtModified());
return user;
}
}
