# 完善用户注册服务
# 概述
以下代码按照项目顺序新增或变更的类与配置文件。
# myshop-commons
# POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cmcc</groupId>
<artifactId>myshop-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../myshop-dependencies/pom.xml</relativePath>
</parent>
<artifactId>myshop-commons</artifactId>
<packaging>jar</packaging>
<name>myshop-commons</name>
<url>http://www.cmcc.com</url>
<inceptionYear>2018-Now</inceptionYear>
<dependencies>
<!-- Spring Begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring End -->
<!-- Apache Begin -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Apache End -->
<!-- Commons Begin -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<!-- Commons End -->
<!-- Projects Begin -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- Projects End -->
</dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# AbstractBaseDomain
package com.cmcc.myshop.commons.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
import java.util.Date;
/**
* 通用的领域模型
* <p>Title: AbstractBaseDomain</p>
* <p>Description: </p>
*
* @author cmcc
* @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)
private Long id;
/**
* 格式化日期,由于是北京时间(我们是在东八区),所以时区 +8
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date created;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updated;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# AbstractBaseResult
package com.cmcc.myshop.commons.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
/**
* 通用的响应结果
* <p>Title: AbstractBaseResult</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/23 15:10
*/
@Data
public abstract class AbstractBaseResult implements Serializable {
/**
* 此为内部类
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
protected static class Links {
private String self;
private String next;
private String last;
}
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
protected static class DataBean<T extends AbstractBaseDomain> {
private String type;
private Long id;
private T attributes;
private T relationships;
private Links links;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# BaseResultFactory
package com.cmcc.myshop.commons.dto;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 通用响应结构工厂
* <p>Title: BaseResultFactory</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/23 15:16
*/
@SuppressWarnings("all")
public class BaseResultFactory<T extends AbstractBaseDomain> {
/**
* 设置日志级别,用于限制发生错误时,是否显示调试信息(detail)
*
* @see ErrorResult#detail
*/
public static final String LOGGER_LEVEL_DEBUG = "DEBUG";
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<T> attributes) {
return new SuccessResult(self, next, last, attributes);
}
/**
* 构建请求错误的响应结构
*
* @param code HTTP 状态码
* @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");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# ErrorResult
package com.cmcc.myshop.commons.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 请求失败
* <p>Title: ErrorResult</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/23 15:07
*/
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
// JSON 不显示为 null 的属性
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResult extends AbstractBaseResult {
private int code;
private String title;
/**
* 调试信息
*/
private String detail;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# SuccessResult
package com.cmcc.myshop.commons.dto;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* 请求成功
* <p>Title: SuccessResult</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/23 15:07
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class SuccessResult<T extends AbstractBaseDomain> extends AbstractBaseResult {
private Links links;
private List<DataBean> data;
/**
* 请求的结果(单笔)
* @param self 当前请求路径
* @param attributes 领域模型
*/
public SuccessResult(String self, T attributes) {
links = new Links();
links.setSelf(self);
createDataBean(null, attributes);
}
/**
* 请求的结果(分页)
* @param self 当前请求路径
* @param next 下一页的页码
* @param last 最后一页的页码
* @param attributes 领域模型集合
*/
public SuccessResult(String self, int next, int last, List<T> attributes) {
links = new Links();
links.setSelf(self);
links.setNext(self + "?page=" + next);
links.setLast(self + "?page=" + last);
attributes.forEach(attribute -> createDataBean(self, attribute));
}
/**
* 创建 DataBean
* @param self 当前请求路径
* @param attributes 领域模型
*/
private void createDataBean(String self, T attributes) {
if (data == null) {
data = Lists.newArrayList();
}
DataBean dataBean = new DataBean();
dataBean.setId(attributes.getId());
dataBean.setType(attributes.getClass().getSimpleName());
dataBean.setAttributes(attributes);
if (StringUtils.isNotBlank(self)) {
Links links = new Links();
links.setSelf(self + "/" + attributes.getId());
dataBean.setLinks(links);
}
data.add(dataBean);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# RegexpUtils
package com.cmcc.myshop.commons.utils;
/**
* 正则表达式工具类
* <p>Title: RegexpUtils</p>
* <p>Description: </p>
*
* @author cmcc
* @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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# AbstractBaseController
注意:封装
AbstractBaseController时使用了protected HttpServletRequest request;定义了一个request成员变量,目的是希望减少代码上的参数传递,使代码看上去简洁些,确无意间留下了可能的 线程安全 隐患;解决方法是在该成员变量上增加@Resource注解;区别在于有注解时创建的HttpServletRequest里绑定的RequestAttributes使用了ThreadLocal(作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性);具体可参考 Spring MVC 成员变量 request 线程安全问题的讨论 (opens new window) 这篇文章
package com.cmcc.myshop.commons.web;
import com.cmcc.myshop.commons.dto.AbstractBaseDomain;
import com.cmcc.myshop.commons.dto.AbstractBaseResult;
import com.cmcc.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;
/**
* 通用的控制器
* <p>Title: AbstractBaseController</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/25 11:11
*/
public abstract class AbstractBaseController<T extends AbstractBaseDomain> {
// 用于动态获取配置文件的属性值
private static final String ENVIRONMENT_LOGGING_LEVEL_MY_SHOP = "logging.level.com.cmcc.myshop";
@Resource
protected HttpServletRequest request;
@Resource
protected HttpServletResponse response;
@Autowired
private ConfigurableApplicationContext applicationContext;
@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<T> 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));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# myshop-commons-domain
# POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cmcc</groupId>
<artifactId>myshop-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../myshop-dependencies/pom.xml</relativePath>
</parent>
<artifactId>myshop-commons-domain</artifactId>
<packaging>jar</packaging>
<name>myshop-commons-domain</name>
<url>http://www.cmcc.com</url>
<inceptionYear>2018-Now</inceptionYear>
<dependencies>
<!-- Spring Begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Spring End -->
<!-- Commons Begin -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- Commons End -->
<!-- Projects Begin -->
<dependency>
<groupId>com.cmcc</groupId>
<artifactId>myshop-commons</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Projects End -->
</dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# TbUser
package com.cmcc.myshop.commons.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.cmcc.myshop.commons.dto.AbstractBaseDomain;
import com.cmcc.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)
public class TbUser extends AbstractBaseDomain {
/**
* 用户名
*/
@NotNull(message = "用户名不可为空")
@Length(min = 5, max = 20, message = "用户名长度必须介于 5 和 20 之间")
private String username;
/**
* 密码,加密存储
*/
@JsonIgnore
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# BeanValidator
package com.cmcc.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)工具类.
* <p>
* ConstraintViolation 中包含 propertyPath, message 和 invalidValue 等信息.
* 提供了各种 convert 方法,适合不同的 i18n 需求:
* 1. List<String>, String 内容为 message
* 2. List<String>, String 内容为 propertyPath + separator + message
* 3. Map<propertyPath, message>
* <p>
* 详情见wiki: https://github.com/springside/springside4/wiki/HibernateValidator
*
* <p>Title: BeanValidator</p>
* <p>Description: </p>
*
* @author cmcc
* @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<ConstraintViolations> 中为 List<message>.
*/
private static List<String> extractMessage(ConstraintViolationException e) {
return extractMessage(e.getConstraintViolations());
}
/**
* 辅助方法, 转换 Set<ConstraintViolation> 为 List<message>
*/
private static List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {
List<String> errorMessages = new ArrayList<>();
for (ConstraintViolation violation : constraintViolations) {
errorMessages.add(violation.getMessage());
}
return errorMessages;
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 Map<property, message>.
*/
private static Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {
return extractPropertyAndMessage(e.getConstraintViolations());
}
/**
* 辅助方法, 转换 Set<ConstraintViolation> 为 Map<property, message>.
*/
private static Map<String, String> extractPropertyAndMessage(Set<? extends ConstraintViolation> constraintViolations) {
Map<String, String> errorMessages = new HashMap<>();
for (ConstraintViolation violation : constraintViolations) {
errorMessages.put(violation.getPropertyPath().toString(), violation.getMessage());
}
return errorMessages;
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 List<propertyPath message>.
*/
private static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {
return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");
}
/**
* 辅助方法, 转换 Set<ConstraintViolations> 为 List<propertyPath message>.
*/
private static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations) {
return extractPropertyAndMessageAsList(constraintViolations, " ");
}
/**
* 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 List<propertyPath + separator + message>.
*/
private static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {
return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);
}
/**
* 辅助方法, 转换 Set<ConstraintViolation> 为 List<propertyPath + separator + message>.
*/
private static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations, String separator) {
List<String> 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<String> 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 ? "<br/>" : "");
} else {
sb.append(exMsg).append(list.size() > 1 ? "<br/>" : "");
}
}
return sb.toString();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# myshop-commons-service
# BaseCrudService
package com.cmcc.myshop.commons.service;
import com.cmcc.myshop.commons.dto.AbstractBaseDomain;
/**
* 通用的业务逻辑
* <p>Title: BaseCrudService</p>
* <p>Description: </p>
*
* @author cmcc
* @version 1.0.0
* @date 2019/1/25 9:43
*/
public interface BaseCrudService<T extends AbstractBaseDomain> {
/**
* 查询属性值是否唯一
*
* @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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# TbUserService
package com.cmcc.myshop.commons.service;
import com.cmcc.myshop.commons.domain.TbUser;
public interface TbUserService extends BaseCrudService<TbUser> {
}
1
2
3
4
5
6
2
3
4
5
6
# BaseCrudServiceImpl
package com.cmcc.myshop.commons.service.impl;
import com.cmcc.myshop.commons.dto.AbstractBaseDomain;
import com.cmcc.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<T extends AbstractBaseDomain, M extends MyMapper<T>> implements BaseCrudService<T> {
@Autowired
protected M mapper;
private Class<T> entityClass = (Class<T>) ((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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# TbUserServiceImpl
package com.cmcc.myshop.commons.service.impl;
import com.cmcc.myshop.commons.domain.TbUser;
import com.cmcc.myshop.commons.mapper.TbUserMapper;
import com.cmcc.myshop.commons.service.TbUserService;
import org.springframework.stereotype.Service;
@Service
public class TbUserServiceImpl extends BaseCrudServiceImpl<TbUser, TbUserMapper> implements TbUserService {
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# myshop-service-reg
# MyShopServiceRegApplication
package com.cmcc.myshop.service.reg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication(scanBasePackages = "com.cmcc.myshop")
@EnableDiscoveryClient
@MapperScan(basePackages = "com.cmcc.myshop.commons.mapper")
public class MyShopServiceRegApplication {
public static void main(String[] args) {
SpringApplication.run(MyShopServiceRegApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# RegController
package com.cmcc.myshop.service.reg.controller;
import com.cmcc.myshop.commons.domain.TbUser;
import com.cmcc.myshop.commons.dto.AbstractBaseResult;
import com.cmcc.myshop.commons.service.TbUserService;
import com.cmcc.myshop.commons.validator.BeanValidator;
import com.cmcc.myshop.commons.web.AbstractBaseController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "reg")
public class RegController extends AbstractBaseController<TbUser> {
@Autowired
private TbUserService tbUserService;
@PostMapping(value = "")
public AbstractBaseResult reg(TbUser tbUser) {
// 数据校验
String message = BeanValidator.validator(tbUser);
if (StringUtils.isNotBlank(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) {
response.setStatus(HttpStatus.CREATED.value());
return success(request.getRequestURI(), user);
}
// 注册失败
return error("注册失败,请重试", null);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52