Compare commits
121 Commits
6f4503684f
...
dev
Author | SHA1 | Date | |
---|---|---|---|
e8274b1beb | |||
3b1c15335b | |||
81cefdca83 | |||
86fcd7a255 | |||
15ed0e2ec6 | |||
3bb5397c49 | |||
b69983077a | |||
7f8a37803a | |||
8414fcb010 | |||
b91ab22354 | |||
00c5ed651e | |||
b25d7589c2 | |||
208d7c93c9 | |||
b870684fea | |||
9582cc7187 | |||
a30e5bcd12 | |||
1c02b37442 | |||
68f6ce5b8f | |||
bd42da6777 | |||
c4e7b8fd26 | |||
e34efdfc56 | |||
7077b84317 | |||
79466d180f | |||
dcb86f9100 | |||
e972899fbb | |||
aa7f10008f | |||
b32dac3b8a | |||
2f07ce0e5f | |||
30f67dfa18 | |||
87942d708a | |||
d05fcbfc9c | |||
363460fe96 | |||
f83a05e429 | |||
92896a0345 | |||
ac3fdae2d0 | |||
8dcf8eadac | |||
39d8eac578 | |||
3858aa16e1 | |||
4d3d0f7cc7 | |||
17b5b55d59 | |||
30f29bb4b3 | |||
104bffb0e8 | |||
62607dc0a4 | |||
fe03b6da4d | |||
0f145e383e | |||
89584d2a48 | |||
f3017e90c0 | |||
f1d16808c6 | |||
8460b9da29 | |||
19fc97362c | |||
d91f818c96 | |||
65d77f35c1 | |||
b14c03fc32 | |||
ba34fa4a2b | |||
b015f5d1c4 | |||
6335fa03b1 | |||
0e0d6f1808 | |||
5215fded9c | |||
c30cca8f6a | |||
06ffc8d858 | |||
60b4f18e8c | |||
2b282039ad | |||
ea7a8ee6a0 | |||
67313938e1 | |||
27be582bfb | |||
288ce9689f | |||
71edaa60a4 | |||
39cd57e675 | |||
7a44c43402 | |||
55395ed327 | |||
956da350ed | |||
78cdded667 | |||
cc3c4be8de | |||
1e31e1f327 | |||
993ba7fa7b | |||
8ae0faa04d | |||
920971c640 | |||
5f1cb36235 | |||
b4bccfe663 | |||
dc09022919 | |||
f0b6014e77 | |||
19557734ed | |||
ff54aca271 | |||
5d9e6ae5b3 | |||
5c3b31bb18 | |||
33d3d86393 | |||
a0b16afde2 | |||
9293ba6817 | |||
92dbc613d2 | |||
1b551eeb4c | |||
5b80f70373 | |||
cf068566a1 | |||
0212020dcf | |||
699ba2394d | |||
5ad6980bf7 | |||
bac7a007e6 | |||
02d918d0a4 | |||
f70e9575ea | |||
cbc2711540 | |||
960a4d4ef3 | |||
1dbd00ccef | |||
44f2067f20 | |||
cc44c3ddbd | |||
2de86dcc4a | |||
7c522cae1d | |||
117390c5ae | |||
cec72ba7f9 | |||
d01db60309 | |||
151a33cad4 | |||
1611bf38e8 | |||
6d7517f778 | |||
c73c722b87 | |||
2c967a4c0d | |||
90da53bf2b | |||
7462b47655 | |||
17273b0bc6 | |||
46a72e63c0 | |||
ad8a6da44d | |||
d398800ce4 | |||
71438901a0 | |||
2d02899372 |
@@ -33,4 +33,4 @@ indent_size=4
|
||||
indent_size=2
|
||||
|
||||
[*.java]
|
||||
indent_size = 4
|
||||
indent_size=4
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -32,3 +32,8 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
log/
|
||||
|
||||
### bak ###
|
||||
*.bak
|
||||
*-secret.yaml
|
||||
|
@@ -18,4 +18,11 @@
|
||||
|
||||
目前项目还没完成,开发中……
|
||||
|
||||
相关的文档和介绍完善中……
|
||||
相关的文档和介绍完善中……
|
||||
|
||||
## 编码规约
|
||||
### 关于 null
|
||||
1. 方法默认参数不为 `null`,**可以**在 Javadoc 中对参数进行说明,如“...(must not be {@code null}[ or empty string])”。方法内部**必须**做参数校验,如使用 Assert 等工具类。
|
||||
2. 如果参数允许为空,**必须**为参数添加 `@Nullable` 注解,并在 Javadoc 中对参数进行说明,如“...(may be {@code null})”。
|
||||
3. 除极特殊的情况,方法默认**不返回 `null`**,如可能返回一个对象表示值的缺失,则**必须**使用 `Optional`,**绝不在返回类型为 Optional 的方法中返回 `null`**。
|
||||
4. 在极其特殊的情况下,方法需要返回 `null`,**必须**给方法加上 `@Nullable` 注解,并在 Javadoc 中详细说明。
|
||||
|
@@ -222,6 +222,10 @@
|
||||
{
|
||||
"code": 4040201,
|
||||
"description": "不支持的 PrincipalType"
|
||||
},
|
||||
{
|
||||
"code": 4040209,
|
||||
"description": "不支持的菜单类型"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>plusone-basic</artifactId>
|
||||
|
@@ -1,11 +1,13 @@
|
||||
package xyz.zhouxy.plusone.exception.handler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
|
||||
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.AllExceptionHandler;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
|
||||
|
||||
/**
|
||||
* AllExceptionHandlerConfig
|
||||
@@ -15,7 +17,7 @@ import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHo
|
||||
public class AllExceptionHandlerConfig {
|
||||
|
||||
@Bean
|
||||
AllExceptionHandler getAllExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
|
||||
return new AllExceptionHandler(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
|
||||
AllExceptionHandler getAllExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
return new AllExceptionHandler(exceptionInfoHolder);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package xyz.zhouxy.plusone.exception.handler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -11,8 +13,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
|
||||
import xyz.zhouxy.plusone.util.RestfulResult;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
|
||||
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
|
||||
|
||||
/**
|
||||
* 默认异常的处理器
|
||||
@@ -41,14 +43,13 @@ import xyz.zhouxy.plusone.util.RestfulResult;
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 1)
|
||||
@Slf4j
|
||||
public class DefaultExceptionHandler extends BaseExceptionHandler {
|
||||
|
||||
public DefaultExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
|
||||
public DefaultExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(exceptionInfoHolder);
|
||||
set(IllegalArgumentException.class, 4010000, "格式错误", HttpStatus.FORBIDDEN);
|
||||
set(DataAccessException.class, 6030000, "数据库错误", HttpStatus.INTERNAL_SERVER_ERROR, true);
|
||||
set(MethodArgumentNotValidException.class,
|
||||
4040401,
|
||||
e -> ((MethodArgumentNotValidException) e).getAllErrors()
|
||||
e -> e.getAllErrors()
|
||||
.stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.toList()
|
||||
@@ -61,7 +62,7 @@ public class DefaultExceptionHandler extends BaseExceptionHandler {
|
||||
DataAccessException.class,
|
||||
MethodArgumentNotValidException.class
|
||||
})
|
||||
public ResponseEntity<RestfulResult> handleException(Exception e) {
|
||||
public ResponseEntity<UnifiedResponse> handleException(Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return buildExceptionResponse(e);
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package xyz.zhouxy.plusone.exception.handler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
|
||||
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
|
||||
import xyz.zhouxy.plusone.exception.SysException;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class SysExceptionHandler extends BaseExceptionHandler {
|
||||
|
||||
public SysExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(exceptionInfoHolder);
|
||||
}
|
||||
|
||||
@ExceptionHandler({ SysException.class })
|
||||
public ResponseEntity<UnifiedResponse> handleException(@Nonnull Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
HttpStatus httpStatus = getHttpStatus(e);
|
||||
return new ResponseEntity<>(UnifiedResponse.error(getErrorCode(e), "系统错误"), httpStatus);
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>plusone-basic</artifactId>
|
||||
@@ -31,20 +31,25 @@
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>xyz.zhouxy.plusone</groupId>
|
||||
<artifactId>plusone-commons</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xyz.zhouxy.plusone</groupId>
|
||||
<artifactId>plusone-validator</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.1.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xyz.zhouxy.plusone</groupId>
|
||||
<artifactId>plusone-exception-handler</artifactId>
|
||||
<version>0.0.4-SNAPSHOT</version>
|
||||
<version>0.0.9-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@@ -2,4 +2,10 @@ package xyz.zhouxy.plusone.constant;
|
||||
|
||||
public class ErrorCodeConsts {
|
||||
public static final int DEFAULT_ERROR_CODE = 9999999;
|
||||
public static final int DEFAULT_SYS_ERROR_CODE = 5000000;
|
||||
public static final int DEFAULT_BIZ_ERROR_CODE = 4000000;
|
||||
|
||||
private ErrorCodeConsts() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,39 @@
|
||||
package xyz.zhouxy.plusone.exception;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.exception.BaseRuntimeException;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public class BizException extends BaseRuntimeException {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -5524759033245815405L;
|
||||
|
||||
public static final String DEFAULT_ERROR_CODE = "4000000";
|
||||
|
||||
public BizException(String code, String msg) {
|
||||
super(code, msg);
|
||||
}
|
||||
|
||||
public BizException(String code, Throwable cause) {
|
||||
super(code, cause);
|
||||
}
|
||||
|
||||
public BizException(String code, String msg, Throwable cause) {
|
||||
super(code, msg, cause);
|
||||
}
|
||||
|
||||
public static BizException of(String msg) {
|
||||
return new BizException(DEFAULT_ERROR_CODE, msg);
|
||||
}
|
||||
|
||||
public static BizException of(Throwable cause) {
|
||||
return new BizException(DEFAULT_ERROR_CODE, cause);
|
||||
}
|
||||
|
||||
public static BizException of(String msg, Throwable cause) {
|
||||
return new BizException(DEFAULT_ERROR_CODE, msg, cause);
|
||||
}
|
||||
}
|
@@ -6,20 +6,15 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
/**
|
||||
* 需要时,当查询数据不存在时抛出的异常
|
||||
*
|
||||
* <p>
|
||||
* 暂时先这样,后续完善异常体系时可能会更改。
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
* @see xyz.zhouxy.plusone.util.AssertResult
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class DataNotExistException extends PlusoneException {
|
||||
public class DataNotExistException extends BizException {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 6536955800679703111L;
|
||||
|
||||
public static final int ERROR_CODE = 4110100;
|
||||
public static final String ERROR_CODE = "4110100";
|
||||
|
||||
public DataNotExistException() {
|
||||
super(ERROR_CODE, "数据不存在");
|
||||
|
@@ -4,28 +4,27 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* 需要时,当数据操作的行数不符合预期时抛出的异常
|
||||
* 需要时,当数据操作的结果不符合预期时抛出的异常
|
||||
*
|
||||
* <p>
|
||||
* 暂时先这样,后续完善异常体系时可能会更改。
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
* @see xyz.zhouxy.plusone.util.AssertResult
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public class DataOperationNumberException extends PlusoneException {
|
||||
public class DataOperationResultException extends SysException {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -9220765735990318186L;
|
||||
|
||||
public static final int ERROR_CODE = 4110200;
|
||||
public static final String ERROR_CODE = "4110200";
|
||||
|
||||
public DataOperationNumberException() {
|
||||
super(ERROR_CODE, "数据操作的行数不符合预期");
|
||||
public DataOperationResultException() {
|
||||
super(ERROR_CODE, "数据操作结果不符合预期");
|
||||
}
|
||||
|
||||
public DataOperationNumberException(String message) {
|
||||
public DataOperationResultException(String message) {
|
||||
super(ERROR_CODE, message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package xyz.zhouxy.plusone.exception;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.exception.BaseRuntimeException;
|
||||
|
||||
public class SysException extends BaseRuntimeException {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 8821240827443168118L;
|
||||
|
||||
public static final String DEFAULT_ERROR_CODE = "5000000";
|
||||
|
||||
public SysException(String code, String msg) {
|
||||
super(code, msg);
|
||||
}
|
||||
|
||||
public SysException(String code, Throwable cause) {
|
||||
super(code, cause);
|
||||
}
|
||||
|
||||
public SysException(String code, String msg, Throwable cause) {
|
||||
super(code, msg, cause);
|
||||
}
|
||||
|
||||
public static SysException of(String msg) {
|
||||
return new SysException(DEFAULT_ERROR_CODE, msg);
|
||||
}
|
||||
|
||||
public static SysException of(Throwable cause) {
|
||||
return new SysException(DEFAULT_ERROR_CODE, cause);
|
||||
}
|
||||
|
||||
public static SysException of(String msg, Throwable cause) {
|
||||
return new SysException(DEFAULT_ERROR_CODE, msg, cause);
|
||||
}
|
||||
}
|
@@ -9,33 +9,33 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
* @author ZhouXY
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class UserOperationException extends PlusoneException {
|
||||
public class UserOperationException extends BizException {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 4371055414421991940L;
|
||||
|
||||
public static final int DEFAULT_ERROR_CODE = 4040600;
|
||||
public static final String DEFAULT_ERROR_CODE = "4040600";
|
||||
|
||||
public UserOperationException(String msg) {
|
||||
super(DEFAULT_ERROR_CODE, msg);
|
||||
}
|
||||
|
||||
public UserOperationException(String msg, Throwable cause) {
|
||||
super(DEFAULT_ERROR_CODE, msg, cause);
|
||||
}
|
||||
|
||||
public UserOperationException(int code, String msg) {
|
||||
public UserOperationException(String code, String msg) {
|
||||
super(code, msg);
|
||||
}
|
||||
|
||||
public UserOperationException(int code, Throwable cause) {
|
||||
public UserOperationException(String code, Throwable cause) {
|
||||
super(code, cause);
|
||||
}
|
||||
|
||||
public UserOperationException(int code, String msg, Throwable cause) {
|
||||
public UserOperationException(String code, String msg, Throwable cause) {
|
||||
super(code, msg, cause);
|
||||
}
|
||||
|
||||
public static UserOperationException of(String msg) {
|
||||
return new UserOperationException(DEFAULT_ERROR_CODE, msg);
|
||||
}
|
||||
|
||||
public static UserOperationException of(String msg, Throwable cause) {
|
||||
return new UserOperationException(DEFAULT_ERROR_CODE, msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无效的操作
|
||||
*
|
||||
@@ -52,7 +52,7 @@ public class UserOperationException extends PlusoneException {
|
||||
* @return 异常对象
|
||||
*/
|
||||
public static UserOperationException invalidOperation(String msg) {
|
||||
return new UserOperationException(4040604, msg);
|
||||
return new UserOperationException("4040604", msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package xyz.zhouxy.plusone.util;
|
||||
|
||||
|
||||
import xyz.zhouxy.plusone.exception.DataNotExistException;
|
||||
import xyz.zhouxy.plusone.exception.DataOperationNumberException;
|
||||
import xyz.zhouxy.plusone.exception.DataOperationResultException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 对数据库执行结果进行判断
|
||||
@@ -17,51 +17,34 @@ public final class AssertResult {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static void update(boolean expression) {
|
||||
if (!expression) {
|
||||
throw new DataOperationNumberException();
|
||||
public static <E extends Throwable> void isTrue(boolean condition, Supplier<E> e) throws E {
|
||||
if (!condition) {
|
||||
throw e.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(boolean expression, String message) {
|
||||
if (!expression) {
|
||||
throw new DataOperationNumberException(message);
|
||||
}
|
||||
public static <T> void equals(T result, T expectedValue) {
|
||||
isTrue(Objects.equals(result, expectedValue), DataOperationResultException::new);
|
||||
}
|
||||
|
||||
public static void update(Object i, int expectedValue) {
|
||||
if (!Objects.equals(i, expectedValue)) {
|
||||
throw new DataOperationNumberException();
|
||||
}
|
||||
public static <T> void equals(T result, T expectedValue, String msgTemplate, Object... args) {
|
||||
isTrue(!Objects.equals(result, expectedValue),
|
||||
() -> new DataOperationResultException(String.format(msgTemplate, args)));
|
||||
}
|
||||
|
||||
public static void update(Object i, int expectedValue, String format) {
|
||||
if (!Objects.equals(i, expectedValue)) {
|
||||
throw new DataOperationNumberException(String.format(format, i));
|
||||
}
|
||||
public static void updateOneRow(int i) {
|
||||
equals(i, 1);
|
||||
}
|
||||
|
||||
public static void exist(boolean expression) {
|
||||
if (!expression) {
|
||||
throw new DataNotExistException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void exist(boolean expression, String message) {
|
||||
if (!expression) {
|
||||
throw new DataNotExistException(message);
|
||||
}
|
||||
public static void updateOneRow(int i, String format, Object... args) {
|
||||
equals(i, 1, format, args);
|
||||
}
|
||||
|
||||
public static void nonNull(Object obj) {
|
||||
if (Objects.isNull(obj)) {
|
||||
throw new DataNotExistException();
|
||||
}
|
||||
isTrue(Objects.nonNull(obj), DataNotExistException::new);
|
||||
}
|
||||
|
||||
public static void nonNull(Object obj, String message) {
|
||||
if (Objects.isNull(obj)) {
|
||||
throw new DataNotExistException(message);
|
||||
}
|
||||
isTrue(Objects.nonNull(obj), () -> new DataNotExistException(message));
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,5 @@
|
||||
package xyz.zhouxy.plusone.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -27,17 +23,4 @@ public class PlusoneStrUtil {
|
||||
return StringUtils.hasLength(value) ? value : EMPTY_STR;
|
||||
}
|
||||
|
||||
public static String checkString(String value, String regex, String message) {
|
||||
Assert.notNull(value, message);
|
||||
Assert.isTrue(Pattern.matches(regex, value), message);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String checkStringNullable(String value, String regex, String message) {
|
||||
return Objects.nonNull(value) ? checkString(value, regex, message) : null;
|
||||
}
|
||||
|
||||
public static String checkStringOrDefault(String value, String regex, String message) {
|
||||
return StringUtils.hasText(value) ? checkString(value, regex, message) : "";
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
package xyz.zhouxy.plusone.util;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public final class RandomUtil {
|
||||
private RandomUtil() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static String randomStr(char[] sourceCharacters, int length) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
char[] result = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = sourceCharacters[random.nextInt(sourceCharacters.length)];
|
||||
}
|
||||
return String.valueOf(result);
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package xyz.zhouxy.plusone.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
class RandomUtilTests {
|
||||
|
||||
@Test
|
||||
void testRandom() {
|
||||
String[] s = new String[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
s[i] = RandomUtil.randomStr(
|
||||
"0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~`!@#$%^&*()_-+={[\\|/:;\"',.<>?]}"
|
||||
.toCharArray(),
|
||||
28);
|
||||
}
|
||||
log.info("{}", Arrays.toString(s));
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>xyz.zhouxy</groupId>
|
||||
@@ -15,11 +15,6 @@
|
||||
<groupId>xyz.zhouxy</groupId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@@ -28,12 +23,10 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.13.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.13.4</version>
|
||||
</dependency>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -1,32 +1,33 @@
|
||||
package xyz.zhouxy.plusone.constant;
|
||||
|
||||
import xyz.zhouxy.plusone.util.Enumeration;
|
||||
import xyz.zhouxy.plusone.util.EnumerationValuesHolder;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.util.Enumeration;
|
||||
|
||||
/**
|
||||
* 实体状态
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public class EntityStatus extends Enumeration<EntityStatus> {
|
||||
public final class EntityStatus extends Enumeration<EntityStatus> {
|
||||
|
||||
private EntityStatus(int value, String name) {
|
||||
super(value, name);
|
||||
private EntityStatus(int id, @Nonnull String name) {
|
||||
super(id, name);
|
||||
}
|
||||
|
||||
// 常量
|
||||
public static final EntityStatus AVAILABLE = new EntityStatus(0, "正常");
|
||||
public static final EntityStatus DISABLED = new EntityStatus(1, "禁用");
|
||||
|
||||
private static final EnumerationValuesHolder<EntityStatus> ENUMERATION_VALUES = new EnumerationValuesHolder<>(
|
||||
new EntityStatus[] { AVAILABLE, DISABLED });
|
||||
private static final ValueSet<EntityStatus> VALUE_SET = ValueSet.of(AVAILABLE, DISABLED);
|
||||
|
||||
public static EntityStatus of(int value) {
|
||||
return ENUMERATION_VALUES.get(value);
|
||||
public static EntityStatus of(int id) {
|
||||
return VALUE_SET.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EntityStatus" + super.toString();
|
||||
public static Collection<EntityStatus> constants() {
|
||||
return VALUE_SET.getValues();
|
||||
}
|
||||
}
|
||||
|
@@ -1,27 +0,0 @@
|
||||
package xyz.zhouxy.plusone.constant;
|
||||
|
||||
/**
|
||||
* 正则表达式常量
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public final class RegexConsts {
|
||||
|
||||
public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}";
|
||||
|
||||
public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$";
|
||||
|
||||
public static final String CAPTCHA = "^[0-9A-Za-z]{4,6}$";
|
||||
|
||||
public static final String EMAIL = "^\\w+([-+.]\\w+)*@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})*(\\.(?![0-9]+$)[a-zA-Z0-9][-0-9A-Za-z]{0,62})$";
|
||||
|
||||
public static final String MOBILE_PHONE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
|
||||
|
||||
public static final String USERNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
|
||||
|
||||
public static final String NICKNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
|
||||
|
||||
private RegexConsts() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package xyz.zhouxy.plusone.domain;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
@@ -16,11 +17,11 @@ import lombok.Getter;
|
||||
*/
|
||||
@Getter
|
||||
public abstract class DomainEvent {
|
||||
private String identifier;
|
||||
private long happenedAt;
|
||||
private final String identifier;
|
||||
private final long happenedAt;
|
||||
|
||||
protected DomainEvent() {
|
||||
this.identifier = UUID.randomUUID().toString(true);
|
||||
this.identifier = UUID.randomUUID().toString();
|
||||
this.happenedAt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.annotation.Overridable;
|
||||
|
||||
/**
|
||||
* 实体
|
||||
*
|
||||
@@ -26,7 +28,29 @@ public abstract class Entity<ID extends Serializable> {
|
||||
|
||||
public abstract Optional<ID> getId();
|
||||
|
||||
protected void addDomainEvent(DomainEvent domainEvent) {
|
||||
protected final void addDomainEvent(DomainEvent domainEvent) {
|
||||
domainEvents.add(domainEvent);
|
||||
}
|
||||
|
||||
public final List<DomainEvent> getDomainEvents() {
|
||||
return List.copyOf(domainEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Overridable
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Overridable
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Overridable
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package xyz.zhouxy.plusone.domain;
|
||||
|
||||
public interface IDomainEventPublisher {
|
||||
|
||||
void publish(DomainEvent domainEvent);
|
||||
|
||||
<T extends DomainEvent> void subsrcibe(IDomainEventSubscriber<T> subscriber);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package xyz.zhouxy.plusone.domain;
|
||||
|
||||
/**
|
||||
* 领域事件消费者
|
||||
*/
|
||||
public interface IDomainEventSubscriber<T extends DomainEvent> {
|
||||
|
||||
void handleEvent(final T event);
|
||||
|
||||
Class<T> subscribedToEventType();
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package xyz.zhouxy.plusone.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Repository 基础接口
|
||||
@@ -10,7 +11,7 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface IRepository<T extends AggregateRoot<ID>, ID extends Serializable> {
|
||||
|
||||
T find(ID id);
|
||||
Optional<T> find(ID id);
|
||||
|
||||
T save(T entity);
|
||||
|
||||
|
@@ -1,34 +0,0 @@
|
||||
package xyz.zhouxy.plusone.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
/**
|
||||
* 带校验的字符串值对象
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public abstract class ValidatableStringRecord implements IValueObject {
|
||||
|
||||
protected String value;
|
||||
protected final String format;
|
||||
|
||||
protected ValidatableStringRecord(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
protected boolean isValid() {
|
||||
return value.matches(format);
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
package xyz.zhouxy.plusone.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RegexUtil {
|
||||
|
||||
private RegexUtil() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static boolean matches(CharSequence input, String regex) {
|
||||
return Pattern.matches(regex, input);
|
||||
}
|
||||
|
||||
public static boolean matchesOr(CharSequence input, String... regexs) {
|
||||
boolean isMatched;
|
||||
for (var regex : regexs) {
|
||||
isMatched = Pattern.matches(regex, input);
|
||||
if (isMatched) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean matchesAnd(CharSequence input, String... regexs) {
|
||||
boolean isMatched;
|
||||
for (var regex : regexs) {
|
||||
isMatched = Pattern.matches(regex, input);
|
||||
if (!isMatched) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>xyz.zhouxy</groupId>
|
||||
@@ -84,6 +86,11 @@
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java-sms</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.csource</groupId>
|
||||
<artifactId>fastdfs-client-java</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@@ -4,7 +4,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
|
||||
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
|
||||
|
||||
@Configuration
|
||||
public class PlusoneExceptionHandlerConfig {
|
||||
@@ -12,6 +13,6 @@ public class PlusoneExceptionHandlerConfig {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ExceptionInfoHolder exceptionInfoHolder() {
|
||||
return new ExceptionInfoHolder();
|
||||
return new ExceptionInfoHolder(ErrorCodeConsts.DEFAULT_ERROR_CODE);
|
||||
}
|
||||
}
|
@@ -1,8 +1,12 @@
|
||||
package xyz.zhouxy.plusone.jdbc;
|
||||
|
||||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.base.IWithCode;
|
||||
import xyz.zhouxy.plusone.commons.base.IWithIntCode;
|
||||
|
||||
/**
|
||||
* 扩展了 {@link BeanPropertySqlParameterSource},在将 POJO 转换为
|
||||
* {@link org.springframework.jdbc.core.namedparam.SqlParameterSource} 时,
|
||||
@@ -17,17 +21,23 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public class BeanPropertyParamSource extends BeanPropertySqlParameterSource {
|
||||
|
||||
public BeanPropertyParamSource(Object object) {
|
||||
super(object);
|
||||
}
|
||||
public BeanPropertyParamSource(Object object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getValue(String paramName) throws IllegalArgumentException {
|
||||
Object value = super.getValue(paramName);
|
||||
if (value instanceof Enum) {
|
||||
return ((Enum<?>) value).ordinal();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getValue(String paramName) throws IllegalArgumentException {
|
||||
Object value = super.getValue(paramName);
|
||||
if (value instanceof Enum<?> e) {
|
||||
if (value instanceof IWithCode<?> c) {
|
||||
return c.getCode();
|
||||
}
|
||||
if (value instanceof IWithIntCode c) {
|
||||
return c.getCode();
|
||||
}
|
||||
return e.ordinal();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@@ -4,69 +4,72 @@ import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
|
||||
import xyz.zhouxy.plusone.domain.Entity;
|
||||
|
||||
public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Serializable> {
|
||||
protected final NamedParameterJdbcTemplate jdbc;
|
||||
public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Serializable>
|
||||
extends PlusoneJdbcDaoSupport {
|
||||
|
||||
protected RowMapper<T> rowMapper;
|
||||
protected ResultSetExtractor<T> resultSetExtractor;
|
||||
protected ResultSetExtractor<Optional<T>> resultSetExtractor;
|
||||
|
||||
protected JdbcEntityDaoSupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
|
||||
this.jdbc = namedParameterJdbcTemplate;
|
||||
super(namedParameterJdbcTemplate);
|
||||
this.rowMapper = (ResultSet rs, int rowNum) -> mapRow(rs);
|
||||
this.resultSetExtractor = (ResultSet rs) -> rs.next() ? mapRow(rs) : null;
|
||||
this.resultSetExtractor = (ResultSet rs) -> rs.next() ? Optional.of(mapRow(rs)) : Optional.empty();
|
||||
}
|
||||
|
||||
protected final T queryForObject(String sql) {
|
||||
return this.jdbc.query(sql, this.resultSetExtractor);
|
||||
protected final Optional<T> queryForObject(String sql) {
|
||||
return queryForObject(sql, this.resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final T queryForObject(String sql, SqlParameterSource paramSource) {
|
||||
return this.jdbc.query(sql, paramSource, this.resultSetExtractor);
|
||||
protected final Optional<T> queryForObject(String sql, SqlParameterSource paramSource) {
|
||||
return queryForObject(sql, paramSource, this.resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final Optional<T> queryForObject(String sql, String paramName, Object value) {
|
||||
return queryForObject(sql, new MapSqlParameterSource(paramName, value), this.resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final List<T> queryForList(String sql) {
|
||||
return this.jdbc.query(sql, this.rowMapper);
|
||||
return queryForList(sql, this.rowMapper);
|
||||
}
|
||||
|
||||
protected final List<T> queryForList(String sql, SqlParameterSource parameterSource) {
|
||||
return this.jdbc.query(sql, parameterSource, this.rowMapper);
|
||||
return queryForList(sql, parameterSource, this.rowMapper);
|
||||
}
|
||||
|
||||
protected final List<T> queryForList(String sql, String paramName, Object value) {
|
||||
return queryForList(sql, new MapSqlParameterSource(paramName, value), this.rowMapper);
|
||||
}
|
||||
|
||||
protected final Stream<T> queryForStream(String sql, SqlParameterSource parameterSource) {
|
||||
return this.jdbc.queryForStream(sql, parameterSource, this.rowMapper);
|
||||
return queryForStream(sql, parameterSource, this.rowMapper);
|
||||
}
|
||||
|
||||
protected final <E> Stream<E> queryForStream(String sql, SqlParameterSource parameterSource, Class<E> elementType) {
|
||||
return this.jdbc.queryForList(sql, parameterSource, elementType).stream();
|
||||
}
|
||||
|
||||
protected final boolean queryExists(String sql, SqlParameterSource parameterSource) {
|
||||
Boolean isExists = this.jdbc.query(sql, parameterSource, ResultSet::next);
|
||||
return Boolean.TRUE.equals(isExists);
|
||||
}
|
||||
|
||||
protected final int update(String sql, SqlParameterSource parameterSource) {
|
||||
return this.jdbc.update(sql, parameterSource);
|
||||
protected final Stream<T> queryForStream(String sql, String paramName, Object value) {
|
||||
return queryForStream(sql, new MapSqlParameterSource(paramName, value), this.rowMapper);
|
||||
}
|
||||
|
||||
protected abstract T mapRow(ResultSet rs) throws SQLException;
|
||||
|
||||
@Deprecated
|
||||
protected void setRowMapper(@Nonnull RowMapper<T> rowMapper) {
|
||||
this.rowMapper = rowMapper;
|
||||
}
|
||||
|
||||
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<T> resultSetExtractor) {
|
||||
@Deprecated
|
||||
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<Optional<T>> resultSetExtractor) {
|
||||
this.resultSetExtractor = resultSetExtractor;
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
package xyz.zhouxy.plusone.jdbc;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import xyz.zhouxy.plusone.spring.SpringContextHolder;
|
||||
|
||||
/**
|
||||
* 全局单例,由 Spring 装配好的对象。
|
||||
* 可通过静态方法获取 Spring 容器中的 {@link JdbcTemplate} 和
|
||||
* 通过静态方法获取 Spring 容器中的 {@link JdbcTemplate} 和
|
||||
* {@link NamedParameterJdbcTemplate} 对象。
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
@@ -16,15 +16,19 @@ import xyz.zhouxy.plusone.spring.SpringContextHolder;
|
||||
*/
|
||||
public final class JdbcFactory {
|
||||
|
||||
private static final ApplicationContext SPRING_APPLICATION_CONTEXT = SpringContextHolder.getContext();
|
||||
private static final JdbcTemplate JDBC_TEMPLATE = SPRING_APPLICATION_CONTEXT.getBean(JdbcTemplate.class);
|
||||
private static final NamedParameterJdbcTemplate NAMED_PARAMETER_JDBC_TEMPLATE = SPRING_APPLICATION_CONTEXT.getBean(NamedParameterJdbcTemplate.class);
|
||||
|
||||
private JdbcFactory() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static JdbcTemplate getJdbcTemplate() {
|
||||
return SpringContextHolder.getContext().getBean(JdbcTemplate.class);
|
||||
return JDBC_TEMPLATE;
|
||||
}
|
||||
|
||||
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
|
||||
return SpringContextHolder.getContext().getBean(NamedParameterJdbcTemplate.class);
|
||||
return NAMED_PARAMETER_JDBC_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,16 @@
|
||||
package xyz.zhouxy.plusone.jdbc;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.annotation.Overridable;
|
||||
import xyz.zhouxy.plusone.domain.AggregateRoot;
|
||||
import xyz.zhouxy.plusone.domain.IRepository;
|
||||
|
||||
@@ -18,35 +22,33 @@ public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID exte
|
||||
super(namedParameterJdbcTemplate);
|
||||
}
|
||||
|
||||
@Overridable
|
||||
protected abstract void doDelete(@Nonnull T entity);
|
||||
|
||||
protected abstract T doFindById(@Nonnull ID id);
|
||||
@Overridable
|
||||
protected abstract Optional<T> doFindById(@Nonnull ID id);
|
||||
|
||||
@Overridable
|
||||
protected abstract T doInsert(@Nonnull T entity);
|
||||
|
||||
@Overridable
|
||||
protected abstract T doUpdate(@Nonnull T entity);
|
||||
|
||||
@Override
|
||||
public final void delete(T entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Cannot delete null.");
|
||||
}
|
||||
Preconditions.checkArgument(entity != null, "Cannot delete null.");
|
||||
doDelete(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T find(ID id) {
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Id cannot be null.");
|
||||
}
|
||||
public final Optional<T> find(ID id) {
|
||||
Preconditions.checkArgument(id != null, "Id cannot be null.");
|
||||
return doFindById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T save(T entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Cannot save null.");
|
||||
}
|
||||
Preconditions.checkArgument(entity != null, "Cannot save null.");
|
||||
return entity.getId().isPresent() ? doUpdate(entity) : doInsert(entity);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,173 @@
|
||||
package xyz.zhouxy.plusone.jdbc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.util.Numbers;
|
||||
import xyz.zhouxy.plusone.exception.DataOperationResultException;
|
||||
|
||||
public abstract class PlusoneJdbcDaoSupport {
|
||||
protected final NamedParameterJdbcTemplate jdbc;
|
||||
|
||||
protected PlusoneJdbcDaoSupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
|
||||
this.jdbc = namedParameterJdbcTemplate;
|
||||
}
|
||||
|
||||
protected final <T> T queryForObject(String sql, ResultSetExtractor<T> resultSetExtractor) {
|
||||
return this.jdbc.query(sql, resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final <T> T queryForObject(String sql, SqlParameterSource paramSource,
|
||||
ResultSetExtractor<T> resultSetExtractor) {
|
||||
return this.jdbc.query(sql, paramSource, resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final <T> T queryForObject(String sql, String paramName, Object value,
|
||||
ResultSetExtractor<T> resultSetExtractor) {
|
||||
return this.jdbc.query(sql, new MapSqlParameterSource(paramName, value), resultSetExtractor);
|
||||
}
|
||||
|
||||
protected final <T> List<T> queryForList(String sql, RowMapper<T> rowMapper) {
|
||||
return this.jdbc.query(sql, rowMapper);
|
||||
}
|
||||
|
||||
protected final <T> List<T> queryForList(String sql, SqlParameterSource parameterSource, RowMapper<T> rowMapper) {
|
||||
return this.jdbc.query(sql, parameterSource, rowMapper);
|
||||
}
|
||||
|
||||
protected final <T> List<T> queryForList(String sql, String paramName, Object value, RowMapper<T> rowMapper) {
|
||||
return this.jdbc.query(sql, new MapSqlParameterSource(paramName, value), rowMapper);
|
||||
}
|
||||
|
||||
protected final <T> Stream<T> queryForStream(String sql, SqlParameterSource parameterSource,
|
||||
RowMapper<T> rowMapper) {
|
||||
return this.jdbc.queryForStream(sql, parameterSource, rowMapper);
|
||||
}
|
||||
|
||||
protected final <T> Stream<T> queryForStream(String sql, String paramName, Object value, RowMapper<T> rowMapper) {
|
||||
return this.jdbc.queryForStream(sql, new MapSqlParameterSource(paramName, value), rowMapper);
|
||||
}
|
||||
|
||||
protected final <T> Stream<T> queryForStream(String sql, SqlParameterSource parameterSource, Class<T> elementType) {
|
||||
return this.jdbc.queryForList(sql, parameterSource, elementType).stream();
|
||||
}
|
||||
|
||||
protected final <T> Stream<T> queryForStream(String sql, String paramName, Object value, Class<T> elementType) {
|
||||
return queryForStream(sql, new MapSqlParameterSource(paramName, value), elementType);
|
||||
}
|
||||
|
||||
protected final boolean queryForBool(String sql, SqlParameterSource parameterSource) {
|
||||
Boolean result = this.jdbc.queryForObject(sql, parameterSource, Boolean.TYPE);
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
|
||||
protected final boolean queryForBool(String sql, String paramName, Object value) {
|
||||
return queryForBool(sql, new MapSqlParameterSource(paramName, value));
|
||||
}
|
||||
|
||||
protected final int update(String sql, SqlParameterSource parameterSource) {
|
||||
return this.jdbc.update(sql, parameterSource);
|
||||
}
|
||||
|
||||
protected final int update(String sql, String paramName, Object value) {
|
||||
return update(sql, new MapSqlParameterSource(paramName, value));
|
||||
}
|
||||
|
||||
protected final long batchUpdate(String sql, SqlParameterSource[] batchArgs) {
|
||||
int[] i = this.jdbc.batchUpdate(sql, batchArgs);
|
||||
return Numbers.sum(i);
|
||||
}
|
||||
|
||||
protected final <T> long batchUpdate(String sql, Stream<T> c,
|
||||
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
|
||||
int[] i = this.jdbc.batchUpdate(sql, buildSqlParameterSourceArray(c, paramSourceBuilder));
|
||||
return Numbers.sum(i);
|
||||
}
|
||||
|
||||
protected final <T> long batchUpdate(String sql, Collection<T> c,
|
||||
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
|
||||
int[] i = this.jdbc.batchUpdate(sql, buildSqlParameterSourceArray(c, paramSourceBuilder));
|
||||
return Numbers.sum(i);
|
||||
}
|
||||
|
||||
protected static <T> void assertResultEquals(T result, T expectedValue) {
|
||||
if (!Objects.equals(result, expectedValue)) {
|
||||
throw new DataOperationResultException();
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T> void assertResultEquals(T result, T expectedValue, Function<T, String> errMsg) {
|
||||
if (!Objects.equals(result, expectedValue)) {
|
||||
throw new DataOperationResultException(errMsg.apply(result));
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T> void assertResultEquals(T result, T expectedValue, String msgTemplate, Object... args) {
|
||||
if (!Objects.equals(result, expectedValue)) {
|
||||
throw new DataOperationResultException(String.format(msgTemplate, args));
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T, E extends Throwable> void assertResultEqualsOrThrow(T result, T expectedValue,
|
||||
Function<T, E> e) throws E {
|
||||
if (!Objects.equals(result, expectedValue)) {
|
||||
throw e.apply(result);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void assertUpdateOneRow(int result) {
|
||||
assertResultEquals(result, 1);
|
||||
}
|
||||
|
||||
protected static void assertUpdateOneRow(int result, Function<Integer, String> errMsg) {
|
||||
assertResultEquals(result, 1, errMsg);
|
||||
}
|
||||
|
||||
protected static void assertUpdateOneRow(int result, String msgTemplate, Object... args) {
|
||||
assertResultEquals(result, 1, msgTemplate, args);
|
||||
}
|
||||
|
||||
protected static <E extends Throwable> void assertUpdateOneRowOrThrow(int result, Function<Integer, E> e)
|
||||
throws E {
|
||||
assertResultEqualsOrThrow(result, 1, e);
|
||||
}
|
||||
|
||||
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
|
||||
T[] c,
|
||||
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
|
||||
if (c == null || c.length == 0) {
|
||||
return new SqlParameterSource[] {};
|
||||
}
|
||||
return buildSqlParameterSourceArray(Arrays.stream(c), paramSourceBuilder);
|
||||
}
|
||||
|
||||
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
|
||||
Collection<T> c,
|
||||
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
|
||||
if (CollectionUtils.isEmpty(c)) {
|
||||
return new SqlParameterSource[] {};
|
||||
}
|
||||
return buildSqlParameterSourceArray(c.stream(), paramSourceBuilder);
|
||||
}
|
||||
|
||||
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
|
||||
Stream<T> stream,
|
||||
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
|
||||
Objects.requireNonNull(stream);
|
||||
Objects.requireNonNull(paramSourceBuilder);
|
||||
return stream.map(paramSourceBuilder).toArray(SqlParameterSource[]::new);
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ import java.sql.SQLException;
|
||||
*
|
||||
* <p>
|
||||
* 通过在 {@link #map(ResultSet)} 中配置 {@link ResultSet} 到对象的映射,
|
||||
* 可将 {@link #rowMapper(ResultSet, int)} 的方法应用,
|
||||
* 可将 {@link #rowMapper(ResultSet, int)} 的方法引用,
|
||||
* 直接当成 {@link org.springframework.jdbc.core.RowMapper} 对象传给
|
||||
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
|
||||
* 的查询方法,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package xyz.zhouxy.plusone.mail;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -15,11 +14,10 @@ import org.springframework.mail.javamail.JavaMailSender;
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(PlusoneMailProperties.class)
|
||||
@ConditionalOnClass(MailService.class)
|
||||
@EnableAutoConfiguration
|
||||
public class PlusoneMailAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public MailService mailService(JavaMailSender mailSender, PlusoneMailProperties mailProperties) {
|
||||
MailService mailService(JavaMailSender mailSender, PlusoneMailProperties mailProperties) {
|
||||
MailMessageFactory mailMessageFactory = new MailMessageFactory(mailProperties);
|
||||
return new SimpleMailService(mailSender, mailMessageFactory);
|
||||
}
|
||||
|
@@ -0,0 +1,92 @@
|
||||
package xyz.zhouxy.plusone.oss;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.csource.common.MyException;
|
||||
import org.csource.fastdfs.ClientGlobal;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import xyz.zhouxy.plusone.oss.FastDFSProperties.ConnectionPool;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FastDFSProperties.class)
|
||||
@ConditionalOnClass(FastDFSUtil.class)
|
||||
public class FastDFSAutoConfig {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
FastDFSUtil fastDFSUtil(FastDFSProperties props) throws IOException, FastDFSException {
|
||||
|
||||
List<String> trackerServerStrList = props.getTrackerServers();
|
||||
if (CollectionUtils.isEmpty(trackerServerStrList)) {
|
||||
throw new FastDFSException(
|
||||
String.format("configure item %s is required - ", ClientGlobal.PROP_KEY_TRACKER_SERVERS));
|
||||
}
|
||||
try {
|
||||
InetSocketAddress[] trackerServers = trackerServerStrList.stream()
|
||||
.map(trackerServer -> {
|
||||
String[] hostPort = trackerServer.trim().split(":");
|
||||
String host = hostPort[0].trim();
|
||||
int port = Integer.parseInt(hostPort[1].trim());
|
||||
return new InetSocketAddress(host, port);
|
||||
})
|
||||
.toArray(InetSocketAddress[]::new);
|
||||
ClientGlobal.initByTrackers(trackerServers);
|
||||
|
||||
var connectTimeoutInSecondsConf = props.getConnectTimeoutInSeconds();
|
||||
if (connectTimeoutInSecondsConf != null) {
|
||||
ClientGlobal.setG_connect_timeout(connectTimeoutInSecondsConf * 1000);
|
||||
}
|
||||
var networkTimeoutInSecondsConf = props.getNetworkTimeoutInSeconds();
|
||||
if (networkTimeoutInSecondsConf != null) {
|
||||
ClientGlobal.setG_network_timeout(networkTimeoutInSecondsConf * 1000);
|
||||
}
|
||||
var charsetConf = props.getCharset();
|
||||
if (StringUtils.hasText(charsetConf)) {
|
||||
ClientGlobal.setG_charset(charsetConf);
|
||||
}
|
||||
var httpAntiStealTokenConf = props.getHttpAntiStealToken();
|
||||
if (httpAntiStealTokenConf != null) {
|
||||
ClientGlobal.setG_anti_steal_token(httpAntiStealTokenConf);
|
||||
}
|
||||
var httpSecretKeyConf = props.getHttpSecretKey();
|
||||
if (StringUtils.hasText(httpSecretKeyConf)) {
|
||||
ClientGlobal.setG_secret_key(httpSecretKeyConf);
|
||||
}
|
||||
var httpTrackerHttpPortConf = props.getHttpTrackerHttpPort();
|
||||
if (httpTrackerHttpPortConf != null) {
|
||||
ClientGlobal.setG_tracker_http_port(httpTrackerHttpPortConf);
|
||||
}
|
||||
|
||||
ConnectionPool connectionPool = props.getConnectionPool();
|
||||
var poolEnabled = Objects.nonNull(connectionPool)
|
||||
&& Boolean.TRUE.equals(connectionPool.getEnabled());
|
||||
|
||||
if (poolEnabled) {
|
||||
var poolMaxCountPerEntry = connectionPool.getMaxCountPerEntry();
|
||||
if (poolMaxCountPerEntry != null) {
|
||||
ClientGlobal.g_connection_pool_max_count_per_entry = poolMaxCountPerEntry;
|
||||
}
|
||||
var poolMaxIdleTime = connectionPool.getMaxIdleTime();
|
||||
if (poolMaxIdleTime != null) {
|
||||
ClientGlobal.g_connection_pool_max_idle_time = poolMaxIdleTime * 1000;
|
||||
}
|
||||
var poolMaxWaitTimeInMS = connectionPool.getMaxWaitTimeInMs();
|
||||
if (poolMaxWaitTimeInMS != null) {
|
||||
ClientGlobal.g_connection_pool_max_wait_time_in_ms = poolMaxWaitTimeInMS;
|
||||
}
|
||||
}
|
||||
return new FastDFSUtil();
|
||||
} catch (MyException e) {
|
||||
throw new FastDFSException(e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package xyz.zhouxy.plusone.oss;
|
||||
|
||||
/**
|
||||
* 封装 FastDFS 的 {@link org.csource.common.MyException}
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public class FastDFSException extends Exception {
|
||||
private static final long serialVersionUID = 7871031982887742468L;
|
||||
|
||||
public FastDFSException() {
|
||||
}
|
||||
|
||||
public FastDFSException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FastDFSException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FastDFSException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FastDFSException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
package xyz.zhouxy.plusone.oss;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties("fastdfs")
|
||||
public class FastDFSProperties {
|
||||
private Integer connectTimeoutInSeconds;
|
||||
private Integer networkTimeoutInSeconds;
|
||||
private String charset;
|
||||
private Boolean httpAntiStealToken;
|
||||
private String httpSecretKey;
|
||||
private Integer httpTrackerHttpPort;
|
||||
private List<String> trackerServers;
|
||||
|
||||
private ConnectionPool connectionPool;
|
||||
|
||||
public Integer getConnectTimeoutInSeconds() {
|
||||
return connectTimeoutInSeconds;
|
||||
}
|
||||
|
||||
public void setConnectTimeoutInSeconds(Integer connectTimeoutInSeconds) {
|
||||
this.connectTimeoutInSeconds = connectTimeoutInSeconds;
|
||||
}
|
||||
|
||||
public Integer getNetworkTimeoutInSeconds() {
|
||||
return networkTimeoutInSeconds;
|
||||
}
|
||||
|
||||
public void setNetworkTimeoutInSeconds(Integer networkTimeoutInSeconds) {
|
||||
this.networkTimeoutInSeconds = networkTimeoutInSeconds;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public Boolean getHttpAntiStealToken() {
|
||||
return httpAntiStealToken;
|
||||
}
|
||||
|
||||
public void setHttpAntiStealToken(Boolean httpAntiStealToken) {
|
||||
this.httpAntiStealToken = httpAntiStealToken;
|
||||
}
|
||||
|
||||
public String getHttpSecretKey() {
|
||||
return httpSecretKey;
|
||||
}
|
||||
|
||||
public void setHttpSecretKey(String httpSecretKey) {
|
||||
this.httpSecretKey = httpSecretKey;
|
||||
}
|
||||
|
||||
public Integer getHttpTrackerHttpPort() {
|
||||
return httpTrackerHttpPort;
|
||||
}
|
||||
|
||||
public void setHttpTrackerHttpPort(Integer httpTrackerHttpPort) {
|
||||
this.httpTrackerHttpPort = httpTrackerHttpPort;
|
||||
}
|
||||
|
||||
public List<String> getTrackerServers() {
|
||||
return trackerServers;
|
||||
}
|
||||
|
||||
public void setTrackerServers(List<String> trackerServers) {
|
||||
this.trackerServers = trackerServers;
|
||||
}
|
||||
|
||||
public ConnectionPool getConnectionPool() {
|
||||
return connectionPool;
|
||||
}
|
||||
|
||||
public void setConnectionPool(ConnectionPool connectionPool) {
|
||||
this.connectionPool = connectionPool;
|
||||
}
|
||||
|
||||
public static class ConnectionPool {
|
||||
private Boolean enabled;
|
||||
private Integer maxCountPerEntry;
|
||||
private Integer maxIdleTime;
|
||||
private Integer maxWaitTimeInMs;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Integer getMaxCountPerEntry() {
|
||||
return maxCountPerEntry;
|
||||
}
|
||||
|
||||
public void setMaxCountPerEntry(Integer maxCountPerEntry) {
|
||||
this.maxCountPerEntry = maxCountPerEntry;
|
||||
}
|
||||
|
||||
public Integer getMaxIdleTime() {
|
||||
return maxIdleTime;
|
||||
}
|
||||
|
||||
public void setMaxIdleTime(Integer maxIdleTime) {
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
}
|
||||
|
||||
public Integer getMaxWaitTimeInMs() {
|
||||
return maxWaitTimeInMs;
|
||||
}
|
||||
|
||||
public void setMaxWaitTimeInMs(Integer maxWaitTimeInMs) {
|
||||
this.maxWaitTimeInMs = maxWaitTimeInMs;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,193 @@
|
||||
package xyz.zhouxy.plusone.oss;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.csource.common.MyException;
|
||||
import org.csource.common.NameValuePair;
|
||||
import org.csource.fastdfs.FileInfo;
|
||||
import org.csource.fastdfs.StorageClient;
|
||||
import org.csource.fastdfs.StorageServer;
|
||||
import org.csource.fastdfs.TrackerClient;
|
||||
import org.csource.fastdfs.TrackerServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import lombok.Getter;
|
||||
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
|
||||
import xyz.zhouxy.plusone.exception.SysException;
|
||||
|
||||
public class FastDFSUtil {
|
||||
|
||||
private final TrackerServer trackerServer;
|
||||
private final StorageServer storageServer;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FastDFSUtil.class);
|
||||
|
||||
FastDFSUtil() throws IOException, MyException {
|
||||
TrackerClient trackerClient = new TrackerClient();
|
||||
this.trackerServer = trackerClient.getTrackerServer();
|
||||
this.storageServer = trackerClient.getStoreStorage(trackerServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到 Fast DFS,失败将抛异常
|
||||
*
|
||||
* @param file 文件信息
|
||||
* @return 2 elements string array if success:<br>
|
||||
* <ul>
|
||||
* <li>results[0]: the group name to store the file</li>
|
||||
* </ul>
|
||||
* <ul>
|
||||
* <li>results[1]: the new created filename</li>
|
||||
* </ul>
|
||||
* return null if fail
|
||||
* @throws FastDFSException
|
||||
*/
|
||||
public String[] upload(FastDFSFile file) throws FastDFSException {
|
||||
logger.info("File Name: {}, File Length: {}", file.getFileName(), file.getContent().length);
|
||||
|
||||
NameValuePair[] metaList = new NameValuePair[1];
|
||||
metaList[0] = new NameValuePair("author", file.getAuthor());
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
StorageClient storageClient = null;
|
||||
String[] uploadResults = null;
|
||||
try {
|
||||
storageClient = new StorageClient(this.trackerServer, this.storageServer);
|
||||
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), metaList);
|
||||
|
||||
if (uploadResults == null) {
|
||||
throw new FastDFSException("upload file fail, error code: " + storageClient.getErrorCode());
|
||||
}
|
||||
logger.info("Upload file successfully!!! group_name: {}, remoteFileName: {}, time used: {} ms",
|
||||
uploadResults[0], uploadResults[1], System.currentTimeMillis() - startTime);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new FastDFSException("IO Exception when uploadind the file:" + file.getFileName(), e);
|
||||
} catch (MyException e) {
|
||||
throw new FastDFSException(e);
|
||||
}
|
||||
|
||||
return uploadResults;
|
||||
}
|
||||
|
||||
public FileInfo getFile(String groupName, String remoteFileName) throws FastDFSException {
|
||||
try {
|
||||
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
|
||||
return storageClient.get_file_info(groupName, remoteFileName);
|
||||
} catch (IOException e) {
|
||||
throw new FastDFSException("IO Exception: Get File from Fast DFS failed", e);
|
||||
} catch (Exception e) {
|
||||
throw new FastDFSException("Non IO Exception: Get File from Fast DFS failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream downFile(String groupName, String remoteFileName) throws FastDFSException {
|
||||
try {
|
||||
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
|
||||
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
|
||||
InputStream ins = new ByteArrayInputStream(fileByte);
|
||||
return ins;
|
||||
} catch (IOException e) {
|
||||
throw new FastDFSException("IO Exception: Get File from Fast DFS failed", e);
|
||||
} catch (Exception e) {
|
||||
throw new FastDFSException("Non IO Exception: Get File from Fast DFS failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteFile(String groupName, String remoteFileName) throws FastDFSException {
|
||||
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
|
||||
try {
|
||||
int i = storageClient.delete_file(groupName, remoteFileName);
|
||||
if (i == 0) {
|
||||
logger.info("Delete file SUCCESSFULLY!!!");
|
||||
} else {
|
||||
throw new FastDFSException("Delete file failed, error code is: " + i);
|
||||
}
|
||||
} catch (IOException | MyException e) {
|
||||
throw new FastDFSException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FastDFSFile {
|
||||
@Getter
|
||||
private final String fileName;
|
||||
private final byte[] content;
|
||||
@Getter
|
||||
private final String ext;
|
||||
@Getter
|
||||
private final String md5;
|
||||
@Getter
|
||||
private final String author;
|
||||
|
||||
private FastDFSFile(@Nonnull File file, @Nullable String author) throws IOException {
|
||||
this.fileName = file.getName();
|
||||
this.content = Files.toByteArray(file);
|
||||
this.ext = Files.getFileExtension(fileName);
|
||||
this.md5 = md5Hex(content);
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
private FastDFSFile(@Nonnull String fileName, @Nonnull byte[] content, @Nullable String author) {
|
||||
this.fileName = fileName;
|
||||
this.content = content;
|
||||
this.ext = Files.getFileExtension(fileName);
|
||||
this.md5 = md5Hex(content);
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@StaticFactoryMethod(FastDFSFile.class)
|
||||
public static FastDFSFile of(@Nonnull File file) throws IOException {
|
||||
return new FastDFSFile(file, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@StaticFactoryMethod(FastDFSFile.class)
|
||||
public static FastDFSFile of(@Nonnull File file, @Nullable String author) throws IOException {
|
||||
return new FastDFSFile(file, author);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@StaticFactoryMethod(FastDFSFile.class)
|
||||
public static FastDFSFile of(@Nonnull String fileName, @Nonnull byte[] content) {
|
||||
return new FastDFSFile(fileName, content, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@StaticFactoryMethod(FastDFSFile.class)
|
||||
public static FastDFSFile of(@Nonnull String fileName, @Nonnull byte[] content, @Nullable String author) {
|
||||
return new FastDFSFile(fileName, content, author);
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return Arrays.copyOf(content, content.length);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String md5Hex(byte[] data) {
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
messageDigest.update(data);
|
||||
byte[] result = messageDigest.digest();
|
||||
var sha512Hex = new BigInteger(1, result).toString(16);
|
||||
return Objects.requireNonNull(sha512Hex);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw SysException.of(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,17 +11,12 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(value = {
|
||||
SmsProperties.class,
|
||||
SmsCredentialProperties.class,
|
||||
SmsClientProperties.class,
|
||||
SmsHttpProperties.class,
|
||||
SmsProxyProperties.class})
|
||||
@EnableConfigurationProperties(SmsProperties.class)
|
||||
@ConditionalOnClass(SmsService.class)
|
||||
public class PlusoneSmsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SmsService smsService(SmsProperties smsProperties) {
|
||||
SmsService smsService(SmsProperties smsProperties) {
|
||||
return new TencentSmsServiceImpl(smsProperties);
|
||||
}
|
||||
}
|
||||
|
@@ -4,14 +4,11 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* SMS 相关参数
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("plusone.sms")
|
||||
public class SmsProperties {
|
||||
private String region;
|
||||
@@ -19,33 +16,138 @@ public class SmsProperties {
|
||||
private SmsClientProperties client;
|
||||
private String appId;
|
||||
private Map<String, String> templates;
|
||||
}
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties("plusone.sms.credential")
|
||||
class SmsCredentialProperties {
|
||||
private String secretId;
|
||||
private String secretKey;
|
||||
}
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties("plusone.sms.client")
|
||||
class SmsClientProperties {
|
||||
private String signMethod;
|
||||
private SmsHttpProperties http;
|
||||
}
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties("plusone.sms.client.http")
|
||||
class SmsHttpProperties {
|
||||
private SmsProxyProperties proxy;
|
||||
private String reqMethod;
|
||||
private Integer connTimeout;
|
||||
}
|
||||
public SmsCredentialProperties getCredential() {
|
||||
return credential;
|
||||
}
|
||||
|
||||
public void setCredential(SmsCredentialProperties credential) {
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
public SmsClientProperties getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(SmsClientProperties client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public Map<String, String> getTemplates() {
|
||||
return templates;
|
||||
}
|
||||
|
||||
public void setTemplates(Map<String, String> templates) {
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
public static class SmsCredentialProperties {
|
||||
private String secretId;
|
||||
private String secretKey;
|
||||
|
||||
public String getSecretId() {
|
||||
return secretId;
|
||||
}
|
||||
|
||||
public void setSecretId(String secretId) {
|
||||
this.secretId = secretId;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SmsClientProperties {
|
||||
private String signMethod;
|
||||
private SmsHttpProperties http;
|
||||
|
||||
public String getSignMethod() {
|
||||
return signMethod;
|
||||
}
|
||||
|
||||
public void setSignMethod(String signMethod) {
|
||||
this.signMethod = signMethod;
|
||||
}
|
||||
|
||||
public SmsHttpProperties getHttp() {
|
||||
return http;
|
||||
}
|
||||
|
||||
public void setHttp(SmsHttpProperties http) {
|
||||
this.http = http;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SmsHttpProperties {
|
||||
private SmsProxyProperties proxy;
|
||||
private String reqMethod;
|
||||
private Integer connTimeout;
|
||||
|
||||
public SmsProxyProperties getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
public void setProxy(SmsProxyProperties proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
public String getReqMethod() {
|
||||
return reqMethod;
|
||||
}
|
||||
|
||||
public void setReqMethod(String reqMethod) {
|
||||
this.reqMethod = reqMethod;
|
||||
}
|
||||
|
||||
public Integer getConnTimeout() {
|
||||
return connTimeout;
|
||||
}
|
||||
|
||||
public void setConnTimeout(Integer connTimeout) {
|
||||
this.connTimeout = connTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SmsProxyProperties {
|
||||
private String host;
|
||||
private Integer port;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties("plusone.sms.client.http.proxy")
|
||||
class SmsProxyProperties {
|
||||
private String host;
|
||||
private Integer port;
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package xyz.zhouxy.plusone.sms;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
@@ -8,11 +10,11 @@ import com.tencentcloudapi.sms.v20210111.SmsClient;
|
||||
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
|
||||
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
|
||||
import xyz.zhouxy.plusone.exception.PlusoneException;
|
||||
import xyz.zhouxy.plusone.exception.SysException;
|
||||
import xyz.zhouxy.plusone.sms.SmsProperties.SmsCredentialProperties;
|
||||
import xyz.zhouxy.plusone.sms.SmsProperties.SmsHttpProperties;
|
||||
import xyz.zhouxy.plusone.sms.SmsProperties.SmsProxyProperties;
|
||||
|
||||
/**
|
||||
* 使用腾讯 SMS 服务
|
||||
@@ -66,7 +68,7 @@ public class TencentSmsServiceImpl implements SmsService {
|
||||
|
||||
} catch (TencentCloudSDKException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new PlusoneException(ErrorCodeConsts.DEFAULT_ERROR_CODE, e);
|
||||
throw SysException.of(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,15 +4,13 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
public class SpringContextHolder {
|
||||
public enum SpringContextHolder {
|
||||
|
||||
INSTANCE
|
||||
;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private static final SpringContextHolder INSTANCE = new SpringContextHolder();
|
||||
|
||||
private SpringContextHolder() {
|
||||
}
|
||||
|
||||
public static ApplicationContext getContext() {
|
||||
return INSTANCE.context;
|
||||
}
|
||||
|
@@ -1,34 +0,0 @@
|
||||
package xyz.zhouxy.plusone.sql;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.ibatis.jdbc.AbstractSQL;
|
||||
|
||||
/**
|
||||
* 扩展 MyBatis 中的 SQL 语句构造器
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public class SQL extends AbstractSQL<SQL> {
|
||||
|
||||
public SQL SET_IF(boolean condition, String sets) {
|
||||
return condition ? SET(sets) : getSelf();
|
||||
}
|
||||
|
||||
public SQL SET_IF_NOT_NULL(Object param, String sets) {
|
||||
return Objects.nonNull(param) ? SET(sets) : getSelf();
|
||||
}
|
||||
|
||||
public SQL WHERE_IF(boolean condition, String sqlCondition) {
|
||||
return condition ? WHERE(sqlCondition) : getSelf();
|
||||
}
|
||||
|
||||
public SQL WHERE_IF_NOT_NULL(Object param, String sqlCondition) {
|
||||
return Objects.nonNull(param) ? WHERE(sqlCondition) : getSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQL getSelf() {
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package xyz.zhouxy.plusone.validator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class InvalidInputExceptionHandler extends BaseExceptionHandler {
|
||||
|
||||
public InvalidInputExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(exceptionInfoHolder);
|
||||
set(InvalidInputException.class, InvalidInputException.ERROR_CODE, "无效的用户输入");
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@Configuration
|
||||
public class WebCorsConfig implements WebMvcConfigurer {
|
||||
|
||||
// TODO 不放行全部,通过配置文件配置允许访问的域名
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
|
@@ -0,0 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
xyz.zhouxy.plusone.sms.PlusoneSmsAutoConfiguration,\
|
||||
xyz.zhouxy.plusone.mail.PlusoneMailAutoConfiguration,\
|
||||
xyz.zhouxy.plusone.oss.FastDFSAutoConfig
|
@@ -1,14 +1,16 @@
|
||||
package xyz.zhouxy.plusone.validatortest;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import xyz.zhouxy.plusone.constant.RegexConsts;
|
||||
import xyz.zhouxy.plusone.validator.validator2.BaseValidator2;
|
||||
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
|
||||
import xyz.zhouxy.plusone.validator.BaseValidator;
|
||||
|
||||
class BaseValidator2Test {
|
||||
class BaseValidatorTest {
|
||||
|
||||
@Test
|
||||
void testValid() {
|
||||
@@ -27,17 +29,18 @@ class LoginCommand {
|
||||
private boolean rememberMe;
|
||||
}
|
||||
|
||||
class LoginCommandValidator extends BaseValidator2<LoginCommand> {
|
||||
class LoginCommandValidator extends BaseValidator<LoginCommand> {
|
||||
|
||||
public static final LoginCommandValidator INSTANCE = new LoginCommandValidator();
|
||||
|
||||
private LoginCommandValidator() {
|
||||
ruleFor(LoginCommand::getAccount)
|
||||
ruleForString(LoginCommand::getAccount)
|
||||
.notNull("邮箱地址不能为空")
|
||||
.matchesOr(new String[] { RegexConsts.EMAIL, RegexConsts.MOBILE_PHONE }, value -> new RuntimeException('"' + value + "\" 不是邮箱地址或手机号"));
|
||||
ruleFor(LoginCommand::getPwd)
|
||||
.matchesOne(new Pattern[] { PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE },
|
||||
value -> new RuntimeException('"' + value + "\" 不是邮箱地址或手机号"));
|
||||
ruleForString(LoginCommand::getPwd)
|
||||
.notNull("密码不能为空")
|
||||
.notEmpty("密码不能为空")
|
||||
.matches(RegexConsts.PASSWORD, "密码格式错误");
|
||||
.matches(PatternConsts.PASSWORD, "密码格式错误");
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>plusone</artifactId>
|
||||
@@ -7,9 +7,7 @@
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>xyz.zhouxy</groupId>
|
||||
<artifactId>plusone-basic</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>plusone</artifactId>
|
||||
@@ -7,18 +7,11 @@
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>xyz.zhouxy</groupId>
|
||||
<artifactId>plusone-start</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<name>plusone-start</name>
|
||||
|
||||
<description>参考 DDD 落地的脚手架</description>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>2.7.6</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
|
@@ -3,14 +3,10 @@ package xyz.zhouxy.plusone;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@SpringBootApplication
|
||||
@Slf4j
|
||||
public class PlusoneApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
log.debug("Plusone started!");
|
||||
SpringApplication.run(PlusoneApplication.class, args);
|
||||
}
|
||||
|
||||
|
@@ -1,12 +0,0 @@
|
||||
package xyz.zhouxy.plusone;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class TestController {
|
||||
@RequestMapping("/test")
|
||||
public String test() throws Exception {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
@@ -1,34 +1,32 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "plusone.application.name",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.application.name'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.server.port",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "A description for 'plusone.server.port'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.debug",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "A description for 'plusone.debug'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.mail.host",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.mail.host'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.mail.password",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.mail.password'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.exception.handle-all-exception",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "A description for 'plusone.exception.handle-all-exception'"
|
||||
}
|
||||
]
|
||||
}
|
||||
{"properties": [
|
||||
{
|
||||
"name": "plusone.application.name",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.application.name'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.server.port",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "A description for 'plusone.server.port'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.debug",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "A description for 'plusone.debug'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.mail.host",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.mail.host'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.mail.password",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'plusone.mail.password'"
|
||||
},
|
||||
{
|
||||
"name": "plusone.exception.handle-all-exception",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "A description for 'plusone.exception.handle-all-exception'"
|
||||
}
|
||||
]}
|
||||
|
78
plusone-start/src/main/resources/application-public.yaml
Normal file
78
plusone-start/src/main/resources/application-public.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
# password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 数据库
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/plusone
|
||||
username: plusone
|
||||
password: XXXXXXXXXXXXXXXX
|
||||
|
||||
plusone:
|
||||
application:
|
||||
name: plusone
|
||||
server:
|
||||
port: 8108
|
||||
debug: true
|
||||
# 短信发送相关参数
|
||||
sms:
|
||||
region: ap-guangzhou
|
||||
credential:
|
||||
secret-id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
secret-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
client:
|
||||
# SDK 默认用 TC3-HMAC-SHA256 进行签名,非必要请不要修改这个字段
|
||||
sign-method: HmacSHA256
|
||||
http:
|
||||
# proxy:
|
||||
# host: xxx
|
||||
# port: xxx
|
||||
req-method: POST
|
||||
conn-timeout: 60
|
||||
app-id: 1111111111
|
||||
templates:
|
||||
'[code]': 0000000
|
||||
|
||||
# 邮件发送相关参数
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
#设置邮件发送者
|
||||
from: example@163.com
|
||||
password: XXXXXXXXXXXXXXXX
|
||||
|
||||
# 异常拦截机制是否拦截所有异常
|
||||
exception:
|
||||
handle-all-exception: false
|
||||
|
||||
fastdfs:
|
||||
connect_timeout_in_seconds: 5
|
||||
network_timeout_in_seconds: 30
|
||||
charset: UTF-8
|
||||
http_anti_steal_token: false
|
||||
http_secret_key: FastDFS1234567890
|
||||
http_tracker_http_port: 80
|
||||
tracker_servers: 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122
|
||||
connection_pool:
|
||||
enabled: true
|
||||
max_count_per_entry: 500
|
||||
max_idle_time: 3600
|
||||
max_wait_time_in_ms: 1000
|
@@ -1,79 +1,16 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: public
|
||||
|
||||
plusone:
|
||||
application:
|
||||
name: plusone
|
||||
server:
|
||||
port: 8108
|
||||
debug: true
|
||||
|
||||
# 短信发送相关参数
|
||||
sms:
|
||||
region: ap-guangzhou
|
||||
credential:
|
||||
secret-id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
secret-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
client:
|
||||
# SDK 默认用 TC3-HMAC-SHA256 进行签名,非必要请不要修改这个字段
|
||||
sign-method: HmacSHA256
|
||||
http:
|
||||
# proxy:
|
||||
# host: xxx
|
||||
# port: xxx
|
||||
req-method: POST
|
||||
conn-timeout: 60
|
||||
app-id: 1111111111
|
||||
templates:
|
||||
code: 0000000
|
||||
|
||||
# 邮件发送相关参数
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
#设置邮件发送者
|
||||
from: example@163.com
|
||||
password: XXXXXXXXXXXXXXXX
|
||||
subject:
|
||||
code: Plusone
|
||||
'[code]': Plusone
|
||||
template:
|
||||
code: 【Plusone】验证码:%s,10分钟内有效,请勿泄露。
|
||||
|
||||
# 异常拦截机制是否拦截所有异常
|
||||
exception:
|
||||
handle-all-exception: false
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
# password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 数据库
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/plusone
|
||||
username: plusone
|
||||
password: XXXXXXXXXXXXXXXX
|
||||
|
||||
'[code]': 【Plusone】验证码:%s,10分钟内有效,请勿泄露。
|
||||
# 日志配置
|
||||
logging:
|
||||
file:
|
||||
name: ${user.home}/logs/${plusone.application.name}.log
|
||||
level:
|
||||
root: INFO
|
||||
xyz.zhouxy.plusone: DEBUG
|
||||
org.springframework.jdbc.core: DEBUG
|
||||
xyz.zhouxy.plusone.system.application.query: DEBUG
|
||||
root: info
|
||||
'[xyz.zhouxy.plusone]': debug
|
||||
|
@@ -10,23 +10,23 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5level ${PID:- } - [%15.15t] %-50.50logger : %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
<file>log/output.log</file>
|
||||
<file>log/plusone.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||
<fileNamePattern>log/output.log.%i</fileNamePattern>
|
||||
<fileNamePattern>log/plusone.log.%i</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<MaxFileSize>20MB</MaxFileSize>
|
||||
</triggeringPolicy>
|
||||
</appender> -->
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<!-- <appender-ref ref="FILE" /> -->
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
@@ -0,0 +1,33 @@
|
||||
package xyz.zhouxy.plusone;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.AccountOverview;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest(classes = PlusoneApplication.class)
|
||||
class AccountQueriesTests {
|
||||
|
||||
@Resource
|
||||
AccountQueries accountQueries;
|
||||
|
||||
@Test
|
||||
void testQueryPage() {
|
||||
AccountQueryParams queryParams = new AccountQueryParams();
|
||||
// queryParams.setOrderBy(List.of("email", "mobile_phone", "id"));
|
||||
queryParams.setSize(20);
|
||||
queryParams.setPageNum(1L);
|
||||
queryParams.setOrderBy(Collections.emptyList());
|
||||
List<AccountOverview> l = accountQueries.queryAccountOverview(queryParams);
|
||||
log.info("l: {}", l);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package xyz.zhouxy.plusone;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.oss.FastDFSException;
|
||||
import xyz.zhouxy.plusone.oss.FastDFSUtil;
|
||||
import xyz.zhouxy.plusone.oss.FastDFSUtil.FastDFSFile;
|
||||
|
||||
@SpringBootTest(classes = PlusoneApplication.class)
|
||||
@Slf4j
|
||||
class FastDFSTests {
|
||||
|
||||
@Resource
|
||||
FastDFSUtil fastDFSUtil;
|
||||
|
||||
@Test
|
||||
void testOSS() throws FileNotFoundException, IOException, FastDFSException {
|
||||
String[] upload = fastDFSUtil.upload(FastDFSFile.of(new File("D:\\ZhouXY\\Desktop\\666.png")));
|
||||
log.info(String.join("/", upload));
|
||||
}
|
||||
}
|
@@ -5,14 +5,14 @@ import java.io.ObjectStreamClass;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.exception.PlusoneException;
|
||||
import xyz.zhouxy.plusone.exception.*;
|
||||
|
||||
@Slf4j
|
||||
class SerialTests {
|
||||
|
||||
@Test
|
||||
void testSerialVersionUID() {
|
||||
var cl = PlusoneException.class;
|
||||
var cl = SysException.class;
|
||||
var c = ObjectStreamClass.lookup(cl);
|
||||
var uid = c.getSerialVersionUID();
|
||||
log.info("\n @java.io.Serial" +
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package xyz.zhouxy.plusone;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -25,6 +27,7 @@ class TestAop {
|
||||
command.setPrincipal("Code108");
|
||||
command.setPassword("2333");
|
||||
command.setRememberMe(false);
|
||||
assertNotNull(service);
|
||||
LoginInfoViewObject loginInfo = service.loginByPassword(command);
|
||||
System.err.println(loginInfo);
|
||||
}
|
||||
|
16
plusone-start/src/test/resources/application.yaml
Normal file
16
plusone-start/src/test/resources/application.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: secret
|
||||
|
||||
plusone:
|
||||
# 邮件发送相关参数
|
||||
mail:
|
||||
subject:
|
||||
'[code]': Plusone
|
||||
template:
|
||||
'[code]': 【Plusone】验证码:%s,10分钟内有效,请勿泄露。
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
'[xyz.zhouxy.plusone]': debug
|
@@ -1,5 +1,5 @@
|
||||
<?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">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>plusone-system</artifactId>
|
||||
|
@@ -0,0 +1,54 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.BizException;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class AccountLoginException extends BizException {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -3040996790739138556L;
|
||||
|
||||
private static final String DEFAULT_ERR_CODE = "4030000";
|
||||
|
||||
private AccountLoginException() {
|
||||
super(DEFAULT_ERR_CODE, "用户登录异常");
|
||||
}
|
||||
|
||||
private AccountLoginException(String code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public static AccountLoginException accountNotExistException() {
|
||||
return accountNotExistException("用户账户不存在");
|
||||
}
|
||||
|
||||
public static AccountLoginException accountNotExistException(String msg) {
|
||||
return new AccountLoginException("4030101", msg);
|
||||
}
|
||||
|
||||
public static AccountLoginException otpErrorException() {
|
||||
return otpErrorException("验证码错误");
|
||||
}
|
||||
|
||||
public static AccountLoginException otpErrorException(String msg) {
|
||||
return new AccountLoginException("4030501", msg);
|
||||
}
|
||||
|
||||
public static AccountLoginException otpNotExistsException() {
|
||||
return otpNotExistsException("验证码不存在或已过期");
|
||||
}
|
||||
|
||||
public static AccountLoginException otpNotExistsException(String msg) {
|
||||
return new AccountLoginException("4030502", msg);
|
||||
}
|
||||
|
||||
public static AccountLoginException passwordErrorException() {
|
||||
return passwordErrorException("用户密码错误");
|
||||
}
|
||||
|
||||
public static AccountLoginException passwordErrorException(String msg) {
|
||||
return new AccountLoginException("4030200", msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.BizException;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class AccountRegisterException extends BizException {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 7580245181633370195L;
|
||||
|
||||
public AccountRegisterException() {
|
||||
this("4020000", "用户注册错误");
|
||||
}
|
||||
|
||||
public AccountRegisterException(String message) {
|
||||
this("4020000", message);
|
||||
}
|
||||
|
||||
public AccountRegisterException(Throwable cause) {
|
||||
super("4020000", cause);
|
||||
}
|
||||
|
||||
public AccountRegisterException(String code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public AccountRegisterException(String code, Throwable cause) {
|
||||
super(code, cause);
|
||||
}
|
||||
|
||||
public static AccountRegisterException emailOrMobilePhoneRequiredException() {
|
||||
return new AccountRegisterException("4020300", "邮箱和手机号应至少绑定一个");
|
||||
}
|
||||
|
||||
public static AccountRegisterException usernameAlreadyExists(String username) {
|
||||
return new AccountRegisterException("4020400", String.format("用户名 %s 已存在", username));
|
||||
}
|
||||
|
||||
public static AccountRegisterException emailAlreadyExists(String value) {
|
||||
return new AccountRegisterException("4020500", String.format("邮箱 %s 已存在", value));
|
||||
}
|
||||
|
||||
public static AccountRegisterException mobilePhoneAlreadyExists(String value) {
|
||||
return new AccountRegisterException("4020600", String.format("手机号 %s 已存在", value));
|
||||
}
|
||||
|
||||
public static AccountRegisterException codeErrorException() {
|
||||
return new AccountRegisterException("4020701", "校验码错误");
|
||||
}
|
||||
|
||||
public static AccountRegisterException codeNotExistsException() {
|
||||
return new AccountRegisterException("4020702", "校验码不存在或已过期");
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package xyz.zhouxy.plusone.system.application.exception;
|
||||
package xyz.zhouxy.plusone.system.application.common.exception;
|
||||
|
||||
import xyz.zhouxy.plusone.validator.InvalidInputException;
|
||||
|
||||
@@ -7,11 +7,13 @@ public class UnsupportedMenuTypeException extends InvalidInputException {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -769169844015637730L;
|
||||
|
||||
public static final String ERROR_CODE = "4040209";
|
||||
|
||||
public UnsupportedMenuTypeException() {
|
||||
this("不支持的菜单类型");
|
||||
}
|
||||
|
||||
public UnsupportedMenuTypeException(String message) {
|
||||
super(message);
|
||||
super(ERROR_CODE, message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.exception;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.BizException;
|
||||
|
||||
/**
|
||||
* 不支持的 Principal 类型出现时抛出的异常
|
||||
*
|
||||
* @author <a href="https://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
|
||||
*/
|
||||
public class UnsupportedPrincipalTypeException extends BizException {
|
||||
|
||||
private static final long serialVersionUID = 5207757290868470762L;
|
||||
|
||||
public static final String ERR_CODE = "4040201";
|
||||
private static final String DEFAULT_ERROR_MSG = "不支持的 PrincipalType";
|
||||
|
||||
public UnsupportedPrincipalTypeException() {
|
||||
super(ERR_CODE, DEFAULT_ERROR_MSG);
|
||||
}
|
||||
|
||||
public UnsupportedPrincipalTypeException(String msg) {
|
||||
super(ERR_CODE, msg);
|
||||
}
|
||||
|
||||
public UnsupportedPrincipalTypeException(Throwable cause) {
|
||||
super(ERR_CODE, cause);
|
||||
}
|
||||
|
||||
public UnsupportedPrincipalTypeException(String msg, Throwable cause) {
|
||||
super(ERR_CODE, msg, cause);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.exception.handler;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
|
||||
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class AccountLoginExceptionHandler extends BaseExceptionHandler {
|
||||
|
||||
public AccountLoginExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(exceptionInfoHolder);
|
||||
}
|
||||
|
||||
@ExceptionHandler({ AccountLoginException.class })
|
||||
public ResponseEntity<UnifiedResponse> handleException(Exception e) {
|
||||
return buildExceptionResponse(Objects.requireNonNull(e));
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
package xyz.zhouxy.plusone.system.application.exception.handler;
|
||||
package xyz.zhouxy.plusone.system.application.common.exception.handler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -6,17 +8,16 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableServiceException;
|
||||
import cn.dev33.satoken.exception.SameTokenInvalidException;
|
||||
import cn.dev33.satoken.exception.NotBasicAuthException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import cn.dev33.satoken.exception.NotSafeException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.SameTokenInvalidException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
|
||||
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
|
||||
import xyz.zhouxy.plusone.util.RestfulResult;
|
||||
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
|
||||
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
|
||||
|
||||
/**
|
||||
* Sa-Token 异常处理器
|
||||
@@ -27,8 +28,9 @@ import xyz.zhouxy.plusone.util.RestfulResult;
|
||||
@Slf4j
|
||||
public class SaTokenExceptionHandler extends BaseExceptionHandler {
|
||||
|
||||
public SaTokenExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
|
||||
|
||||
public SaTokenExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
|
||||
super(exceptionInfoHolder);
|
||||
set(NotPermissionException.class, 4030103, "会话未能通过权限认证", HttpStatus.FORBIDDEN);
|
||||
set(NotRoleException.class, 4030103, "会话未能通过角色认证", HttpStatus.FORBIDDEN);
|
||||
set(DisableServiceException.class, 4030202, "账号指定服务已被封禁", HttpStatus.FORBIDDEN);
|
||||
@@ -37,7 +39,7 @@ public class SaTokenExceptionHandler extends BaseExceptionHandler {
|
||||
set(NotSafeException.class, 4020300, "会话未能通过二级认证", HttpStatus.UNAUTHORIZED);
|
||||
set(NotLoginException.class,
|
||||
4020400,
|
||||
e -> switch (((NotLoginException) e).getType()) {
|
||||
e -> switch (e.getType()) {
|
||||
case NotLoginException.NOT_TOKEN -> "未提供 Token";
|
||||
case NotLoginException.INVALID_TOKEN -> "Token 无效";
|
||||
case NotLoginException.TOKEN_TIMEOUT -> "Token 已过期";
|
||||
@@ -50,7 +52,7 @@ public class SaTokenExceptionHandler extends BaseExceptionHandler {
|
||||
}
|
||||
|
||||
@ExceptionHandler(SaTokenException.class)
|
||||
public ResponseEntity<RestfulResult> handleSaTokenException(SaTokenException e) {
|
||||
public ResponseEntity<UnifiedResponse> handleSaTokenException(SaTokenException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return buildExceptionResponse(e);
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.BizException;
|
||||
|
||||
public class AuthenticationInfo<T> {
|
||||
|
||||
/** token 值 */
|
||||
private final String token;
|
||||
|
||||
/** 此 token 对应的LoginId,未登录时不会构建此类的实例 */
|
||||
private final T loginId;
|
||||
|
||||
/** 账号类型 */
|
||||
private final String loginType;
|
||||
|
||||
/** 登录设备类型 */
|
||||
private final String deviceType;
|
||||
|
||||
/** 自定义数据 */
|
||||
@Nonnull
|
||||
private final Map<String, Object> metadata;
|
||||
|
||||
private AuthenticationInfo(String token, T loginId, String loginType, String deviceType,
|
||||
@Nonnull Map<String, Object> metadata) {
|
||||
this.token = token;
|
||||
this.loginId = loginId;
|
||||
this.loginType = loginType;
|
||||
this.deviceType = deviceType;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public T getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
public String getLoginType() {
|
||||
return loginType;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
private Object getObjFromMetadata(String key) {
|
||||
if (this.metadata.containsKey(key)) {
|
||||
return this.metadata.get(key);
|
||||
}
|
||||
throw BizException.of(String.format("不存在 key 为 \"%s\"的元数据。", key));
|
||||
}
|
||||
|
||||
public OptionalDouble getMetadataAsDouble(String key) {
|
||||
Object valObj = getObjFromMetadata(key);
|
||||
if (valObj instanceof Double val) {
|
||||
return OptionalDouble.of(val.doubleValue());
|
||||
}
|
||||
return OptionalDouble.empty();
|
||||
}
|
||||
|
||||
public OptionalLong getMetadataAsLong(String key) {
|
||||
Object valObj = getObjFromMetadata(key);
|
||||
if (valObj instanceof Long val) {
|
||||
return OptionalLong.of(val.longValue());
|
||||
}
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
|
||||
public OptionalInt getMetadataAsInt(String key) {
|
||||
Object valObj = getObjFromMetadata(key);
|
||||
if (valObj instanceof Integer val) {
|
||||
return OptionalInt.of(val.intValue());
|
||||
}
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
public Optional<String> getMetadataAsString(String key) {
|
||||
Object valObj = getObjFromMetadata(key);
|
||||
if (valObj instanceof String val) {
|
||||
return Optional.of(val);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> Optional<C> getMetadata(String key, Class<C> type) {
|
||||
Object valObj = getObjFromMetadata(key);
|
||||
if (valObj == null || !type.isAssignableFrom(valObj.getClass())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of((C) valObj);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// builder
|
||||
|
||||
public static <T> Builder<T> builder(String token, T loginId, String loginType, String deviceType) {
|
||||
return new Builder<>(token, loginId, loginType, deviceType);
|
||||
}
|
||||
|
||||
public static final class Builder<T> {
|
||||
private final String token;
|
||||
private final T loginId;
|
||||
private final String loginType;
|
||||
private final String deviceType;
|
||||
|
||||
@Nonnull
|
||||
private final Map<String, Object> meta = new ConcurrentHashMap<>();
|
||||
|
||||
private Builder(String token, T loginId, String loginType, String deviceType) {
|
||||
this.token = token;
|
||||
this.loginId = loginId;
|
||||
this.loginType = loginType;
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public <C> Builder<T> putMetadata(String key, @Nullable C value) {
|
||||
this.meta.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthenticationInfo<T> build() {
|
||||
return new AuthenticationInfo<>(token, loginId, loginType, deviceType, meta);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.model;
|
||||
|
||||
public class PlusoneContext {
|
||||
private static final ThreadLocal<AuthenticationInfo<Long>> context = new ThreadLocal<>();
|
||||
|
||||
public static void setContext(AuthenticationInfo<Long> value) {
|
||||
context.set(value);
|
||||
}
|
||||
|
||||
public static AuthenticationInfo<Long> getContext() {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
context.remove();
|
||||
}
|
||||
|
||||
private PlusoneContext() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import xyz.zhouxy.plusone.domain.IWithOrderNumber;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
|
||||
import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
|
||||
|
||||
/**
|
||||
* 菜单工具类
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
public class MenuUtil {
|
||||
private MenuUtil() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建菜单树
|
||||
*
|
||||
* @param allMenus 菜单列表
|
||||
* @return 菜单树
|
||||
*/
|
||||
public static List<MenuViewObject> buildMenuTree(Collection<MenuViewObject> allMenus) {
|
||||
// 先排序,保证添加到 rootMenus 中的顺序,以及 addChild 添加的子菜单的顺序
|
||||
allMenus = allMenus.stream()
|
||||
.sorted(Comparator.comparing(IWithOrderNumber::getOrderNumber))
|
||||
.toList();
|
||||
|
||||
// 一级菜单
|
||||
List<MenuViewObject> rootMenus = new ArrayList<>();
|
||||
// key: 菜单 id; value: 菜单对象. 方便根据 id 查找相应对象。
|
||||
Map<Long, MenuViewObject> menuListMap = new HashMap<>();
|
||||
|
||||
for (var menu : allMenus) {
|
||||
// 添加 MENU_LIST 到 map 中,方便后面调用对象的方法
|
||||
if (menu.getType() == MenuType.MENU_LIST.ordinal()) {
|
||||
menuListMap.put(menu.getId(), menu);
|
||||
}
|
||||
// 一级菜单
|
||||
if (menu.getParentId() == 0) {
|
||||
rootMenus.add(menu);
|
||||
}
|
||||
}
|
||||
for (var menu : allMenus) {
|
||||
var parent = menuListMap.getOrDefault(menu.getParentId(), null);
|
||||
// 父菜单存在于 map 中,调用父菜单的 addChild 方法将当前菜单添加为父菜单的子菜单。
|
||||
if (parent != null) {
|
||||
parent.addChild(menu);
|
||||
}
|
||||
}
|
||||
|
||||
return rootMenus;
|
||||
}
|
||||
}
|
@@ -1,18 +1,20 @@
|
||||
package xyz.zhouxy.plusone.system.application.common.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.Getter;
|
||||
import xyz.zhouxy.plusone.constant.RegexConsts;
|
||||
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
|
||||
|
||||
public enum PrincipalType {
|
||||
EMAIL(RegexConsts.EMAIL),
|
||||
MOBILE_PHONE(RegexConsts.MOBILE_PHONE),
|
||||
USERNAME(RegexConsts.USERNAME)
|
||||
EMAIL(PatternConsts.EMAIL),
|
||||
MOBILE_PHONE(PatternConsts.MOBILE_PHONE),
|
||||
USERNAME(PatternConsts.USERNAME)
|
||||
;
|
||||
|
||||
@Getter
|
||||
private final String regex;
|
||||
private final Pattern regex;
|
||||
|
||||
PrincipalType(String regex) {
|
||||
PrincipalType(Pattern regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
}
|
||||
|
@@ -2,11 +2,11 @@ package xyz.zhouxy.plusone.system.application.common.util;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Email;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Username;
|
||||
import xyz.zhouxy.plusone.validator.InvalidInputException;
|
||||
|
||||
/**
|
||||
* 根据字面值,判断并生成 {@link Principal} 值对象。
|
||||
@@ -16,7 +16,7 @@ import xyz.zhouxy.plusone.validator.InvalidInputException;
|
||||
* @see Username
|
||||
* @see Email
|
||||
* @see MobilePhone
|
||||
* @see InvalidInputException
|
||||
* @see UnsupportedPrincipalTypeException
|
||||
*/
|
||||
public class PrincipalUtil {
|
||||
|
||||
@@ -30,11 +30,11 @@ public class PrincipalUtil {
|
||||
}
|
||||
PrincipalType[] principalTypes = PrincipalType.values();
|
||||
for (var principalType : principalTypes) {
|
||||
if (principal.matches(principalType.getRegex())) {
|
||||
if (principalType.getRegex().matcher(principal).matches()) {
|
||||
return principalType;
|
||||
}
|
||||
}
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException();
|
||||
throw new UnsupportedPrincipalTypeException();
|
||||
}
|
||||
|
||||
public static Principal getPrincipal(@Nullable String principal) {
|
||||
@@ -48,7 +48,7 @@ public class PrincipalUtil {
|
||||
if (principalType == PrincipalType.USERNAME) {
|
||||
return Username.of(principal);
|
||||
}
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException();
|
||||
throw new UnsupportedPrincipalTypeException();
|
||||
}
|
||||
|
||||
public static Principal getEmailOrMobilePhone(@Nullable String principal) {
|
||||
@@ -59,6 +59,6 @@ public class PrincipalUtil {
|
||||
if (principalType == PrincipalType.MOBILE_PHONE) {
|
||||
return MobilePhone.of(principal);
|
||||
}
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
|
||||
throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
|
||||
}
|
||||
}
|
||||
|
@@ -1,30 +0,0 @@
|
||||
package xyz.zhouxy.plusone.system.application.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import xyz.zhouxy.plusone.system.application.service.AdminLogoutService;
|
||||
import xyz.zhouxy.plusone.util.RestfulResult;
|
||||
|
||||
/**
|
||||
* Admin 账号登出
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("logout")
|
||||
public class AdminLogoutController {
|
||||
|
||||
private final AdminLogoutService service;
|
||||
|
||||
public AdminLogoutController(AdminLogoutService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public RestfulResult execute() {
|
||||
service.execute();
|
||||
return RestfulResult.success("注销成功");
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
package xyz.zhouxy.plusone.system.application.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.PlusoneException;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class AccountLoginException extends PlusoneException {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -3040996790739138556L;
|
||||
|
||||
private static final int DEFAULT_ERR_CODE = 4030000;
|
||||
|
||||
private AccountLoginException() {
|
||||
super(DEFAULT_ERR_CODE, "用户登录异常");
|
||||
}
|
||||
|
||||
private AccountLoginException(int code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public static AccountLoginException accountNotExistException() {
|
||||
return new AccountLoginException(4030101, "用户账户不存在");
|
||||
}
|
||||
|
||||
public static AccountLoginException otpErrorException() {
|
||||
return new AccountLoginException(4030501, "验证码错误");
|
||||
}
|
||||
|
||||
public static AccountLoginException otpNotExistsException() {
|
||||
return new AccountLoginException(4030502, "验证码不存在或已过期");
|
||||
}
|
||||
|
||||
public static AccountLoginException passwordErrorException() {
|
||||
return new AccountLoginException(4030200, "用户密码错误");
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
package xyz.zhouxy.plusone.system.application.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.PlusoneException;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class AccountRegisterException extends PlusoneException {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 7580245181633370195L;
|
||||
|
||||
public AccountRegisterException() {
|
||||
this(4020000, "用户注册错误");
|
||||
}
|
||||
|
||||
public AccountRegisterException(String message) {
|
||||
this(4020000, message);
|
||||
}
|
||||
|
||||
public AccountRegisterException(Throwable cause) {
|
||||
super(4020000, cause);
|
||||
}
|
||||
|
||||
public AccountRegisterException(int code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public AccountRegisterException(int code, Throwable cause) {
|
||||
super(code, cause);
|
||||
}
|
||||
|
||||
public static AccountRegisterException emailOrMobilePhoneRequiredException() {
|
||||
return new AccountRegisterException(4020300, "邮箱和手机号应至少绑定一个");
|
||||
}
|
||||
|
||||
public static AccountRegisterException usernameAlreadyExists(String username) {
|
||||
return new AccountRegisterException(4020400, String.format("用户名 %s 已存在", username));
|
||||
}
|
||||
|
||||
public static AccountRegisterException emailAlreadyExists(String value) {
|
||||
return new AccountRegisterException(4020500, String.format("邮箱 %s 已存在", value));
|
||||
}
|
||||
|
||||
public static AccountRegisterException mobilePhoneAlreadyExists(String value) {
|
||||
return new AccountRegisterException(4020600, String.format("手机号 %s 已存在", value));
|
||||
}
|
||||
|
||||
public static AccountRegisterException codeErrorException() {
|
||||
return new AccountRegisterException(4020701, "校验码错误");
|
||||
}
|
||||
|
||||
public static AccountRegisterException codeNotExistsException() {
|
||||
return new AccountRegisterException(4020702, "校验码不存在或已过期");
|
||||
}
|
||||
}
|
@@ -2,12 +2,14 @@ package xyz.zhouxy.plusone.system.application.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import xyz.zhouxy.plusone.commons.util.PageDTO;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.AccountOverview;
|
||||
import xyz.zhouxy.plusone.util.PageDTO;
|
||||
|
||||
/**
|
||||
* 账号信息查询器
|
||||
@@ -23,6 +25,7 @@ public interface AccountQueries {
|
||||
return PageDTO.of(content, total);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
List<AccountOverview> queryAccountOverview(AccountQueryParams queryParams);
|
||||
|
||||
long count(AccountQueryParams queryParams);
|
||||
|
@@ -2,12 +2,12 @@ package xyz.zhouxy.plusone.system.application.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.jdbc.SQL;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import xyz.zhouxy.plusone.sql.SQL;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.DictQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.DictOverview;
|
||||
|
||||
@@ -26,14 +26,21 @@ public class DictQueries {
|
||||
}
|
||||
|
||||
public List<DictOverview> queryDictOverviewList(DictQueryParams queryParams) {
|
||||
String sql = new SQL()
|
||||
.SELECT("id", "dict_type", "dict_label",
|
||||
"created_by", "create_time", "updated_by", "update_time", "count")
|
||||
.FROM("view_sys_dict_overview")
|
||||
.WHERE_IF(queryParams.getDictType() != null, "dict_type LIKE '%:dictType%'")
|
||||
.WHERE_IF(queryParams.getDictLabel() != null, "dict_label LIKE '%:dictLabel%'")
|
||||
.toString();
|
||||
String sql = new SQL() {
|
||||
{
|
||||
SELECT("id", "dict_type", "dict_label",
|
||||
"created_by", "create_time", "updated_by", "update_time", "count");
|
||||
FROM("view_sys_dict_overview");
|
||||
if (queryParams.getDictType() != null) {
|
||||
WHERE("dict_type LIKE '%:dictType%'");
|
||||
}
|
||||
if (queryParams.getDictLabel() != null) {
|
||||
WHERE("dict_label LIKE '%:dictLabel%'");
|
||||
}
|
||||
}
|
||||
}.toString();
|
||||
return this.jdbcTemplate
|
||||
.query(sql, new BeanPropertySqlParameterSource(queryParams), new BeanPropertyRowMapper<>(DictOverview.class));
|
||||
.query(sql, new BeanPropertySqlParameterSource(queryParams),
|
||||
new BeanPropertyRowMapper<>(DictOverview.class));
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import xyz.zhouxy.plusone.sql.SQL;
|
||||
import org.apache.ibatis.jdbc.SQL;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.RoleQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.RoleOverview;
|
||||
|
||||
@@ -35,30 +35,47 @@ public class RoleQueries {
|
||||
* @return 查询结果
|
||||
*/
|
||||
public List<RoleOverview> query(RoleQueryParams params) {
|
||||
String b = new SQL()
|
||||
.SELECT("id")
|
||||
.FROM("sys_role")
|
||||
.WHERE_IF_NOT_NULL(params.getId(), "id = :id")
|
||||
.WHERE_IF_NOT_NULL(params.getName(), "name = :name")
|
||||
.WHERE_IF_NOT_NULL(params.getIdentifier(), "identifier = :identifier")
|
||||
.WHERE_IF_NOT_NULL(params.getStatus(), "status = :status")
|
||||
.WHERE_IF_NOT_NULL(params.getCreateTimeStart(), "create_time >= :createTimeStart")
|
||||
.WHERE_IF_NOT_NULL(params.getCreateTimeEnd(), "create_time < :createTimeEnd")
|
||||
.WHERE_IF_NOT_NULL(params.getUpdateTimeStart(), "update_time >= :updateTimeStart")
|
||||
.WHERE_IF_NOT_NULL(params.getUpdateTimeEnd(), "update_time < :updateTimeEnd")
|
||||
.LIMIT(params.getSize())
|
||||
.OFFSET(params.getOffset())
|
||||
.toString();
|
||||
var sql = """
|
||||
SELECT a.id AS id, a.name AS name, a.identifier AS identifier, a.status AS status
|
||||
FROM sys_role AS a, (
|
||||
""" + b + """
|
||||
) AS b
|
||||
WHERE a.id = b.id
|
||||
""";
|
||||
var sql = new SQL() {
|
||||
{
|
||||
SELECT("a.id AS id", "a.name AS name", "a.identifier AS identifier", "a.status AS status");
|
||||
FROM("sys_role AS a", "(" +
|
||||
new SQL() {
|
||||
{
|
||||
SELECT("id");
|
||||
FROM("sys_role");
|
||||
if (null != params.getId()) {
|
||||
WHERE("id = :id");
|
||||
}
|
||||
if (null != params.getName()) {
|
||||
WHERE("name = :name");
|
||||
}
|
||||
if (null != params.getIdentifier()) {
|
||||
WHERE("identifier = :identifier");
|
||||
}
|
||||
if (null != params.getStatus()) {
|
||||
WHERE("status = :status");
|
||||
}
|
||||
if (null != params.getCreateTimeStart()) {
|
||||
WHERE("create_time >= :createTimeStart");
|
||||
}
|
||||
if (null != params.getCreateTimeEnd()) {
|
||||
WHERE("create_time < :createTimeEnd");
|
||||
}
|
||||
if (null != params.getUpdateTimeStart()) {
|
||||
WHERE("update_time >= :updateTimeStart");
|
||||
}
|
||||
if (null != params.getUpdateTimeEnd()) {
|
||||
WHERE("update_time < :updateTimeEnd");
|
||||
}
|
||||
LIMIT(params.getSize());
|
||||
OFFSET(params.getOffset());
|
||||
}
|
||||
}.toString() + ") AS b");
|
||||
WHERE("a.id = b.id");
|
||||
}
|
||||
}.toString();
|
||||
return this.jdbcTemplate
|
||||
.query(sql, new BeanPropertySqlParameterSource(params),
|
||||
new BeanPropertyRowMapper<>(RoleOverview.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,14 +5,14 @@ import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
|
||||
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
|
||||
|
||||
/**
|
||||
* 账号信息查询参数
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@ToString
|
||||
@ToString(callSuper = true)
|
||||
public class AccountQueryParams extends PagingAndSortingQueryParams {
|
||||
|
||||
public AccountQueryParams() {
|
||||
@@ -29,7 +29,6 @@ public class AccountQueryParams extends PagingAndSortingQueryParams {
|
||||
"update_time");
|
||||
}
|
||||
|
||||
// TODO【添加】 注解参数校验
|
||||
private @Getter @Setter Long id;
|
||||
private @Getter @Setter String username;
|
||||
private @Getter @Setter String email;
|
||||
|
@@ -4,7 +4,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
|
||||
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
|
||||
|
||||
/**
|
||||
* 数据字典查询参数
|
||||
|
@@ -5,7 +5,7 @@ import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
|
||||
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
|
||||
|
||||
/**
|
||||
* 角色信息查询参数
|
||||
|
@@ -2,9 +2,10 @@ package xyz.zhouxy.plusone.system.application.query.result;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
@@ -81,18 +82,18 @@ public class MenuViewObject implements IWithOrderNumber {
|
||||
List<Action> actions;
|
||||
|
||||
// MENU_LIST
|
||||
List<MenuViewObject> children;
|
||||
SortedSet<MenuViewObject> children;
|
||||
|
||||
public void addChild(MenuViewObject child) {
|
||||
if (this.children == null) {
|
||||
this.children = new ArrayList<>();
|
||||
this.children = new TreeSet<>();
|
||||
}
|
||||
this.children.add(child);
|
||||
}
|
||||
|
||||
public void addChildren(Collection<MenuViewObject> children) {
|
||||
if (this.children == null) {
|
||||
this.children = new ArrayList<>();
|
||||
this.children = new TreeSet<>();
|
||||
}
|
||||
this.children.addAll(children);
|
||||
}
|
||||
@@ -109,7 +110,7 @@ public class MenuViewObject implements IWithOrderNumber {
|
||||
viewObject.icon = menu.getIcon();
|
||||
viewObject.hidden = menu.isHidden();
|
||||
viewObject.orderNumber = menu.getOrderNumber();
|
||||
viewObject.status = menu.getStatus().getValue();
|
||||
viewObject.status = menu.getStatus().getId();
|
||||
viewObject.remarks = menu.getRemarks();
|
||||
if (viewObject.type == MenuType.MENU_ITEM.ordinal()) {
|
||||
viewObject.component = menu.getComponent();
|
||||
@@ -121,11 +122,33 @@ public class MenuViewObject implements IWithOrderNumber {
|
||||
}
|
||||
|
||||
public List<MenuViewObject> getChildren() {
|
||||
return Objects.nonNull(this.children)
|
||||
? this.children
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(IWithOrderNumber::getOrderNumber))
|
||||
.toList()
|
||||
: null;
|
||||
return this.children == null || this.children.isEmpty()
|
||||
? Collections.emptyList()
|
||||
: new ArrayList<>(this.children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MenuViewObject other = (MenuViewObject) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!id.equals(other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package xyz.zhouxy.plusone.system.application.service;
|
||||
|
||||
import xyz.zhouxy.plusone.system.constant.AuthLogic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -10,12 +8,16 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
|
||||
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordByOtpCommand;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordCommand;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordWithoutLoginCommand;
|
||||
import xyz.zhouxy.plusone.system.constant.AuthLogic;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Account;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Email;
|
||||
@@ -23,14 +25,14 @@ import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
|
||||
|
||||
/**
|
||||
* 账号查询本身相关信息
|
||||
* 账号对当前帐号进行操作
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@Service
|
||||
public class AccountContextService {
|
||||
|
||||
private final static StpLogic adminAuthLogic = AuthLogic.adminAuthLogic;
|
||||
private static final StpLogic adminAuthLogic = AuthLogic.adminAuthLogic;
|
||||
|
||||
@Resource
|
||||
private AccountQueries accountQueries;
|
||||
@@ -47,6 +49,11 @@ public class AccountContextService {
|
||||
return accountQueries.queryAccountDetails(accountId);
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
adminAuthLogic.checkLogin();
|
||||
adminAuthLogic.logout();
|
||||
}
|
||||
|
||||
public List<MenuViewObject> getMenuTree() {
|
||||
adminAuthLogic.checkLogin();
|
||||
long accountId = adminAuthLogic.getLoginIdAsLong();
|
||||
@@ -56,7 +63,8 @@ public class AccountContextService {
|
||||
@Transactional
|
||||
public void changePassword(ChangePasswordCommand command) {
|
||||
adminAuthLogic.checkLogin();
|
||||
Account account = accountRepository.find(adminAuthLogic.getLoginIdAsLong());
|
||||
Account account = accountRepository.find(adminAuthLogic.getLoginIdAsLong())
|
||||
.orElseThrow(() -> AccountLoginException.accountNotExistException("当前所登录的账号不存在"));
|
||||
account.checkPassword(command.getPassword());
|
||||
account.changePassword(command.getNewPassword(), command.getPasswordConfirmation());
|
||||
accountRepository.save(account);
|
||||
@@ -68,12 +76,28 @@ public class AccountContextService {
|
||||
String principal = command.getAccount();
|
||||
Principal emailOrMobilePhone = PrincipalUtil.getEmailOrMobilePhone(principal);
|
||||
|
||||
Account account = emailOrMobilePhone instanceof Email
|
||||
Account account = (emailOrMobilePhone instanceof Email
|
||||
? accountRepository.findByEmail((Email) emailOrMobilePhone)
|
||||
: accountRepository.findByMobilePhone((MobilePhone) emailOrMobilePhone);
|
||||
: accountRepository.findByMobilePhone((MobilePhone) emailOrMobilePhone))
|
||||
.orElseThrow(() -> AccountLoginException.accountNotExistException("当前所登录的账号不存在"));
|
||||
account.checkPassword(command.getOldPassword());
|
||||
account.changePassword(command.getNewPassword(), command.getPasswordConfirmation());
|
||||
accountRepository.save(account);
|
||||
adminAuthLogic.logout();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void changePasswordByOtp(ChangePasswordByOtpCommand command) {
|
||||
|
||||
var principal = command.getAccount();
|
||||
boolean accountExists = switch (command.getPrincipalType()) {
|
||||
case EMAIL -> accountRepository.existsEmail(Email.of(principal));
|
||||
case MOBILE_PHONE -> accountRepository.existsMobilePhone(MobilePhone.of(principal));
|
||||
default -> throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
|
||||
};
|
||||
if (accountExists) {
|
||||
throw AccountLoginException.accountNotExistException();
|
||||
}
|
||||
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,9 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.commons.util.PageDTO;
|
||||
import xyz.zhouxy.plusone.exception.DataNotExistException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
|
||||
@@ -24,11 +26,12 @@ import xyz.zhouxy.plusone.system.application.service.command.UpdateAccountComman
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Account;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountInfo;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountStatus;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Email;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Username;
|
||||
import xyz.zhouxy.plusone.util.AssertResult;
|
||||
import xyz.zhouxy.plusone.util.PageDTO;
|
||||
|
||||
/**
|
||||
* 账号管理
|
||||
@@ -66,9 +69,13 @@ public class AccountManagementService {
|
||||
mobilePhone,
|
||||
command.getPassword(),
|
||||
command.getPasswordConfirmation(),
|
||||
command.getStatus(),
|
||||
AccountStatus.of(command.getStatus()),
|
||||
command.getRoleRefs(),
|
||||
AccountInfo.of(command.getNickname(), command.getAvatar(), command.getSex()),
|
||||
AccountInfo.builder()
|
||||
.nickname(command.getNickname())
|
||||
.avatar(command.getAvatar())
|
||||
.sex(Sex.of(command.getSex()))
|
||||
.build(),
|
||||
adminAuthLogic.getLoginIdAsLong());
|
||||
accountRepository.save(account);
|
||||
}
|
||||
@@ -76,18 +83,17 @@ public class AccountManagementService {
|
||||
public void deleteAccounts(List<Long> ids) {
|
||||
Account accountToDelete;
|
||||
for (var id : ids) {
|
||||
accountToDelete = accountRepository.find(id);
|
||||
AssertResult.nonNull(accountToDelete);
|
||||
accountToDelete = accountRepository.find(id)
|
||||
.orElseThrow(() -> new DataNotExistException("该账号不存在"));
|
||||
accountRepository.delete(accountToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAccountInfo(Long id, @Valid UpdateAccountCommand command) {
|
||||
Assert.isTrue(Objects.equals(id, command.getId()), "参数错误: id 不匹配");
|
||||
Account account = accountRepository.find(id);
|
||||
AssertResult.nonNull(account, "该账号不存在");
|
||||
account.setAccountInfo(command.getNickname(), command.getAvatar(), command.getSex());
|
||||
account.setUpdatedBy(adminAuthLogic.getLoginIdAsLong());
|
||||
Account account = accountRepository.find(id)
|
||||
.orElseThrow(() -> new DataNotExistException("该账号不存在"));
|
||||
account.setAccountInfo(command.getNickname(), command.getAvatar(), Sex.of(command.getSex()));
|
||||
accountRepository.save(account);
|
||||
}
|
||||
|
||||
@@ -96,6 +102,13 @@ public class AccountManagementService {
|
||||
return accountQueries.queryAccountOverviewPage(queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询账号详细信息,如果查不到将抛出 {@link DataNotExistException}。
|
||||
*
|
||||
* @param accountId 账号 id
|
||||
* @return 账号信息
|
||||
* @throws DataNotExistException 查询不到数据时抛出异常
|
||||
*/
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public AccountDetails queryAccountDetails(@PathVariable("accountId") Long accountId) {
|
||||
var accountDetails = accountQueries.queryAccountDetails(accountId);
|
||||
|
@@ -5,10 +5,11 @@ import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.InvalidInputException;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
|
||||
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
|
||||
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.LoginInfoViewObject;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.LoginByOtpCommand;
|
||||
@@ -17,7 +18,6 @@ import xyz.zhouxy.plusone.system.domain.model.account.Account;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Email;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Username;
|
||||
import xyz.zhouxy.plusone.validator.ValidateDto;
|
||||
|
||||
@@ -43,62 +43,43 @@ public class AdminLoginService {
|
||||
|
||||
@ValidateDto
|
||||
public LoginInfoViewObject loginByPassword(LoginByPasswordCommand command) {
|
||||
Principal principal = PrincipalUtil.getPrincipal(command.getPrincipal());
|
||||
Account account;
|
||||
if (principal instanceof Email) {
|
||||
account = accountRepository.findByEmail((Email) principal);
|
||||
} else if (principal instanceof MobilePhone) {
|
||||
account = accountRepository.findByMobilePhone((MobilePhone) principal);
|
||||
} else {
|
||||
account = accountRepository.findByUsername((Username) principal);
|
||||
}
|
||||
var principal = command.getPrincipal();
|
||||
Account account = (switch (command.getPrincipalType()) {
|
||||
case USERNAME -> accountRepository.findByUsername(Username.of(principal));
|
||||
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
|
||||
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
|
||||
}).orElseThrow(AccountLoginException::accountNotExistException);
|
||||
var isPasswordCorrect = account.checkPassword(command.getPassword());
|
||||
Assert.isTrue(isPasswordCorrect, AccountLoginException::passwordErrorException);
|
||||
|
||||
if (account == null) {
|
||||
throw AccountLoginException.accountNotExistException();
|
||||
}
|
||||
@SuppressWarnings("null")
|
||||
boolean isPasswordCorrect = account.checkPassword(command.getPassword());
|
||||
if (!isPasswordCorrect) {
|
||||
throw AccountLoginException.passwordErrorException();
|
||||
}
|
||||
adminAuthLogic.login(account.getId().orElseThrow(), command.isRememberMe());
|
||||
|
||||
var accountDetails = accountQueries.queryAccountDetails(account.getId().orElseThrow());
|
||||
return LoginInfoViewObject.of(adminAuthLogic.getTokenValue(), accountDetails);
|
||||
}
|
||||
|
||||
@ValidateDto
|
||||
public LoginInfoViewObject loginByOtp(LoginByOtpCommand command) {
|
||||
String principal = command.getPrincipal();
|
||||
PrincipalType principalType = PrincipalUtil.getPrincipalType(principal);
|
||||
String otp = command.getOtp();
|
||||
boolean rememberMe = command.isRememberMe();
|
||||
var principal = command.getPrincipal();
|
||||
Account account = (switch (command.getPrincipalType()) {
|
||||
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
|
||||
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
|
||||
default -> throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
|
||||
}).orElseThrow(AccountLoginException::accountNotExistException);
|
||||
|
||||
Account account;
|
||||
if (principalType == PrincipalType.EMAIL) {
|
||||
account = accountRepository.findByEmail(Email.of(principal));
|
||||
} else if (principalType == PrincipalType.MOBILE_PHONE) {
|
||||
account = accountRepository.findByMobilePhone(MobilePhone.of(principal));
|
||||
} else {
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
|
||||
}
|
||||
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());
|
||||
|
||||
if (account == null) {
|
||||
throw AccountLoginException.accountNotExistException();
|
||||
}
|
||||
mailAndSmsVerifyService.checkOtp(principal, otp);
|
||||
adminAuthLogic.login(account.getId().orElseThrow(), rememberMe);
|
||||
adminAuthLogic.login(account.getId().orElseThrow(), command.isRememberMe());
|
||||
|
||||
var accountDetails = accountQueries.queryAccountDetails(account.getId().orElseThrow());
|
||||
return LoginInfoViewObject.of(adminAuthLogic.getTokenValue(), accountDetails);
|
||||
}
|
||||
|
||||
public void sendOtp(String principal) {
|
||||
Principal emailOrMobilePhone = PrincipalUtil.getEmailOrMobilePhone(principal);
|
||||
if (emailOrMobilePhone instanceof Email) {
|
||||
mailAndSmsVerifyService.sendOtpToEmail((Email) emailOrMobilePhone);
|
||||
PrincipalType principalType = PrincipalUtil.getPrincipalType(principal);
|
||||
if (principalType == PrincipalType.EMAIL) {
|
||||
mailAndSmsVerifyService.sendOtpToEmail(Email.of(principal));
|
||||
} else {
|
||||
mailAndSmsVerifyService.sendOtpToMobilePhone((MobilePhone) emailOrMobilePhone);
|
||||
mailAndSmsVerifyService.sendOtpToMobilePhone(MobilePhone.of(principal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
package xyz.zhouxy.plusone.system.application.service;
|
||||
|
||||
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Admin 账号登出
|
||||
*
|
||||
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
|
||||
*/
|
||||
@Service
|
||||
public class AdminLogoutService {
|
||||
|
||||
public void execute() {
|
||||
adminAuthLogic.checkLogin();
|
||||
adminAuthLogic.logout();
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.DataNotExistException;
|
||||
import xyz.zhouxy.plusone.system.application.query.DictQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.DictQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.DictOverview;
|
||||
@@ -45,21 +46,21 @@ public class DictManagementService {
|
||||
public void deleteDicts(List<Long> ids) {
|
||||
Dict dictToDelete;
|
||||
for (Long id : ids) {
|
||||
dictToDelete = dictRepository.find(id);
|
||||
dictToDelete = dictRepository.find(id).orElseThrow(DataNotExistException::new);
|
||||
dictRepository.delete(dictToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDict(Long id, @Valid UpdateDictCommand command) {
|
||||
Assert.isTrue(Objects.equals(id, command.getId()), "id 不匹配");
|
||||
Dict dictToUpdate = dictRepository.find(command.getId());
|
||||
Dict dictToUpdate = dictRepository.find(command.getId()).orElseThrow(DataNotExistException::new);
|
||||
dictToUpdate.updateDict(command.getDictType(), command.getDictLabel(), command.getKeyLabelMap());
|
||||
dictRepository.save(dictToUpdate);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public Dict findDictDetails(Long dictId) {
|
||||
return dictRepository.find(dictId);
|
||||
return dictRepository.find(dictId).orElseThrow(DataNotExistException::new);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
|
@@ -9,8 +9,8 @@ import org.springframework.util.Assert;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import xyz.zhouxy.plusone.mail.MailService;
|
||||
import xyz.zhouxy.plusone.sms.SmsService;
|
||||
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
|
||||
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Email;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package xyz.zhouxy.plusone.system.application.service;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -13,8 +12,10 @@ import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import xyz.zhouxy.plusone.domain.IWithOrderNumber;
|
||||
import xyz.zhouxy.plusone.system.application.exception.UnsupportedMenuTypeException;
|
||||
import xyz.zhouxy.plusone.commons.util.MoreCollections;
|
||||
import xyz.zhouxy.plusone.constant.EntityStatus;
|
||||
import xyz.zhouxy.plusone.exception.DataNotExistException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedMenuTypeException;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.CreateMenuCommand;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.UpdateMenuCommand;
|
||||
@@ -22,7 +23,6 @@ import xyz.zhouxy.plusone.system.domain.model.menu.Menu;
|
||||
import xyz.zhouxy.plusone.system.domain.model.menu.MenuConstructor;
|
||||
import xyz.zhouxy.plusone.system.domain.model.menu.MenuRepository;
|
||||
import xyz.zhouxy.plusone.system.domain.service.MenuService;
|
||||
import xyz.zhouxy.plusone.util.AssertResult;
|
||||
|
||||
/**
|
||||
* 菜单管理
|
||||
@@ -66,7 +66,7 @@ public class MenuManagementService {
|
||||
command.getIcon(),
|
||||
command.getHidden(),
|
||||
command.getOrderNumber(),
|
||||
command.getStatus(),
|
||||
EntityStatus.of(command.getStatus()),
|
||||
command.getRemarks());
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class MenuManagementService {
|
||||
command.getIcon(),
|
||||
command.getHidden(),
|
||||
command.getOrderNumber(),
|
||||
command.getStatus(),
|
||||
EntityStatus.of(command.getStatus()),
|
||||
command.getComponent(),
|
||||
command.getResource(),
|
||||
command.getCache(),
|
||||
@@ -88,15 +88,16 @@ public class MenuManagementService {
|
||||
|
||||
// ==================== delete ====================
|
||||
public void deleteMenu(Long id) {
|
||||
Menu menuToDelete = menuRepository.find(id);
|
||||
AssertResult.nonNull(menuToDelete);
|
||||
Menu menuToDelete = menuRepository.find(id)
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
menuRepository.delete(menuToDelete);
|
||||
}
|
||||
|
||||
// ==================== update ====================
|
||||
public void updateMenu(Long id, @Valid UpdateMenuCommand command) {
|
||||
Assert.isTrue(Objects.equals(id, command.getId()), "id 不匹配");
|
||||
Menu menuToUpdate = menuRepository.find(command.getId());
|
||||
Menu menuToUpdate = menuRepository.find(command.getId())
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
menuToUpdate.updateMenuInfo(
|
||||
command.getMenuType(),
|
||||
command.getParentId(),
|
||||
@@ -106,7 +107,7 @@ public class MenuManagementService {
|
||||
command.getIcon(),
|
||||
command.getHidden(),
|
||||
command.getOrderNumber(),
|
||||
command.getStatus(),
|
||||
EntityStatus.of(command.getStatus()),
|
||||
command.getComponent(),
|
||||
command.getResource(),
|
||||
command.getCache(),
|
||||
@@ -118,7 +119,7 @@ public class MenuManagementService {
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public MenuViewObject findById(Long id) {
|
||||
var menu = menuRepository.find(id);
|
||||
return MenuViewObject.of(menu);
|
||||
return MenuViewObject.of(menu.orElseThrow(DataNotExistException::new));
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
@@ -137,38 +138,33 @@ public class MenuManagementService {
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public List<MenuViewObject> buildMenuTree(List<MenuViewObject> menus) {
|
||||
List<MenuViewObject> rootMenus = menus
|
||||
.stream()
|
||||
.filter(menu -> Objects.equals(menu.getParentId(), 0L))
|
||||
.toList();
|
||||
|
||||
Map<Long, MenuViewObject> allMenus = new HashMap<>();
|
||||
for (var item : menus) {
|
||||
allMenus.put(item.getId(), item);
|
||||
}
|
||||
|
||||
// 创建并填充 Map
|
||||
final Map<Long, MenuViewObject> menuMap = MoreCollections.toHashMap(menus, MenuViewObject::getId);
|
||||
for (MenuViewObject menu : menus) {
|
||||
long parentId = menu.getParentId();
|
||||
while (parentId != 0 && !allMenus.containsKey(parentId)) {
|
||||
MenuViewObject parent = findById(parentId);
|
||||
if (parent == null) {
|
||||
break;
|
||||
if (parentId != 0L) {
|
||||
while (!menuMap.containsKey(parentId)) {
|
||||
MenuViewObject parent = findById(parentId);
|
||||
if (parent == null) {
|
||||
break;
|
||||
}
|
||||
menuMap.put(parent.getId(), parent);
|
||||
parentId = parent.getParentId();
|
||||
}
|
||||
allMenus.put(parent.getId(), parent);
|
||||
parentId = parent.getParentId();
|
||||
}
|
||||
}
|
||||
|
||||
for (var menu : allMenus.values()) {
|
||||
var parent = allMenus.getOrDefault(menu.getParentId(), null);
|
||||
Collection<MenuViewObject> allMenus = menuMap.values();
|
||||
for (var menu : allMenus) {
|
||||
var parent = menuMap.get(menu.getParentId());
|
||||
if (parent != null) {
|
||||
parent.addChild(menu);
|
||||
}
|
||||
}
|
||||
|
||||
return rootMenus
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(IWithOrderNumber::getOrderNumber))
|
||||
return allMenus.stream()
|
||||
.filter(menu -> (menu != null) && Objects.equals(menu.getParentId(), 0L))
|
||||
.sorted()
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
@@ -5,10 +5,10 @@ import java.util.Set;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.zhouxy.plusone.exception.InvalidInputException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
|
||||
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
|
||||
import xyz.zhouxy.plusone.system.application.service.command.RegisterAccountCommand;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Account;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountInfo;
|
||||
@@ -69,7 +69,7 @@ public class RegisterAccountService {
|
||||
throw AccountRegisterException.emailAlreadyExists(mobilePhone.value());
|
||||
}
|
||||
} else {
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException();
|
||||
throw new UnsupportedPrincipalTypeException();
|
||||
}
|
||||
|
||||
verifyService.checkCode(emailOrMobilePhone, command.getCode());
|
||||
@@ -78,10 +78,12 @@ public class RegisterAccountService {
|
||||
Username.of(username),
|
||||
email,
|
||||
mobilePhone,
|
||||
Password.newPassword(command.getPassword(), command.getPasswordConfirmation()),
|
||||
Password.newPassword(command.getPassword(), command.getReenteredPassword()),
|
||||
AccountStatus.AVAILABLE,
|
||||
Set.of(DEFAULT_ROLE_ID),
|
||||
AccountInfo.of(command.getNickname(), command.getAvatar(), command.getSex()));
|
||||
AccountInfo.builder()
|
||||
.nickname(command.getNickname())
|
||||
.build());
|
||||
accountRepository.save(accountToSave);
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ public class RegisterAccountService {
|
||||
} else if (principalType == PrincipalType.MOBILE_PHONE) {
|
||||
verifyService.sendCodeToMobilePhone(MobilePhone.of(principal));
|
||||
} else {
|
||||
throw InvalidInputException.unsupportedPrincipalTypeException();
|
||||
throw new UnsupportedPrincipalTypeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import xyz.zhouxy.plusone.constant.EntityStatus;
|
||||
import xyz.zhouxy.plusone.exception.DataNotExistException;
|
||||
import xyz.zhouxy.plusone.system.application.query.RoleQueries;
|
||||
import xyz.zhouxy.plusone.system.application.query.params.RoleQueryParams;
|
||||
import xyz.zhouxy.plusone.system.application.query.result.RoleOverview;
|
||||
@@ -53,7 +55,7 @@ public class RoleManagementService {
|
||||
Role roleToCreate = Role.newInstance(
|
||||
command.getName(),
|
||||
command.getIdentifier(),
|
||||
command.getStatus(),
|
||||
EntityStatus.of(command.getStatus()),
|
||||
command.getRemarks(),
|
||||
menuRefs,
|
||||
permissionRefs);
|
||||
@@ -62,11 +64,11 @@ public class RoleManagementService {
|
||||
|
||||
public void updateRole(@Valid UpdateRoleCommand command) {
|
||||
Long roleId = command.getId();
|
||||
Role roleToUpdate = _roleRepository.find(roleId);
|
||||
Role roleToUpdate = _roleRepository.find(roleId).orElseThrow(DataNotExistException::new);
|
||||
roleToUpdate.update(
|
||||
command.getName(),
|
||||
command.getIdentifier(),
|
||||
command.getStatus(),
|
||||
EntityStatus.of(command.getStatus()),
|
||||
command.getRemarks(),
|
||||
Set.copyOf(_menuRepository.findByIdIn(command.getMenus())),
|
||||
Set.copyOf(_menuRepository.findPermissionsByIdIn(command.getPermissions())));
|
||||
@@ -74,7 +76,7 @@ public class RoleManagementService {
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
Role role = _roleRepository.find(id);
|
||||
Role role = _roleRepository.find(id).orElseThrow(DataNotExistException::new);
|
||||
_roleRepository.delete(role);
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ public class RoleManagementService {
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public Role findById(Long id) {
|
||||
return _roleRepository.find(id);
|
||||
return _roleRepository.find(id).orElseThrow(DataNotExistException::new);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
|
@@ -0,0 +1,11 @@
|
||||
package xyz.zhouxy.plusone.system.application.service.command;
|
||||
|
||||
import lombok.Data;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
|
||||
|
||||
@Data
|
||||
public class ChangePasswordByOtpCommand {
|
||||
String account;
|
||||
String otp;
|
||||
PrincipalType principalType;
|
||||
}
|
@@ -10,10 +10,8 @@ import javax.validation.constraints.Pattern;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import lombok.Data;
|
||||
import xyz.zhouxy.plusone.constant.RegexConsts;
|
||||
import xyz.zhouxy.plusone.commons.constant.RegexConsts;
|
||||
import xyz.zhouxy.plusone.domain.ICommand;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.AccountStatus;
|
||||
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
|
||||
|
||||
/**
|
||||
* 创建账号命令
|
||||
@@ -41,7 +39,7 @@ public class CreateAccountCommand implements ICommand {
|
||||
String passwordConfirmation;
|
||||
|
||||
@NotNull
|
||||
AccountStatus status;
|
||||
Integer status;
|
||||
|
||||
Set<Long> roleRefs;
|
||||
|
||||
@@ -52,5 +50,5 @@ public class CreateAccountCommand implements ICommand {
|
||||
@URL
|
||||
String avatar;
|
||||
|
||||
Sex sex;
|
||||
Integer sex;
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import xyz.zhouxy.plusone.constant.EntityStatus;
|
||||
import xyz.zhouxy.plusone.domain.ICommand;
|
||||
import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
|
||||
|
||||
@@ -42,7 +41,7 @@ public class CreateMenuCommand implements ICommand {
|
||||
private Integer orderNumber;
|
||||
|
||||
@NotNull
|
||||
private EntityStatus status;
|
||||
private Integer status;
|
||||
|
||||
private String component;
|
||||
|
||||
|
@@ -6,7 +6,6 @@ import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Data;
|
||||
import xyz.zhouxy.plusone.constant.EntityStatus;
|
||||
import xyz.zhouxy.plusone.domain.ICommand;
|
||||
|
||||
/**
|
||||
@@ -23,7 +22,7 @@ public class CreateRoleCommand implements ICommand {
|
||||
String identifier;
|
||||
|
||||
@NotNull
|
||||
EntityStatus status;
|
||||
Integer status;
|
||||
String remarks;
|
||||
|
||||
Set<Long> menus;
|
||||
|
@@ -2,6 +2,7 @@ package xyz.zhouxy.plusone.system.application.service.command;
|
||||
|
||||
import lombok.Data;
|
||||
import xyz.zhouxy.plusone.domain.ICommand;
|
||||
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
|
||||
|
||||
/**
|
||||
* 登录命令
|
||||
@@ -14,6 +15,7 @@ public class LoginByOtpCommand implements ICommand {
|
||||
String principal; // 邮箱地址 / 手机号
|
||||
String otp; // 密码
|
||||
boolean rememberMe; // 记住我
|
||||
PrincipalType principalType;
|
||||
|
||||
// 进入登陆界面时或刷新验证码时,前端发送图形验证码的请求,后端生成 captcha 并暂存到 redis 中,key 为 UUID,将图形和 uuid 响应给前端。
|
||||
// String uuid; // 校验码的 key
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user