tbUsers = Lists.newArrayList();
tbUsers.add(tbUser1);
tbUsers.add(tbUser2);
return BaseResultFactory.getInstance().build(request.getRequestURI(), 2, 10, tbUsers);
}
}
```

---
结果
```
http://localhost:9501/test/records/
```

```
http://localhost:9501/test/records/1
```

```
http://localhost:9501/test/records/2
```

---
设置 Json 不返回 null 字段
```java
@JsonInclude(JsonInclude.Include.NON_NULL)
```
附:SpringMVC 返回状态码
```java
response.setHeader("Content-Type", "application/vnd.api+json");
response.setStatus(500);
```
----
# 完善用户注册服务
## 1.添加表单验证
在myshop-common-domain项目中
```xml
org.hibernate.validator
hibernate-validator
```

通用验证,验证实体类
----
在myshop-common-domain项目中添加spring的相关依赖
```xml
org.springframework
spring-context
```
---

--BeanValidator
```java
package com.funtl.myshop.commons.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* JSR303 Validator(Hibernate Validator)工具类.
*
* ConstraintViolation 中包含 propertyPath, message 和 invalidValue 等信息.
* 提供了各种 convert 方法,适合不同的 i18n 需求:
* 1. List, String 内容为 message
* 2. List, String 内容为 propertyPath + separator + message
* 3. Map
*
* 详情见wiki: https://github.com/springside/springside4/wiki/HibernateValidator
*
*
Title: BeanValidator
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2018/6/26 17:21
*/
@Component
public class BeanValidator {
@Autowired
private Validator validatorInstance;
private static Validator validator;
@PostConstruct
public void init() {
BeanValidator.validator = validatorInstance;
}
/**
* 调用 JSR303 的 validate 方法, 验证失败时抛出 ConstraintViolationException.
*/
private static void validateWithException(Validator validator, Object object, Class>... groups) throws ConstraintViolationException {
Set constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set 中为 List.
*/
private static List extractMessage(ConstraintViolationException e) {
return extractMessage(e.getConstraintViolations());
}
/**
* 辅助方法, 转换 Set 为 List
*/
private static List extractMessage(Set extends ConstraintViolation> constraintViolations) {
List errorMessages = new ArrayList<>();
for (ConstraintViolation violation : constraintViolations) {
errorMessages.add(violation.getMessage());
}
return errorMessages;
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set 为 Map.
*/
private static Map extractPropertyAndMessage(ConstraintViolationException e) {
return extractPropertyAndMessage(e.getConstraintViolations());
}
/**
* 辅助方法, 转换 Set 为 Map.
*/
private static Map extractPropertyAndMessage(Set extends ConstraintViolation> constraintViolations) {
Map errorMessages = new HashMap<>();
for (ConstraintViolation violation : constraintViolations) {
errorMessages.put(violation.getPropertyPath().toString(), violation.getMessage());
}
return errorMessages;
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set 为 List.
*/
private static List extractPropertyAndMessageAsList(ConstraintViolationException e) {
return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");
}
/**
* 辅助方法, 转换 Set 为 List.
*/
private static List extractPropertyAndMessageAsList(Set extends ConstraintViolation> constraintViolations) {
return extractPropertyAndMessageAsList(constraintViolations, " ");
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set 为 List.
*/
private static List extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {
return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);
}
/**
* 辅助方法, 转换 Set 为 List.
*/
private static List extractPropertyAndMessageAsList(Set extends ConstraintViolation> constraintViolations, String separator) {
List errorMessages = new ArrayList<>();
for (ConstraintViolation violation : constraintViolations) {
errorMessages.add(violation.getPropertyPath() + separator + violation.getMessage());
}
return errorMessages;
}
/**
* 服务端参数有效性验证
*
* @param object 验证的实体对象
* @param groups 验证组
* @return 验证成功:返回 null;验证失败:返回错误信息
*/
public static String validator(Object object, Class>... groups) {
try {
validateWithException(validator, object, groups);
} catch (ConstraintViolationException ex) {
List list = extractMessage(ex);
list.add(0, "数据验证失败:");
// 封装错误消息为字符串
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
String exMsg = list.get(i);
if (i != 0) {
sb.append(String.format("%s. %s", i, exMsg)).append(list.size() > 1 ? "
" : "");
} else {
sb.append(exMsg).append(list.size() > 1 ? "
" : "");
}
}
return sb.toString();
}
return null;
}
}
```
需加注解@Component,这样才能被spring扫描到

---
在myshop-commons添加工具类RegexpUtils
```java
package com.funtl.myshop.commons.utils;
/**
* 正则表达式工具类
* Title: RegexpUtils
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2018/6/16 23:48
*/
public class RegexpUtils {
/**
* 验证手机号
*/
public static final String PHONE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
/**
* 验证邮箱地址
*/
public static final String EMAIL = "\\w+(\\.\\w)*@\\w+(\\.\\w{2,3}){1,3}";
/**
* 验证手机号
* @param phone
* @return
*/
public static boolean checkPhone(String phone) {
return phone.matches(PHONE);
}
/**
* 验证邮箱
* @param email
* @return
*/
public static boolean checkEmail(String email) {
return email.matches(EMAIL);
}
}
```

----
### 修改实体类
修改实体类,增加验证注解,以后我们只需要在实体类的属性上使用 JSR-303 注解即可完成相关数据的验证工作,关键代码如下:
```java
@Length(min = 6, max = 20, message = "用户名长度必须介于 6 和 20 之间")
private String username;
@Length(min = 6, max = 20, message = "密码长度必须介于 6 和 20 之间")
private String password;
@Pattern(regexp = RegexpUtils.PHONE, message = "手机号格式不正确")
private String phone;
@Pattern(regexp = RegexpUtils.EMAIL, message = "邮箱格式不正确")
private String email;
```
用TbUser类测试

---
给MyShopServiceRegApplication添加注解
```
@SpringBootApplication(scanBasePackages = "com.funtl.myshop")
```

---
controller
```java
package com.funtl.myshop.service.reg.controller;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import com.funtl.myshop.commons.mapper.TbUserMapper;
import com.funtl.myshop.commons.validator.BeanValidator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "reg")
public class RegController {
@Autowired
private TbUserMapper tbUserMapper;
@Autowired
private ConfigurableApplicationContext applicationContext;
@PostMapping(value = "")
public AbstractBaseResult reg(TbUser tbUser) {
String messsage = BeanValidator.validator(tbUser);
if (StringUtils.isNoneBlank(messsage)) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,null,applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
return null;
}
}
```
---
启动验证
```
http://localhost:9501/reg
```

---
## 2.通用的业务逻辑(重构)

### BaseCrudService
```java
package com.funtl.myshop.commons.service;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
import com.github.pagehelper.PageInfo;
/**
* 通用的业务逻辑
* Title: BaseCrudService
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/25 9:43
*/
public interface BaseCrudService {
/**
* 查询属性值是否唯一
*
* @param property
* @param value
* @return true/唯一,false/不唯一
*/
default boolean unique(String property, String value) {
return false;
}
/**
* 保存
*
* @param domain
* @return
*/
default T save(T domain) {
return null;
}
/**
* 分页查询
* @param domain
* @param pageNum
* @param pageSize
* @return
*/
default PageInfo page(T domain, int pageNum, int pageSize) {
return null;
}
}
```
### BaseCrudServiceImpl
```java
package com.funtl.myshop.commons.service.impl;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
import com.funtl.myshop.commons.service.BaseCrudService;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.MyMapper;
import tk.mybatis.mapper.entity.Example;
import java.lang.reflect.ParameterizedType;
public class BaseCrudServiceImpl> implements BaseCrudService {
@Autowired
protected M mapper;
//实例化泛型,获取泛型的class
private Class entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
@Override
public boolean unique(String property, String value) {
Example example = new Example(entityClass);
example.createCriteria().andEqualTo(property, value);
int result = mapper.selectCountByExample(example);
if (result > 0) {
return false;
}
return true;
}
}
```
---
BaseCrudService是通用的业务逻辑,TbUserService继承了它
### TbUserService
```java
package com.funtl.myshop.commons.service;
import com.funtl.myshop.commons.domain.TbUser;
public interface TbUserService extends BaseCrudService {
}
```
### TbUserServiceImpl
```java
package com.funtl.myshop.commons.service.impl;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.mapper.TbUserMapper;
import com.funtl.myshop.commons.service.TbUserService;
import org.springframework.stereotype.Service;
@Service
public class TbUserServiceImpl extends BaseCrudServiceImpl implements TbUserService {
}
```

---
## 3.验证表单
### RegController
```java
package com.funtl.myshop.service.reg.controller;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import com.funtl.myshop.commons.mapper.TbUserMapper;
import com.funtl.myshop.commons.service.TbUserService;
import com.funtl.myshop.commons.validator.BeanValidator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import tk.mybatis.mapper.entity.Example;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping(value = "reg")
public class RegController {
@Autowired
private TbUserService tbUserService;
@Autowired
private ConfigurableApplicationContext applicationContext;
@PostMapping(value = "")
public AbstractBaseResult reg(TbUser tbUser) {
//数据校验
String messsage = BeanValidator.validator(tbUser);
if (StringUtils.isNoneBlank(messsage)) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,null,applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
// 验证用户名是否重复
if (!tbUserService.unique("username", tbUser.getUsername())) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,"用户名重复",applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
// 验证邮箱是否重复
if (!tbUserService.unique("email", tbUser.getEmail())) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,"邮箱重复",applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
return null;
}
}
```

---
### 验证
启动application

改进
```
HttpServletResponse response,
response.setStatus(HttpStatus.UNAUTHORIZED.value());
```

```java
package com.funtl.myshop.service.reg.controller;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import com.funtl.myshop.commons.mapper.TbUserMapper;
import com.funtl.myshop.commons.service.TbUserService;
import com.funtl.myshop.commons.validator.BeanValidator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import tk.mybatis.mapper.entity.Example;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping(value = "reg")
public class RegController {
@Autowired
private TbUserService tbUserService;
@Autowired
private ConfigurableApplicationContext applicationContext;
@PostMapping(value = "")
public AbstractBaseResult reg(HttpServletResponse response,TbUser tbUser) {
//数据校验
String messsage = BeanValidator.validator(tbUser);
if (StringUtils.isNoneBlank(messsage)) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,null,applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
// 验证用户名是否重复
if (!tbUserService.unique("username", tbUser.getUsername())) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,"用户名重复",applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
// 验证邮箱是否重复
if (!tbUserService.unique("email", tbUser.getEmail())) {
return BaseResultFactory.getInstance().build(HttpStatus.UNAUTHORIZED.value(),messsage,"邮箱重复",applicationContext.getEnvironment().getProperty("logging.level.com.funtl.myshop"));
}
return null;
}
}
```
重新运行

---
## 4.重构
### 添加依赖
```xml
org.apache.tomcat.embed
tomcat-embed-core
provided
```
添加这个依赖,是为了使用HttpServletRequest
```xml
org.springframework
spring-webmvc
provided
```
添加这个依赖,是为了使用注解
---
### BaseResultFactory
```java
package com.funtl.myshop.commons.dto;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 通用响应结构工厂
* Title: BaseResultFactory
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/23 15:16
*/
public class BaseResultFactory {
/**
* 设置日志级别,用于限制发生错误时,是否显示调试信息(detail)
*
*/
private static final String LOGGER_LEVEL_DEBUG = "DEBUG";
//下面是单例模块,目的是实例是点出来,而不是new出来
private static BaseResultFactory baseResultFactory;
private BaseResultFactory() {
}
// 设置通用的响应
private static HttpServletResponse response;
//单例模式
public static BaseResultFactory getInstance(HttpServletResponse response) {
if (baseResultFactory == null) {
synchronized (BaseResultFactory.class) {
if (baseResultFactory == null) {
baseResultFactory = new BaseResultFactory();
}
}
}
BaseResultFactory.response = response;
// 设置通用响应
baseResultFactory.initResponse();
return baseResultFactory;
}
/**
* 构建单笔数据结果集
*
* @param self
* @return
*/
public AbstractBaseResult build(String self, T attributes) {
return new SuccessResult(self, attributes);
}
/**
* 构建多笔数据结果集
*
* @param self
* @param next
* @param last
* @return
*/
public AbstractBaseResult build(String self, int next, int last, List attributes) {
return new SuccessResult(self, next, last, attributes);
}
/**
* 构建请求错误的响应结构
*
* @param code
* @param title
* @param detail
* @param level 日志级别,只有 DEBUG 时才显示详情
* @return
*/
public AbstractBaseResult build(int code, String title, String detail, String level) {
// 设置请求失败的响应码
response.setStatus(code);
if (LOGGER_LEVEL_DEBUG.equals(level)) {
return new ErrorResult(code, title, detail);
} else {
return new ErrorResult(code, title, null);
}
}
/**
* 初始化 HttpServletResponse
*/
private void initResponse() {
// 需要符合 JSON API 规范
response.setHeader("Content-Type", "application/vnd.api+json");
}
}
```

---
### AbstractBaseController
```java
package com.funtl.myshop.commons.web;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ModelAttribute;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 通用的控制器
* Title: AbstractBaseController
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/25 11:11
*/
public abstract class AbstractBaseController {
// 用于动态获取配置文件的属性值
private static final String ENVIRONMENT_LOGGING_LEVEL_MY_SHOP = "logging.level.com.funtl.myshop";
/**
* protected HttpServletRequest request; 定义了一个 request 成员变量,目的是希望减少代码上的参数传递,使代码看上去简洁些,
* 确无意间留下了可能的 线程安全 隐患;解决方法是在该成员变量上增加 @Resource 注解;区别在于有注解时创建的 HttpServletRequest 里
* 绑定的 RequestAttributes 使用了 ThreadLocal
* (作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性)
*/
@Resource
protected HttpServletRequest request;
@Resource
protected HttpServletResponse response;
@Autowired
private ConfigurableApplicationContext applicationContext;
//@ModelAttribute注释的方法会在此controller每个方法执行前被执行
@ModelAttribute
public void initReqAndRes(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
/**
* 请求成功
* @param self
* @param attribute
* @return
*/
protected AbstractBaseResult success(String self, T attribute) {
return BaseResultFactory.getInstance(response).build(self, attribute);
}
/**
* 请求成功
* @param self
* @param next
* @param last
* @param attributes
* @return
*/
protected AbstractBaseResult success(String self, int next, int last, List attributes) {
return BaseResultFactory.getInstance(response).build(self, next, last, attributes);
}
/**
* 请求失败
* @param title
* @param detail
* @return
*/
protected AbstractBaseResult error(String title, String detail) {
return error(HttpStatus.UNAUTHORIZED.value(), title, detail);
}
/**
* 请求失败
* @param code
* @param title
* @param detail
* @return
*/
protected AbstractBaseResult error(int code, String title, String detail) {
return BaseResultFactory.getInstance(response).build(code, title, detail, applicationContext.getEnvironment().getProperty(ENVIRONMENT_LOGGING_LEVEL_MY_SHOP));
}
}
```
---
### RegController
```java
package com.funtl.myshop.service.reg.controller;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import com.funtl.myshop.commons.service.TbUserService;
import com.funtl.myshop.commons.validator.BeanValidator;
import com.funtl.myshop.commons.web.AbstractBaseController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "reg")
public class RegController extends AbstractBaseController {
@Autowired
private TbUserService tbUserService;
@PostMapping(value = "")
public AbstractBaseResult reg(TbUser tbUser) {
//数据校验
String message = BeanValidator.validator(tbUser);
if (StringUtils.isNoneBlank(message)) {
return error(message, null);
}
// 验证用户名是否重复
if (!tbUserService.unique("username", tbUser.getUsername())) {
return error("用户名已存在", null);
}
// 验证邮箱是否重复
if (!tbUserService.unique("email", tbUser.getEmail())) {
return error("邮箱重复,请重试", null);
}
return null;
}
}
```

# 完善用户注册服务2

```java
package com.funtl.myshop.commons.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
import com.funtl.myshop.commons.utils.RegexpUtils;
import org.hibernate.validator.constraints.Length;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@Table(name = "tb_user")
@JsonInclude(JsonInclude.Include.NON_NULL)//json不返回值为null的属性
public class TbUser extends AbstractBaseDomain {
/**
* 用户名
*/
@NotNull(message = "用户名不可为空")
@Length(min = 5, max = 20, message = "用户名长度必须介于 5 和 20 之间")
private String username;
/**
* 密码,加密存储
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 注册邮箱
*/
@NotNull(message = "邮箱不可为空")
@Pattern(regexp = RegexpUtils.EMAIL, message = "邮箱格式不正确")
private String email;
/**
* 获取用户名
*
* @return username - 用户名
*/
public String getUsername() {
return username;
}
/**
* 设置用户名
*
* @param username 用户名
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 获取密码,加密存储
*
* @return password - 密码,加密存储
*/
public String getPassword() {
return password;
}
/**
* 设置密码,加密存储
*
* @param password 密码,加密存储
*/
public void setPassword(String password) {
this.password = password;
}
/**
* 获取注册手机号
*
* @return phone - 注册手机号
*/
public String getPhone() {
return phone;
}
/**
* 设置注册手机号
*
* @param phone 注册手机号
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* 获取注册邮箱
*
* @return email - 注册邮箱
*/
public String getEmail() {
return email;
}
/**
* 设置注册邮箱
*
* @param email 注册邮箱
*/
public void setEmail(String email) {
this.email = email;
}
}
```
## AbstractBaseDomain
```java
package com.funtl.myshop.commons.dto;
import lombok.Data;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
import java.util.Date;
/**
* 通用的领域模型
* Title: AbstractBaseDomain
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/23 15:50
*/
@Data
public abstract class AbstractBaseDomain implements Serializable {
/**
* 该注解需要保留,用于 tk.mybatis 回显 ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//引用了hibernate-jpa-2.1-api包
private Long id;
private Date created;
private Date updated;
}
```

---
依赖
```xml
org.hibernate.javax.persistence
hibernate-jpa-2.1-api
```
> 目的:
>
> ```java
> /** * 该注解需要保留,用于 tk.mybatis 回显 ID */@Id@GeneratedValue(strategy = GenerationType.IDENTITY)//引用了hibernate-jpa-2.1-api包
> ```
---

## BaseCrudService
```java
package com.funtl.myshop.commons.service;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
/**
* 通用的业务逻辑
* Title: BaseCrudService
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/25 9:43
*/
public interface BaseCrudService {
/**
* 查询属性值是否唯一
*
* @param property
* @param value
* @return true/唯一,false/不唯一
*/
default boolean unique(String property, String value) {
return false;
}
/**
* 保存
* @param domain
* @return
*/
default T save(T domain) {
return null;
}
}
```

## BaseCrudServiceImpl
```java
package com.funtl.myshop.commons.service.impl;
import com.funtl.myshop.commons.dto.AbstractBaseDomain;
import com.funtl.myshop.commons.service.BaseCrudService;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.MyMapper;
import tk.mybatis.mapper.entity.Example;
import java.lang.reflect.ParameterizedType;
import java.util.Date;
public class BaseCrudServiceImpl> implements BaseCrudService {
@Autowired
protected M mapper;
//实例化泛型,获取泛型的class
private Class entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
@Override
public boolean unique(String property, String value) {
Example example = new Example(entityClass);
example.createCriteria().andEqualTo(property, value);
int result = mapper.selectCountByExample(example);
if (result > 0) {
return false;
}
return true;
}
@Override
public T save(T domain) {
int result = 0;
Date currentDate = new Date();
domain.setUpdated(currentDate);
// 创建
if (domain.getId() == null) {
domain.setCreated(currentDate);
/**
* 用于自动回显 ID,领域模型中需要 @ID 注解的支持
* {@link AbstractBaseDomain}
*/
result = mapper.insertUseGeneratedKeys(domain);
}
// 更新
else {
result = mapper.updateByPrimaryKey(domain);
}
// 保存数据成功
if (result > 0) {
return domain;
}
// 保存数据失败
return null;
}
}
```
---
## RegController
```java
package com.funtl.myshop.service.reg.controller;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.dto.AbstractBaseResult;
import com.funtl.myshop.commons.dto.BaseResultFactory;
import com.funtl.myshop.commons.service.TbUserService;
import com.funtl.myshop.commons.validator.BeanValidator;
import com.funtl.myshop.commons.web.AbstractBaseController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "reg")
public class RegController extends AbstractBaseController {
@Autowired
private TbUserService tbUserService;
@PostMapping(value = "")
public AbstractBaseResult reg(TbUser tbUser) {
//数据校验
String message = BeanValidator.validator(tbUser);
if (StringUtils.isNoneBlank(message)) {
return error(message, null);
}
// 验证用户名是否重复
if (!tbUserService.unique("username", tbUser.getUsername())) {
return error("用户名已存在", null);
}
// 验证邮箱是否重复
if (!tbUserService.unique("email", tbUser.getEmail())) {
return error("邮箱重复,请重试", null);
}
//注册用户
tbUser.setPassword(DigestUtils.md5DigestAsHex(tbUser.getPassword().getBytes()));
TbUser user = tbUserService.save(tbUser);
if (user != null) {
return success(request.getRequestURI(),user);
}
// 注册失败
return error("注册失败,请重试", null);
}
}
```

---
验证

---
## 优化
### 隐藏密码
使用注解`@JsonIgnore`

---
### 日期格式化
使用注解
```java
/**
* 格式化日期,由于是北京时间(我们是在东八区),所以时区 +8
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
```

---
测试

---
# 发送注册成功邮件
## 1.用户注册服务增加依赖
```xml
org.springframework.cloud
spring-cloud-starter-stream-rocketmq
```
## 2.Application
主要增加了 `@EnableBinding` 和 `@EnableAsync` 注解,其中 `@EnableAsync` 注解用于开启异步调用功能
```java
package com.funtl.myshop.service.reg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.scheduling.annotation.EnableAsync;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication(scanBasePackages = "com.funtl.myshop")
@EnableDiscoveryClient
@MapperScan(basePackages = "com.funtl.myshop.commons.mapper")
@EnableBinding({Source.class})
@EnableAsync //允许异步。开启异步功能
public class MyShopServiceRegApplication {
public static void main(String[] args) {
SpringApplication.run(MyShopServiceRegApplication.class, args);
}
}
```

---
## 3.配置文件
>温馨提示
>
>RocketMQ 配置在 Nacos Config 配置中心会导致无法连接 RocketMQ Server 的问题,故我们还需要在项目中额外配置 `application.yml`
创建 `application.yml` 配置文件
```yaml
spring:
cloud:
stream:
rocketmq:
binder:
namesrv-addr: 192.168.1.18:9876
bindings:
output: {destination: topic-email, content-type: application/json}
```

---
## 4.Service
-RegService
```java
package com.funtl.myshop.service.reg.service;
import com.funtl.myshop.commons.domain.TbUser;
import com.funtl.myshop.commons.utils.MapperUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class RegService {
@Autowired
private MessageChannel output;
@Async //这里变成异步
public void sendEmail(TbUser tbUser) {
try {
output.send(MessageBuilder.withPayload(MapperUtils.obj2json(tbUser)).build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
```

---
## 5.工具类
将对象变为json
MapperUtils

```java
package com.funtl.myshop.commons.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Jackson 工具类
* Title: MapperUtils
* Description:
*
* @author Lusifer
* @version 1.0.0
* @date 2018/3/4 21:50
*/
public class MapperUtils {
private final static ObjectMapper objectMapper = new ObjectMapper();
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* 转换为 JSON 字符串
*
* @param obj
* @return
* @throws Exception
*/
public static String obj2json(Object obj) throws Exception {
return objectMapper.writeValueAsString(obj);
}
/**
* 转换为 JSON 字符串,忽略空值
*
* @param obj
* @return
* @throws Exception
*/
public static String obj2jsonIgnoreNull(Object obj) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.writeValueAsString(obj);
}
/**
* 转换为 JavaBean
*
* @param jsonString
* @param clazz
* @return
* @throws Exception
*/
public static T json2pojo(String jsonString, Class clazz) throws Exception {
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
return objectMapper.readValue(jsonString, clazz);
}
/**
* 字符串转换为 Map
*
* @param jsonString
* @return
* @throws Exception
*/
public static Map json2map(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.readValue(jsonString, Map.class);
}
/**
* 字符串转换为 Map
*/
public static Map json2map(String jsonString, Class clazz) throws Exception {
Map> map = objectMapper.readValue(jsonString, new TypeReference