Compare commits

...

55 Commits
main ... dev

Author SHA1 Message Date
ZhouXY108 e8274b1beb 添加 TODO,不放行全部域名,通过配置文件配置允许访问的域名。 2023-12-06 18:14:29 +08:00
ZhouXY108 3b1c15335b 升级 Spring Boot 版本。 2023-12-06 18:13:58 +08:00
ZhouXY108 81cefdca83 修改排序的 bug。 2023-12-06 18:13:34 +08:00
ZhouXY108 86fcd7a255 plusone-commons 中新增 BaseRuntimeException 作为基础运行时异常,原 BaseException 作为基础检查型异常。 2023-10-18 11:09:55 +08:00
ZhouXY108 15ed0e2ec6 升级 Spring Boot 版本。 2023-10-18 11:09:26 +08:00
ZhouXY108 3bb5397c49 修改 UnsupportedMenuTypeException 的错误码 2023-09-10 16:47:53 +08:00
ZhouXY108 b69983077a plusone-validator 修改了方法名 2023-09-10 16:47:30 +08:00
ZhouXY108 7f8a37803a 修改菜单树的构建 2023-09-09 18:41:00 +08:00
ZhouXY108 8414fcb010 删除 MenuUtil 2023-09-09 18:40:25 +08:00
ZhouXY108 b91ab22354 plusone-commons 修改了 IWithIntCode 的包名 2023-09-09 18:39:13 +08:00
ZhouXY108 00c5ed651e 修改异常 2023-09-09 18:38:44 +08:00
ZhouXY108 b25d7589c2 使用 UnifiedResponse 替代 RestfulResult 2023-09-09 18:38:09 +08:00
ZhouXY108 208d7c93c9 plusone-commons 修改了 BaseException 2023-09-09 18:19:58 +08:00
ZhouXY108 b870684fea 静态方法不需要 final 2023-09-09 18:18:01 +08:00
ZhouXY108 9582cc7187 plusone-commons 修改了 IWithCode 和 IWithIntCode 的包名 2023-09-09 18:17:17 +08:00
ZhouXY108 a30e5bcd12 升级依赖 2023-09-09 18:14:41 +08:00
ZhouXY108 1c02b37442 优化代码。 2023-07-16 03:28:17 +08:00
ZhouXY108 68f6ce5b8f Merge branch 'dev' of http://zhouxy.xyz:3000/ZhouXY108/plusone-admin into dev 2023-07-07 15:34:45 +08:00
ZhouXY108 bd42da6777 优化 MenuViewObject。 2023-07-07 15:33:49 +08:00
ZhouXY108 c4e7b8fd26 格式化代码。 2023-06-29 02:03:24 +08:00
ZhouXY108 e34efdfc56 去除 TODO 注释。 2023-06-29 01:44:28 +08:00
ZhouXY108 7077b84317 修改 MenuType。 2023-06-29 01:44:06 +08:00
ZhouXY108 79466d180f 优化代码。 2023-06-29 01:43:43 +08:00
ZhouXY108 dcb86f9100 标识过时方法。 2023-06-29 01:43:08 +08:00
ZhouXY108 e972899fbb IWithCode 相关接口的使用调整。 2023-06-29 01:41:55 +08:00
ZhouXY108 aa7f10008f NumberUtil 改为 Numbers。 2023-06-29 01:39:43 +08:00
ZhouXY108 b32dac3b8a ValidatableStringRecord 移动到 plusone-commons 并优化。 2023-06-29 01:37:35 +08:00
ZhouXY108 2f07ce0e5f Merge branch 'dev' of http://zhouxy.xyz:3000/ZhouXY108/plusone-admin into dev 2023-06-29 01:32:42 +08:00
ZhouXY108 30f67dfa18 领域事件相关建模。 2023-06-29 01:28:14 +08:00
ZhouXY108 87942d708a 正则相关的操作由 RegexUtil 负责。 2023-06-29 01:27:14 +08:00
ZhouXY108 d05fcbfc9c 更新 Spring Boot 版本。 2023-06-29 01:26:39 +08:00
ZhouXY108 363460fe96 调整枚举类。 2023-06-29 01:26:17 +08:00
ZhouXY108 f83a05e429 改用 ValueSet 的静态工厂方法。 2023-05-16 17:41:29 +08:00
ZhouXY108 92896a0345 Merge branch 'dev' of http://zhouxy.xyz:3000/ZhouXY108/plusone-admin into dev 2023-05-16 17:39:21 +08:00
ZhouXY108 ac3fdae2d0 改用 ValueSet 的静态工厂方法。 2023-05-16 17:39:09 +08:00
ZhouXY108 8dcf8eadac 修改注释。 2023-05-16 17:38:37 +08:00
ZhouXY108 39d8eac578 删除多余的导入。 2023-04-28 23:42:15 +08:00
ZhouXY108 3858aa16e1 更新 Spring Boot 版本。 2023-04-28 23:41:15 +08:00
ZhouXY108 4d3d0f7cc7 commit. 2023-04-20 21:29:23 +08:00
ZhouXY108 17b5b55d59 添加些许规约。 2023-04-19 19:24:00 +08:00
ZhouXY108 30f29bb4b3 更改 Password 实现。 2023-04-19 06:05:02 +08:00
ZhouXY108 104bffb0e8 Merge branch 'dev' of http://zhouxy.xyz:3000/ZhouXY108/plusone-admin into dev 2023-04-16 04:33:31 +08:00
ZhouXY108 62607dc0a4 plusone-exception-handler 的代码调整。 2023-04-16 00:54:59 +08:00
ZhouXY108 fe03b6da4d 静态工厂方法添加 @StaticFactoryMethod 注解。 2023-04-16 00:16:11 +08:00
ZhouXY108 0f145e383e 重构 FastDFSFile。 2023-04-16 00:08:36 +08:00
ZhouXY108 89584d2a48 重构代码。 2023-04-16 00:08:35 +08:00
ZhouXY108 f3017e90c0 修改错别字。 2023-04-16 00:08:35 +08:00
ZhouXY108 f1d16808c6 添加常量。 2023-04-16 00:08:35 +08:00
ZhouXY108 65d77f35c1 静态工厂方法添加 @StaticFactoryMethod 注解。 2023-04-15 14:01:46 +08:00
ZhouXY108 b14c03fc32 静态工厂方法添加 @StaticFactoryMethod 注解。 2023-04-15 14:00:20 +08:00
ZhouXY108 ba34fa4a2b 重构 FastDFSFile。 2023-04-15 13:38:00 +08:00
ZhouXY108 b015f5d1c4 重构代码。 2023-04-15 13:37:44 +08:00
ZhouXY108 6335fa03b1 Merge branch 'dev' of http://zhouxy.xyz:3000/ZhouXY108/plusone-admin into dev 2023-04-15 12:26:02 +08:00
ZhouXY108 5215fded9c 修改错别字。 2023-04-06 16:21:54 +08:00
ZhouXY108 c30cca8f6a 添加常量。 2023-04-04 18:53:38 +08:00
93 changed files with 851 additions and 551 deletions

View File

@ -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 中详细说明。

View File

@ -222,6 +222,10 @@
{
"code": 4040201,
"description": "不支持的 PrincipalType"
},
{
"code": 4040209,
"description": "不支持的菜单类型"
}
]
},

View File

@ -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>

View File

@ -14,7 +14,7 @@ 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.exception.handler.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
*
@ -62,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);
}

View File

@ -9,7 +9,7 @@ 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.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
import xyz.zhouxy.plusone.exception.SysException;
@RestControllerAdvice
@ -21,9 +21,9 @@ public class SysExceptionHandler extends BaseExceptionHandler {
}
@ExceptionHandler({ SysException.class })
public ResponseEntity<RestfulResult> handleException(@Nonnull Exception e) {
public ResponseEntity<UnifiedResponse> handleException(@Nonnull Exception e) {
log.error(e.getMessage(), e);
HttpStatus httpStatus = getHttpStatus(e);
return new ResponseEntity<>(RestfulResult.error(getErrorCode(e), "系统错误"), httpStatus);
return new ResponseEntity<>(UnifiedResponse.error(getErrorCode(e), "系统错误"), httpStatus);
}
}

View File

@ -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-basic</artifactId>

View File

@ -2,6 +2,8 @@ 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");

View File

@ -1,39 +1,39 @@
package xyz.zhouxy.plusone.exception;
import xyz.zhouxy.plusone.commons.exception.BaseException;
import xyz.zhouxy.plusone.commons.exception.BaseRuntimeException;
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public class BizException extends BaseException {
public class BizException extends BaseRuntimeException {
@java.io.Serial
private static final long serialVersionUID = -5524759033245815405L;
public static final int DEFAULT_ERROR_CODE = 4000000;
public static final String DEFAULT_ERROR_CODE = "4000000";
public BizException(int code, String msg) {
public BizException(String code, String msg) {
super(code, msg);
}
public BizException(int code, Throwable cause) {
public BizException(String code, Throwable cause) {
super(code, cause);
}
public BizException(int code, String msg, Throwable cause) {
public BizException(String code, String msg, Throwable cause) {
super(code, msg, cause);
}
public BizException(String msg) {
super(DEFAULT_ERROR_CODE, msg);
public static BizException of(String msg) {
return new BizException(DEFAULT_ERROR_CODE, msg);
}
public BizException(Throwable cause) {
super(DEFAULT_ERROR_CODE, cause);
public static BizException of(Throwable cause) {
return new BizException(DEFAULT_ERROR_CODE, cause);
}
public BizException(String msg, Throwable cause) {
super(DEFAULT_ERROR_CODE, msg, cause);
public static BizException of(String msg, Throwable cause) {
return new BizException(DEFAULT_ERROR_CODE, msg, cause);
}
}

View File

@ -14,7 +14,7 @@ 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, "数据不存在");

View File

@ -18,7 +18,7 @@ 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 DataOperationResultException() {
super(ERROR_CODE, "数据操作结果不符合预期");

View File

@ -1,34 +1,34 @@
package xyz.zhouxy.plusone.exception;
import xyz.zhouxy.plusone.commons.exception.BaseException;
import xyz.zhouxy.plusone.commons.exception.BaseRuntimeException;
public class SysException extends BaseException {
public class SysException extends BaseRuntimeException {
@java.io.Serial
private static final long serialVersionUID = 8821240827443168118L;
public static final int DEFAULT_ERROR_CODE = 5000000;
public static final String DEFAULT_ERROR_CODE = "5000000";
public SysException(int code, String msg) {
public SysException(String code, String msg) {
super(code, msg);
}
public SysException(int code, Throwable cause) {
public SysException(String code, Throwable cause) {
super(code, cause);
}
public SysException(int code, String msg, Throwable cause) {
public SysException(String code, String msg, Throwable cause) {
super(code, msg, cause);
}
public SysException(String msg) {
super(DEFAULT_ERROR_CODE, msg);
public static SysException of(String msg) {
return new SysException(DEFAULT_ERROR_CODE, msg);
}
public SysException(Throwable cause) {
super(DEFAULT_ERROR_CODE, cause);
public static SysException of(Throwable cause) {
return new SysException(DEFAULT_ERROR_CODE, cause);
}
public SysException(String msg, Throwable cause) {
super(DEFAULT_ERROR_CODE, msg, cause);
public static SysException of(String msg, Throwable cause) {
return new SysException(DEFAULT_ERROR_CODE, msg, cause);
}
}

View File

@ -14,28 +14,28 @@ 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 BizException {
* @return
*/
public static UserOperationException invalidOperation(String msg) {
return new UserOperationException(4040604, msg);
return new UserOperationException("4040604", msg);
}
}

View File

@ -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) : "";
}
}

View File

@ -1,6 +1,6 @@
package xyz.zhouxy.plusone.util;
import java.util.concurrent.ThreadLocalRandom;
import java.security.SecureRandom;
public final class RandomUtil {
private RandomUtil() {
@ -8,7 +8,7 @@ public final class RandomUtil {
}
public static String randomStr(char[] sourceCharacters, int length) {
ThreadLocalRandom random = ThreadLocalRandom.current();
SecureRandom random = new SecureRandom();
char[] result = new char[length];
for (int i = 0; i < length; i++) {
result[i] = sourceCharacters[random.nextInt(sourceCharacters.length)];

View File

@ -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>

View File

@ -21,8 +21,7 @@ public final class EntityStatus extends Enumeration<EntityStatus> {
public static final EntityStatus AVAILABLE = new EntityStatus(0, "正常");
public static final EntityStatus DISABLED = new EntityStatus(1, "禁用");
private static final ValueSet<EntityStatus> VALUE_SET = new ValueSet<>(
AVAILABLE, DISABLED);
private static final ValueSet<EntityStatus> VALUE_SET = ValueSet.of(AVAILABLE, DISABLED);
public static EntityStatus of(int id) {
return VALUE_SET.get(id);

View File

@ -17,8 +17,8 @@ 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();

View File

@ -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();
}
}

View File

@ -0,0 +1,8 @@
package xyz.zhouxy.plusone.domain;
public interface IDomainEventPublisher {
void publish(DomainEvent domainEvent);
<T extends DomainEvent> void subsrcibe(IDomainEventSubscriber<T> subscriber);
}

View File

@ -0,0 +1,11 @@
package xyz.zhouxy.plusone.domain;
/**
*
*/
public interface IDomainEventSubscriber<T extends DomainEvent> {
void handleEvent(final T event);
Class<T> subscribedToEventType();
}

View File

@ -1,41 +0,0 @@
package xyz.zhouxy.plusone.domain;
import java.util.Optional;
import java.util.regex.Pattern;
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 Pattern format;
protected ValidatableStringRecord(Pattern format) {
this.format = format;
}
@JsonIgnore
protected boolean isValid() {
return format.matcher(value).matches();
}
@JsonValue
public String value() {
return value;
}
@Override
public String toString() {
return value;
}
public static String getValueOrNull(Optional<? extends ValidatableStringRecord> s) {
return s.isPresent() ? s.get().value() : null;
}
}

View File

@ -1,7 +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>

View File

@ -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;
}
}

View File

@ -63,10 +63,12 @@ public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Seri
protected abstract T mapRow(ResultSet rs) throws SQLException;
@Deprecated
protected void setRowMapper(@Nonnull RowMapper<T> rowMapper) {
this.rowMapper = rowMapper;
}
@Deprecated
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<Optional<T>> resultSetExtractor) {
this.resultSetExtractor = resultSetExtractor;
}

View File

@ -7,8 +7,7 @@ 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>
@ -17,17 +16,19 @@ import xyz.zhouxy.plusone.spring.SpringContextHolder;
*/
public final class JdbcFactory {
private static final ApplicationContext CONTEXT = SpringContextHolder.getContext();
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 CONTEXT.getBean(JdbcTemplate.class);
return JDBC_TEMPLATE;
}
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return CONTEXT.getBean(NamedParameterJdbcTemplate.class);
return NAMED_PARAMETER_JDBC_TEMPLATE;
}
}

View File

@ -8,6 +8,9 @@ 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;
@ -19,35 +22,33 @@ public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID exte
super(namedParameterJdbcTemplate);
}
@Overridable
protected abstract void doDelete(@Nonnull T entity);
@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 Optional<T> find(ID id) {
if (id == null) {
throw new IllegalArgumentException("Id cannot be null.");
}
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);
}

View File

@ -16,7 +16,7 @@ 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.NumberUtil;
import xyz.zhouxy.plusone.commons.util.Numbers;
import xyz.zhouxy.plusone.exception.DataOperationResultException;
public abstract class PlusoneJdbcDaoSupport {
@ -88,64 +88,64 @@ public abstract class PlusoneJdbcDaoSupport {
protected final long batchUpdate(String sql, SqlParameterSource[] batchArgs) {
int[] i = this.jdbc.batchUpdate(sql, batchArgs);
return NumberUtil.sum(i);
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 NumberUtil.sum(i);
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 NumberUtil.sum(i);
return Numbers.sum(i);
}
protected static final <T> void assertResultEquals(T result, T expectedValue) {
protected static <T> void assertResultEquals(T result, T expectedValue) {
if (!Objects.equals(result, expectedValue)) {
throw new DataOperationResultException();
}
}
protected static final <T> void assertResultEquals(T result, T expectedValue, Function<T, String> errMsg) {
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 final <T> void assertResultEquals(T result, T expectedValue, String msgTemplate, Object... args) {
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 final <T, E extends Throwable> void assertResultEqualsOrThrow(T result, T expectedValue,
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 final void assertUpdateOneRow(int result) {
protected static void assertUpdateOneRow(int result) {
assertResultEquals(result, 1);
}
protected static final void assertUpdateOneRow(int result, Function<Integer, String> errMsg) {
protected static void assertUpdateOneRow(int result, Function<Integer, String> errMsg) {
assertResultEquals(result, 1, errMsg);
}
protected static final void assertUpdateOneRow(int result, String msgTemplate, Object... args) {
protected static void assertUpdateOneRow(int result, String msgTemplate, Object... args) {
assertResultEquals(result, 1, msgTemplate, args);
}
protected static final <E extends Throwable> void assertUpdateOneRowOrThrow(int result, Function<Integer, E> e)
protected static <E extends Throwable> void assertUpdateOneRowOrThrow(int result, Function<Integer, E> e)
throws E {
assertResultEqualsOrThrow(result, 1, e);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
T[] c,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
if (c == null || c.length == 0) {
@ -154,7 +154,7 @@ public abstract class PlusoneJdbcDaoSupport {
return buildSqlParameterSourceArray(Arrays.stream(c), paramSourceBuilder);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
Collection<T> c,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
if (CollectionUtils.isEmpty(c)) {
@ -163,7 +163,7 @@ public abstract class PlusoneJdbcDaoSupport {
return buildSqlParameterSourceArray(c.stream(), paramSourceBuilder);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
protected static <T> SqlParameterSource[] buildSqlParameterSourceArray(
Stream<T> stream,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
Objects.requireNonNull(stream);

View File

@ -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}
*

View File

@ -1,8 +1,17 @@
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;
@ -14,7 +23,11 @@ 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 {
@ -44,7 +57,7 @@ public class FastDFSUtil {
* @throws FastDFSException
*/
public String[] upload(FastDFSFile file) throws FastDFSException {
logger.info("File Name: {}, File Length: {}", file.getName(), file.getContent().length);
logger.info("File Name: {}, File Length: {}", file.getFileName(), file.getContent().length);
NameValuePair[] metaList = new NameValuePair[1];
metaList[0] = new NameValuePair("author", file.getAuthor());
@ -63,7 +76,7 @@ public class FastDFSUtil {
uploadResults[0], uploadResults[1], System.currentTimeMillis() - startTime);
} catch (IOException e) {
throw new FastDFSException("IO Exception when uploadind the file:" + file.getName(), e);
throw new FastDFSException("IO Exception when uploadind the file:" + file.getFileName(), e);
} catch (MyException e) {
throw new FastDFSException(e);
}
@ -109,18 +122,72 @@ public class FastDFSUtil {
}
}
@Getter
public static final class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
@Getter
private final String fileName;
private final byte[] content;
@Getter
private final String ext;
@Getter
private final String md5;
@Getter
private final String author;
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
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 = ext;
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);
}
}
}
}

View File

@ -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,8 @@ 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.BizException;
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;
@ -69,7 +68,7 @@ public class TencentSmsServiceImpl implements SmsService {
} catch (TencentCloudSDKException e) {
log.error(e.getMessage(), e);
throw new BizException(ErrorCodeConsts.DEFAULT_ERROR_CODE, e);
throw SysException.of(e);
}
}

View File

@ -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;
}

View File

@ -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("/**")

View File

@ -36,7 +36,7 @@ class LoginCommandValidator extends BaseValidator<LoginCommand> {
private LoginCommandValidator() {
ruleForString(LoginCommand::getAccount)
.notNull("邮箱地址不能为空")
.matchesOr(new Pattern[] { PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE },
.matchesOne(new Pattern[] { PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE },
value -> new RuntimeException('"' + value + "\" 不是邮箱地址或手机号"));
ruleForString(LoginCommand::getPwd)
.notNull("密码不能为空")

View File

@ -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>

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -1,12 +1,11 @@
package xyz.zhouxy.plusone;
import java.io.FileInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.annotation.Resource;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@ -24,10 +23,7 @@ class FastDFSTests {
@Test
void testOSS() throws FileNotFoundException, IOException, FastDFSException {
try (FileInputStream in = new FileInputStream("D:\\ZhouXY\\Desktop\\666.png");) {
byte[] content = IOUtils.toByteArray(in);
String[] upload = fastDFSUtil.upload(new FastDFSFile("666.png", content, "png"));
log.info(String.join("/", upload));
}
String[] upload = fastDFSUtil.upload(FastDFSFile.of(new File("D:\\ZhouXY\\Desktop\\666.png")));
log.info(String.join("/", upload));
}
}

View File

@ -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>

View File

@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.exception;
package xyz.zhouxy.plusone.system.application.common.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -10,13 +10,13 @@ public class AccountLoginException extends BizException {
@java.io.Serial
private static final long serialVersionUID = -3040996790739138556L;
private static final int DEFAULT_ERR_CODE = 4030000;
private static final String DEFAULT_ERR_CODE = "4030000";
private AccountLoginException() {
super(DEFAULT_ERR_CODE, "用户登录异常");
}
private AccountLoginException(int code, String message) {
private AccountLoginException(String code, String message) {
super(code, message);
}
@ -25,7 +25,7 @@ public class AccountLoginException extends BizException {
}
public static AccountLoginException accountNotExistException(String msg) {
return new AccountLoginException(4030101, msg);
return new AccountLoginException("4030101", msg);
}
public static AccountLoginException otpErrorException() {
@ -33,7 +33,7 @@ public class AccountLoginException extends BizException {
}
public static AccountLoginException otpErrorException(String msg) {
return new AccountLoginException(4030501, msg);
return new AccountLoginException("4030501", msg);
}
public static AccountLoginException otpNotExistsException() {
@ -41,7 +41,7 @@ public class AccountLoginException extends BizException {
}
public static AccountLoginException otpNotExistsException(String msg) {
return new AccountLoginException(4030502, msg);
return new AccountLoginException("4030502", msg);
}
public static AccountLoginException passwordErrorException() {
@ -49,6 +49,6 @@ public class AccountLoginException extends BizException {
}
public static AccountLoginException passwordErrorException(String msg) {
return new AccountLoginException(4030200, msg);
return new AccountLoginException("4030200", msg);
}
}

View File

@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.exception;
package xyz.zhouxy.plusone.system.application.common.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -12,46 +12,46 @@ public class AccountRegisterException extends BizException {
private static final long serialVersionUID = 7580245181633370195L;
public AccountRegisterException() {
this(4020000, "用户注册错误");
this("4020000", "用户注册错误");
}
public AccountRegisterException(String message) {
this(4020000, message);
this("4020000", message);
}
public AccountRegisterException(Throwable cause) {
super(4020000, cause);
super("4020000", cause);
}
public AccountRegisterException(int code, String message) {
public AccountRegisterException(String code, String message) {
super(code, message);
}
public AccountRegisterException(int code, Throwable cause) {
public AccountRegisterException(String code, Throwable cause) {
super(code, cause);
}
public static AccountRegisterException emailOrMobilePhoneRequiredException() {
return new AccountRegisterException(4020300, "邮箱和手机号应至少绑定一个");
return new AccountRegisterException("4020300", "邮箱和手机号应至少绑定一个");
}
public static AccountRegisterException usernameAlreadyExists(String username) {
return new AccountRegisterException(4020400, String.format("用户名 %s 已存在", username));
return new AccountRegisterException("4020400", String.format("用户名 %s 已存在", username));
}
public static AccountRegisterException emailAlreadyExists(String value) {
return new AccountRegisterException(4020500, String.format("邮箱 %s 已存在", value));
return new AccountRegisterException("4020500", String.format("邮箱 %s 已存在", value));
}
public static AccountRegisterException mobilePhoneAlreadyExists(String value) {
return new AccountRegisterException(4020600, String.format("手机号 %s 已存在", value));
return new AccountRegisterException("4020600", String.format("手机号 %s 已存在", value));
}
public static AccountRegisterException codeErrorException() {
return new AccountRegisterException(4020701, "校验码错误");
return new AccountRegisterException("4020701", "校验码错误");
}
public static AccountRegisterException codeNotExistsException() {
return new AccountRegisterException(4020702, "校验码不存在或已过期");
return new AccountRegisterException("4020702", "校验码不存在或已过期");
}
}

View File

@ -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);
}
}

View File

@ -11,7 +11,7 @@ public class UnsupportedPrincipalTypeException extends BizException {
private static final long serialVersionUID = 5207757290868470762L;
public static final int ERR_CODE = 4040201;
public static final String ERR_CODE = "4040201";
private static final String DEFAULT_ERROR_MSG = "不支持的 PrincipalType";
public UnsupportedPrincipalTypeException() {

View File

@ -1,4 +1,6 @@
package xyz.zhouxy.plusone.system.application.exception.handler;
package xyz.zhouxy.plusone.system.application.common.exception.handler;
import java.util.Objects;
import javax.annotation.Nonnull;
@ -6,9 +8,9 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.exception.handler.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
@RestControllerAdvice
public class AccountLoginExceptionHandler extends BaseExceptionHandler {
@ -18,7 +20,7 @@ public class AccountLoginExceptionHandler extends BaseExceptionHandler {
}
@ExceptionHandler({ AccountLoginException.class })
public ResponseEntity<RestfulResult> handleException(@Nonnull Exception e) {
return buildExceptionResponse(e);
public ResponseEntity<UnifiedResponse> handleException(Exception e) {
return buildExceptionResponse(Objects.requireNonNull(e));
}
}

View File

@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.exception.handler;
package xyz.zhouxy.plusone.system.application.common.exception.handler;
import javax.annotation.Nonnull;
@ -17,7 +17,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.exception.handler.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
* Sa-Token
@ -52,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);
}

View File

@ -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);
}
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -12,7 +12,7 @@ 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;

View File

@ -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);
}
@ -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;
}
}

View File

@ -8,9 +8,9 @@ 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.exception.AccountLoginException;
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;

View File

@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import xyz.zhouxy.plusone.commons.util.PageDTO;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
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;
@ -94,7 +94,6 @@ public class AccountManagementService {
Account account = accountRepository.find(id)
.orElseThrow(() -> new DataNotExistException("该账号不存在"));
account.setAccountInfo(command.getNickname(), command.getAvatar(), Sex.of(command.getSex()));
account.setUpdatedBy(adminAuthLogic.getLoginIdAsLong());
accountRepository.save(account);
}

View File

@ -6,10 +6,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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;

View File

@ -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;

View File

@ -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,10 +12,10 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import xyz.zhouxy.plusone.commons.util.MoreCollections;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.IWithOrderNumber;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.system.application.exception.UnsupportedMenuTypeException;
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;
@ -108,7 +107,7 @@ public class MenuManagementService {
command.getIcon(),
command.getHidden(),
command.getOrderNumber(),
EntityStatus.of(command.getStatus()),
EntityStatus.of(command.getStatus()),
command.getComponent(),
command.getResource(),
command.getCache(),
@ -139,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();
}
}

View File

@ -5,10 +5,10 @@ import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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;

View File

@ -16,7 +16,7 @@ public class LoginByOtpCommandValidator extends BaseValidator<LoginByOtpCommand>
ruleForString(LoginByOtpCommand::getPrincipal)
.notNull("输入邮箱地址或手机号")
.notEmpty("输入邮箱地址或手机号")
.matchesOr(List.of(PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE), "输入用户名、邮箱地址或手机号");
.matchesOne(List.of(PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE), "输入用户名、邮箱地址或手机号");
ruleForString(LoginByOtpCommand::getOtp)
.notNull("验证码不能为空")
.notEmpty("验证码不能为空")

View File

@ -15,7 +15,7 @@ public class LoginByPasswordCommandValidator extends BaseValidator<LoginByPasswo
ruleForString(LoginByPasswordCommand::getPrincipal)
.notNull("输入用户名、邮箱地址或手机号")
.notEmpty("输入用户名、邮箱地址或手机号")
.matchesOr(List.of(PatternConsts.USERNAME, PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE),
.matchesOne(List.of(PatternConsts.USERNAME, PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE),
"输入用户名、邮箱地址或手机号");
ruleForString(LoginByPasswordCommand::getPassword)
.notNull("密码不能为空")

View File

@ -0,0 +1,20 @@
package xyz.zhouxy.plusone.system.application.web.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
HandlerInterceptor[] interceptors;
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (var interceptor : interceptors) {
registry.addInterceptor(interceptor);
}
}
}

View File

@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
import xyz.zhouxy.plusone.system.application.service.AccountContextService;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordWithoutLoginCommand;
@ -28,34 +28,34 @@ public class AccountContextController {
}
@GetMapping("info")
public RestfulResult getAccountInfo() {
public UnifiedResponse getAccountInfo() {
adminAuthLogic.checkLogin();
var result = service.getAccountInfo();
return RestfulResult.success("查询成功", result);
return UnifiedResponse.success("查询成功", result);
}
@GetMapping("logout")
public RestfulResult logout() {
public UnifiedResponse logout() {
service.logout();
return RestfulResult.success("注销成功");
return UnifiedResponse.success("注销成功");
}
@GetMapping("menus")
public RestfulResult getMenuTree() {
public UnifiedResponse getMenuTree() {
adminAuthLogic.checkLogin();
var result = service.getMenuTree();
return RestfulResult.success("查询成功", result);
return UnifiedResponse.success("查询成功", result);
}
@PostMapping("changePassword")
public RestfulResult changePassword(ChangePasswordCommand command) {
public UnifiedResponse changePassword(ChangePasswordCommand command) {
service.changePassword(command);
return RestfulResult.success("修改成功,请重新登录。");
return UnifiedResponse.success("修改成功,请重新登录。");
}
@PostMapping("changePasswordWithoutLogin")
public RestfulResult changePasswordWithoutLogin(ChangePasswordWithoutLoginCommand command) {
public UnifiedResponse changePasswordWithoutLogin(ChangePasswordWithoutLoginCommand command) {
service.changePasswordWithoutLogin(command);
return RestfulResult.success("修改成功,请重新登录。");
return UnifiedResponse.success("修改成功,请重新登录。");
}
}

View File

@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.commons.util.UnifiedResponse.success;
import java.util.List;
@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
import xyz.zhouxy.plusone.system.application.service.AccountManagementService;
import xyz.zhouxy.plusone.system.application.service.command.CreateAccountCommand;
@ -38,7 +38,7 @@ public class AccountManagementController {
}
@PostMapping
public RestfulResult createAccount(@RequestBody @Valid CreateAccountCommand command) {
public UnifiedResponse createAccount(@RequestBody @Valid CreateAccountCommand command) {
adminAuthLogic.checkLogin();
adminAuthLogic.checkPermission("sys-account-create");
service.createAccount(command);
@ -46,7 +46,7 @@ public class AccountManagementController {
}
@DeleteMapping
public RestfulResult deleteAccounts(@RequestBody List<Long> ids) {
public UnifiedResponse deleteAccounts(@RequestBody List<Long> ids) {
adminAuthLogic.checkLogin();
adminAuthLogic.checkPermission("sys-account-delete");
service.deleteAccounts(ids);
@ -54,7 +54,7 @@ public class AccountManagementController {
}
@PatchMapping("{id}")
public RestfulResult updateAccountInfo(
public UnifiedResponse updateAccountInfo(
@PathVariable("id") Long id,
@RequestBody @Valid UpdateAccountCommand command) {
adminAuthLogic.checkLogin();
@ -64,7 +64,7 @@ public class AccountManagementController {
}
@GetMapping("query")
public RestfulResult queryAccountOverviewList(AccountQueryParams queryParams) {
public UnifiedResponse queryAccountOverviewList(AccountQueryParams queryParams) {
adminAuthLogic.checkLogin();
adminAuthLogic.checkPermission("sys-account-list");
var accountOverviewList = service.queryAccountOverviewList(queryParams);
@ -72,7 +72,7 @@ public class AccountManagementController {
}
@GetMapping("{accountId}")
public RestfulResult queryAccountDetails(@PathVariable("accountId") Long accountId) {
public UnifiedResponse queryAccountDetails(@PathVariable("accountId") Long accountId) {
adminAuthLogic.checkLogin();
adminAuthLogic.checkPermission("sys-account-details");
var accountDetails = service.queryAccountDetails(accountId);

View File

@ -1,6 +1,6 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.commons.util.UnifiedResponse.success;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.system.application.service.AdminLoginService;
import xyz.zhouxy.plusone.system.application.service.command.LoginByOtpCommand;
import xyz.zhouxy.plusone.system.application.service.command.LoginByPasswordCommand;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
* Admin
@ -30,19 +30,19 @@ public class AdminLoginController {
}
@PostMapping("byPassword")
public RestfulResult loginByPassword(@RequestBody LoginByPasswordCommand command) {
public UnifiedResponse loginByPassword(@RequestBody LoginByPasswordCommand command) {
var loginInfo = service.loginByPassword(command);
return success("登录成功", loginInfo);
}
@PostMapping("byOtp")
public RestfulResult loginByOtp(@RequestBody LoginByOtpCommand command) {
public UnifiedResponse loginByOtp(@RequestBody LoginByOtpCommand command) {
var loginInfo = service.loginByOtp(command);
return success("登录成功", loginInfo);
}
@GetMapping("sendOtp")
public RestfulResult sendOtp(@RequestParam String principal) {
public UnifiedResponse sendOtp(@RequestParam String principal) {
service.sendOtp(principal);
return success("发送成功");
}

View File

@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.commons.util.UnifiedResponse.success;
import java.util.List;
@ -20,7 +20,7 @@ import xyz.zhouxy.plusone.system.application.query.params.DictQueryParams;
import xyz.zhouxy.plusone.system.application.service.DictManagementService;
import xyz.zhouxy.plusone.system.application.service.command.CreateDictCommand;
import xyz.zhouxy.plusone.system.application.service.command.UpdateDictCommand;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
*
@ -38,21 +38,21 @@ public class DictManagementController {
}
@PostMapping
public RestfulResult createDict(@RequestBody @Valid CreateDictCommand command) {
public UnifiedResponse createDict(@RequestBody @Valid CreateDictCommand command) {
adminAuthLogic.checkPermission("sys-dict-create");
service.createDict(command);
return success();
}
@DeleteMapping
public RestfulResult deleteDicts(@RequestBody List<Long> ids) {
public UnifiedResponse deleteDicts(@RequestBody List<Long> ids) {
adminAuthLogic.checkPermission("sys-dict-delete");
service.deleteDicts(ids);
return success();
}
@PatchMapping("{id}")
public RestfulResult updateDict(
public UnifiedResponse updateDict(
@PathVariable("id") Long id,
@RequestBody @Valid UpdateDictCommand command) {
adminAuthLogic.checkPermission("sys-dict-update");
@ -61,21 +61,21 @@ public class DictManagementController {
}
@GetMapping("{dictId}")
public RestfulResult findDictDetails(@PathVariable("dictId") Long dictId) {
public UnifiedResponse findDictDetails(@PathVariable("dictId") Long dictId) {
adminAuthLogic.checkPermission("sys-dict-details");
var dictDetails = service.findDictDetails(dictId);
return success("查询成功", dictDetails);
}
@GetMapping("all")
public RestfulResult loadAllDicts() {
public UnifiedResponse loadAllDicts() {
adminAuthLogic.checkPermissionAnd("sys-dict-list", "sys-dict-details");
var dicts = service.loadAllDicts();
return success("查询成功", dicts);
}
@GetMapping("query")
public RestfulResult queryDictOverviewList(@Valid DictQueryParams queryParams) {
public UnifiedResponse queryDictOverviewList(@Valid DictQueryParams queryParams) {
adminAuthLogic.checkPermission("sys-dict-list");
var dicts = service.queryDictOverviewList(queryParams);
return success("查询成功", dicts);

View File

@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.commons.util.UnifiedResponse.success;
import javax.validation.Valid;
@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.system.application.service.MenuManagementService;
import xyz.zhouxy.plusone.system.application.service.command.CreateMenuCommand;
import xyz.zhouxy.plusone.system.application.service.command.UpdateMenuCommand;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
*
@ -37,7 +37,7 @@ public class MenuManagementController {
// ==================== create ====================
@PostMapping
public RestfulResult createMenu(@RequestBody @Valid CreateMenuCommand command) {
public UnifiedResponse createMenu(@RequestBody @Valid CreateMenuCommand command) {
adminAuthLogic.checkPermission("sys-menu-create");
service.createMenu(command);
return success();
@ -45,7 +45,7 @@ public class MenuManagementController {
// ==================== delete ====================
@DeleteMapping("{id}")
public RestfulResult deleteMenu(@PathVariable("id") Long id) {
public UnifiedResponse deleteMenu(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-menu-delete");
service.deleteMenu(id);
return success();
@ -53,7 +53,7 @@ public class MenuManagementController {
// ==================== update ====================
@PatchMapping("{id}")
public RestfulResult updateMenu(
public UnifiedResponse updateMenu(
@PathVariable("id") Long id,
@RequestBody @Valid UpdateMenuCommand command) {
adminAuthLogic.checkPermission("sys-menu-update");
@ -63,21 +63,21 @@ public class MenuManagementController {
// ==================== query ====================
@GetMapping("{id}")
public RestfulResult findById(@PathVariable("id") Long id) {
public UnifiedResponse findById(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-menu-details");
var result = service.findById(id);
return RestfulResult.success("查询成功", result);
return UnifiedResponse.success("查询成功", result);
}
@GetMapping("queryByAccountId")
public RestfulResult queryByAccountId(@RequestParam Long accountId) {
public UnifiedResponse queryByAccountId(@RequestParam Long accountId) {
adminAuthLogic.checkPermission("sys-menu-details");
var result = service.queryByAccountId(accountId);
return success("查询成功", result);
}
@GetMapping("queryByRoleId")
public RestfulResult queryByRoleId(@RequestParam Long roleId) {
public UnifiedResponse queryByRoleId(@RequestParam Long roleId) {
adminAuthLogic.checkPermission("sys-menu-details");
var result = service.queryByRoleId(roleId);
return success("查询成功", result);

View File

@ -1,6 +1,6 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.commons.util.UnifiedResponse.success;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.system.application.service.RegisterAccountService;
import xyz.zhouxy.plusone.system.application.service.command.RegisterAccountCommand;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
*
@ -29,13 +29,13 @@ public class RegisterAccountController {
}
@PostMapping
public RestfulResult registerAccount(@RequestBody RegisterAccountCommand command) {
public UnifiedResponse registerAccount(@RequestBody RegisterAccountCommand command) {
service.registerAccount(command);
return success("注册成功");
}
@GetMapping("sendCode")
public RestfulResult sendCode(@RequestParam String principal) {
public UnifiedResponse sendCode(@RequestParam String principal) {
service.sendCode(principal);
return success("发送成功");
}

View File

@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.controller;
package xyz.zhouxy.plusone.system.application.web.controller;
import javax.validation.Valid;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
@ -17,7 +17,7 @@ import xyz.zhouxy.plusone.system.application.query.params.RoleQueryParams;
import xyz.zhouxy.plusone.system.application.service.RoleManagementService;
import xyz.zhouxy.plusone.system.application.service.command.CreateRoleCommand;
import xyz.zhouxy.plusone.system.application.service.command.UpdateRoleCommand;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.commons.util.UnifiedResponse;
/**
*
@ -35,44 +35,44 @@ public class RoleManagementController {
}
@PostMapping
public RestfulResult createRole(@RequestBody @Valid CreateRoleCommand command) {
public UnifiedResponse createRole(@RequestBody @Valid CreateRoleCommand command) {
adminAuthLogic.checkPermission("sys-role-create");
service.createRole(command);
return RestfulResult.success();
return UnifiedResponse.success();
}
@PatchMapping
public RestfulResult updateRole(@RequestBody @Valid UpdateRoleCommand command) {
public UnifiedResponse updateRole(@RequestBody @Valid UpdateRoleCommand command) {
adminAuthLogic.checkPermission("sys-role-update");
service.updateRole(command);
return RestfulResult.success();
return UnifiedResponse.success();
}
@DeleteMapping("{id}")
public RestfulResult delete(@PathVariable("id") Long id) {
public UnifiedResponse delete(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-role-delete");
service.delete(id);
return RestfulResult.success();
return UnifiedResponse.success();
}
@GetMapping("exists")
public RestfulResult exists(@RequestParam("id") Long id) {
public UnifiedResponse exists(@RequestParam("id") Long id) {
adminAuthLogic.checkPermissionOr("sys-role-list", "sys-role-details");
var isExists = service.exists(id);
return RestfulResult.success(isExists ? "存在" : "不存在", isExists);
return UnifiedResponse.success(isExists ? "存在" : "不存在", isExists);
}
@GetMapping("{id}")
public RestfulResult findById(@PathVariable("id") Long id) {
public UnifiedResponse findById(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-role-details");
var result = service.findById(id);
return RestfulResult.success("查询成功", result);
return UnifiedResponse.success("查询成功", result);
}
@GetMapping("query")
public RestfulResult query(RoleQueryParams params) {
public UnifiedResponse query(RoleQueryParams params) {
adminAuthLogic.checkPermission("sys-role-list");
var result = service.query(params);
return RestfulResult.success("查询成功", result);
return UnifiedResponse.success("查询成功", result);
}
}

View File

@ -0,0 +1,32 @@
package xyz.zhouxy.plusone.system.application.web.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.system.application.common.model.PlusoneContext;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;;
@Slf4j
@Order(1)
@Component
public class HttpContextInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("" + adminAuthLogic.isLogin());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
PlusoneContext.remove();
log.info("拦截器清理 ThreadLocal防止内存泄漏");
}
}

View File

@ -0,0 +1,7 @@
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 1.0
*/
package xyz.zhouxy.plusone.system.application.web.interceptor;

View File

@ -87,10 +87,13 @@
AND sar.role_id = #{roleId}
AND sr.deleted = 0
</if>
<if test="orderBy != null">
ORDER BY sa.${orderBy}, sa.id
<if test="orderBy != null and !orderBy.isEmpty()">
ORDER BY
<foreach item="col" index="index" collection="orderBy" open="" separator="," close="">
sa.${col}
</foreach>
</if>
<if test="orderBy == null">
<if test="orderBy == null or orderBy.isEmpty()">
ORDER BY sa.id
</if>
LIMIT #{size} OFFSET #{offset}
@ -99,7 +102,7 @@
</select>
<!--
long count(SysAccountQuery queryParams);
long count(AccountQueryParams queryParams);
-->
<select id="count" resultType="long">
SELECT COUNT(*)

View File

@ -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>

View File

@ -1,15 +1,12 @@
package xyz.zhouxy.plusone.system.util;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.annotation.Nonnull;
import org.springframework.util.Assert;
import com.google.common.hash.Hashing;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.BizException;
import xyz.zhouxy.plusone.util.RandomUtil;
/**
@ -17,7 +14,6 @@ import xyz.zhouxy.plusone.util.RandomUtil;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Slf4j
public final class PasswordUtil {
private static final char[] SALT_BASE_CHAR_ARRAY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]|\\:;\"',.<>?/"
.toCharArray();
@ -29,18 +25,18 @@ public final class PasswordUtil {
* @param salt
* @return
*/
@Nonnull
public static String hashPassword(@Nonnull String password, @Nonnull String salt) {
int length = salt.length();
int i = length > 0 ? length / 2 : 0;
var passwordWithSalt = salt.substring(0, i)
+ password
+ salt.substring(1);
String sha512Hex = sha512Hex(passwordWithSalt);
if (sha512Hex == null) {
throw new BizException(ErrorCodeConsts.DEFAULT_ERROR_CODE, "未知错误:哈希加密失败!");
}
return sha512Hex;
public static String hashPassword(String password, String salt) {
Assert.notNull(password, "Password must not be null");
Assert.notNull(salt, "Salt must not be null");
return Hashing.sha512().newHasher()
.putInt(Arrays.hashCode(salt.toCharArray()))
.putString(password, StandardCharsets.UTF_8)
.putInt(password.length())
.putBoolean(password.length() % 2 == 0)
.putString(salt, StandardCharsets.UTF_8)
.putInt(Arrays.hashCode(password.toCharArray()))
.hash()
.toString();
}
/**
@ -56,16 +52,4 @@ public final class PasswordUtil {
// 不允许实例化
throw new IllegalStateException("Utility class");
}
private static String sha512Hex(String data) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
messageDigest.update(data.getBytes(StandardCharsets.UTF_8));
byte[] result = messageDigest.digest();
return new BigInteger(1, result).toString(16);
} catch (NoSuchAlgorithmException e) {
log.error("{}", e);
}
return null;
}
}

View File

@ -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>

View File

@ -6,6 +6,7 @@ import java.util.Optional;
import java.util.Set;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IWithVersion;
import xyz.zhouxy.plusone.exception.UserOperationException;
@ -164,6 +165,7 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
return newInstance;
}
@StaticFactoryMethod(Account.class)
public static Account register(
Username username,
Email email,
@ -193,6 +195,7 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
password, status, accountInfo, roleRefs, createdBy, updatedBy, version);
}
@StaticFactoryMethod(Account.class)
public static Account newInstance(
String username,
String email,
@ -210,6 +213,7 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
return newInstance;
}
@StaticFactoryMethod(Account.class)
public static Account register(
String username,
String email,
@ -265,10 +269,6 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
return Optional.ofNullable(updatedBy);
}
public void setUpdatedBy(long updatedBy) {
this.updatedBy = updatedBy;
}
@Override
public long getVersion() {
return this.version;

View File

@ -23,7 +23,7 @@ public final class AccountStatus extends Enumeration<AccountStatus> implements I
public static final AccountStatus AVAILABLE = new AccountStatus(0, "账号正常");
public static final AccountStatus LOCKED = new AccountStatus(1, "账号被锁定");
private static final ValueSet<AccountStatus> VALUE_SET = new ValueSet<>(
private static final ValueSet<AccountStatus> VALUE_SET = ValueSet.of(
AVAILABLE,
LOCKED);

View File

@ -3,7 +3,11 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.Objects;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import cn.hutool.core.util.DesensitizedUtil;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
/**
@ -11,38 +15,28 @@ import xyz.zhouxy.plusone.commons.constant.PatternConsts;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public class Email extends Principal {
@ValueObject
public final class Email extends Principal {
public static final Pattern REGEX = PatternConsts.EMAIL;
private Email(String email) {
super(REGEX);
if (email == null) {
throw new IllegalArgumentException("邮箱地址不能为空");
}
this.value = email;
if (!isValid()) {
throw new IllegalArgumentException("邮箱地址格式错误");
}
super(email, REGEX);
}
@StaticFactoryMethod(Email.class)
public static Email of(String email) {
return new Email(email);
}
@StaticFactoryMethod(Email.class)
@Nullable
public static Email ofNullable(String email) {
return Objects.nonNull(email) ? new Email(email) : null;
}
/**
*
*/
public String safeValue() {
return DesensitizedUtil.email(this.value);
}
@Override
public String toString() {
return this.safeValue();
public String safeValue() {
return DesensitizedUtil.email(value());
}
}

View File

@ -3,7 +3,11 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.Objects;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import cn.hutool.core.util.DesensitizedUtil;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
/**
@ -11,35 +15,28 @@ import xyz.zhouxy.plusone.commons.constant.PatternConsts;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@ValueObject
public class MobilePhone extends Principal {
public static final Pattern REGEX = PatternConsts.MOBILE_PHONE;
private MobilePhone(String mobilePhone) {
super(REGEX);
if (mobilePhone == null) {
throw new IllegalArgumentException("手机号不能为空");
}
this.value = mobilePhone;
if (!isValid()) {
throw new IllegalArgumentException("手机号格式错误");
}
super(mobilePhone, REGEX);
}
@StaticFactoryMethod(MobilePhone.class)
public static MobilePhone of(String mobilePhone) {
return new MobilePhone(mobilePhone);
}
@StaticFactoryMethod(MobilePhone.class)
@Nullable
public static MobilePhone ofNullable(String mobilePhone) {
return Objects.nonNull(mobilePhone) ? new MobilePhone(mobilePhone) : null;
}
public String safeValue() {
return DesensitizedUtil.mobilePhone(this.value);
}
@Override
public String toString() {
return this.safeValue();
public String safeValue() {
return DesensitizedUtil.mobilePhone(this.value());
}
}

View File

@ -3,33 +3,31 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.Objects;
import java.util.regex.Pattern;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import xyz.zhouxy.plusone.domain.ValidatableStringRecord;
import xyz.zhouxy.plusone.commons.domain.ValidatableStringRecord;
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@ValueObject
public class Nickname extends ValidatableStringRecord {
public static final Pattern REGEX = PatternConsts.NICKNAME;
private Nickname(String value) {
super(REGEX);
if (value == null) {
throw new IllegalArgumentException("昵称不能为空");
}
this.value = value;
if (!isValid()) {
throw new IllegalArgumentException("昵称格式错误");
}
super(value, REGEX);
}
@StaticFactoryMethod(Nickname.class)
public static Nickname of(String nickname) {
return new Nickname(nickname);
}
@StaticFactoryMethod(Nickname.class)
public static Nickname ofNullable(String nickname) {
return Objects.nonNull(nickname) ? new Nickname(nickname) : null;
}

View File

@ -3,14 +3,11 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.Objects;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.springframework.util.Assert;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.domain.IValueObject;
import xyz.zhouxy.plusone.exception.BizException;
import xyz.zhouxy.plusone.system.util.PasswordUtil;
/**
@ -23,43 +20,35 @@ public class Password implements IValueObject {
private static final Pattern PATTERN = PatternConsts.PASSWORD;
private static final String DEFAULT_PASSWORD = "A1b2C3d4";
@Nonnull
private final String passwordVal;
@Nonnull
private final String saltVal;
private Password(String password) {
if (password == null) {
throw new IllegalArgumentException("密码不能为空");
}
if (!PATTERN.matcher(password).matches()) {
throw new IllegalArgumentException("密码格式不符合要求");
}
var salt = PasswordUtil.generateRandomSalt();
if (salt == null) {
throw new BizException(ErrorCodeConsts.DEFAULT_ERROR_CODE, "未知错误:生成随机盐失败");
}
Assert.notNull(password, "密码不能为空");
Assert.isTrue(PATTERN.matcher(password).matches(), "密码格式不符合要求");
String salt = PasswordUtil.generateRandomSalt();
this.saltVal = salt;
this.passwordVal = PasswordUtil.hashPassword(password, salt);
}
private Password(String password, String salt) {
if (password == null || salt == null) {
throw new IllegalArgumentException("password 和 salt 不能为空");
}
Assert.isTrue(password != null && salt != null, "password 和 salt 不能为空");
this.passwordVal = password;
this.saltVal = salt;
}
@StaticFactoryMethod(Password.class)
public static Password of(String password, String salt) {
return new Password(password, salt);
}
@StaticFactoryMethod(Password.class)
public static Password newPassword(String newPassword, String passwordConfirmation) {
Assert.isTrue(Objects.equals(newPassword, passwordConfirmation), "两次输入的密码不一致");
return newPassword(newPassword);
}
@StaticFactoryMethod(Password.class)
public static Password newPassword(String newPassword) {
return new Password(newPassword);
}
@ -80,6 +69,7 @@ public class Password implements IValueObject {
return saltVal;
}
@StaticFactoryMethod(Nickname.class)
public static Password newDefaultPassword() {
return newPassword(DEFAULT_PASSWORD);
}

View File

@ -2,15 +2,23 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.regex.Pattern;
import xyz.zhouxy.plusone.domain.ValidatableStringRecord;
import xyz.zhouxy.plusone.commons.domain.ValidatableStringRecord;
import xyz.zhouxy.plusone.domain.IValueObject;
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public abstract class Principal extends ValidatableStringRecord {
protected Principal(Pattern format) {
super(format);
public abstract class Principal extends ValidatableStringRecord implements IValueObject {
protected Principal(String principal, Pattern pattern) {
super(principal, pattern);
}
protected abstract String safeValue();
@Override
public final String toString() {
return this.safeValue();
}
}

View File

@ -4,6 +4,7 @@ import java.util.Collection;
import javax.annotation.Nonnull;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.util.Enumeration;
import xyz.zhouxy.plusone.domain.IValueObject;
@ -24,8 +25,9 @@ public final class Sex extends Enumeration<Sex> implements IValueObject {
super(id, name);
}
private static final ValueSet<Sex> VALUE_SET = new ValueSet<>(UNSET, MALE, FEMALE);
private static final ValueSet<Sex> VALUE_SET = ValueSet.of(UNSET, MALE, FEMALE);
@StaticFactoryMethod(Sex.class)
public static Sex of(int value) {
return VALUE_SET.get(value);
}

View File

@ -2,6 +2,8 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.regex.Pattern;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
/**
@ -9,22 +11,23 @@ import xyz.zhouxy.plusone.commons.constant.PatternConsts;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@ValueObject
public class Username extends Principal {
public static final Pattern REGEX = PatternConsts.USERNAME;
private Username(String username) {
super(REGEX);
if (username == null) {
throw new IllegalArgumentException("用户名不能为空");
}
this.value = username;
if (!isValid()) {
throw new IllegalArgumentException("用户名格式错误");
}
super(username, REGEX);
}
@StaticFactoryMethod(Username.class)
public static Username of(String username) {
return new Username(username);
}
@Override
protected String safeValue() {
// 不需要脱敏。
return value();
}
}

View File

@ -8,6 +8,7 @@ import java.util.Optional;
import java.util.Set;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IWithLabel;
import xyz.zhouxy.plusone.domain.IWithVersion;
@ -70,12 +71,14 @@ public class Dict extends AggregateRoot<Long> implements IWithLabel, IWithVersio
this.version = version;
}
@StaticFactoryMethod(Dict.class)
public static Dict newInstance(
String dictType,
String dictLabel) {
return new Dict(null, dictType, dictLabel, Collections.emptySet(), 0);
}
@StaticFactoryMethod(Dict.class)
public static Dict newInstance(
String dictType,
String dictLabel,
@ -83,6 +86,7 @@ public class Dict extends AggregateRoot<Long> implements IWithLabel, IWithVersio
return new Dict(null, dictType, dictLabel, values, 0);
}
@StaticFactoryMethod(Dict.class)
public static Dict newInstance(
String dictType,
String dictLabel,

View File

@ -4,6 +4,7 @@ import java.util.Objects;
import lombok.Getter;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.domain.IValueObject;
/**
@ -25,6 +26,7 @@ public class DictValue implements IValueObject {
this.label = label;
}
@StaticFactoryMethod(DictValue.class)
public static DictValue of(int key, String label) {
return new DictValue(key, label);
}

View File

@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.base.IWithIntCode;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IWithOrderNumber;
@ -129,12 +130,28 @@ public class Menu extends AggregateRoot<Long> implements IWithOrderNumber, IWith
this.version = version;
}
public enum MenuType {
MENU_LIST, MENU_ITEM;
public enum MenuType implements IWithIntCode {
MENU_LIST(0), MENU_ITEM(1);
private final int code;
private MenuType(int code) {
this.code = code;
}
@JsonValue
public int value() {
return ordinal();
@Override
public int getCode() {
return code;
}
public static MenuType valueOf(int code) {
for (MenuType value : values()) {
if (value.code == code) {
return value;
}
}
throw new EnumConstantNotPresentException(MenuType.class, Integer.toString(code));
}
}
}

View File

@ -2,6 +2,7 @@ package xyz.zhouxy.plusone.system.domain.model.menu;
import java.util.List;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
@ -16,6 +17,7 @@ public class MenuConstructor {
throw new IllegalStateException("Utility class");
}
@StaticFactoryMethod(Menu.class)
public static Menu newMenuItem(
long parentId,
String path,
@ -39,6 +41,7 @@ public class MenuConstructor {
remarks, component, cache, resource, actions, 0L);
}
@StaticFactoryMethod(Menu.class)
public static Menu newMenuList(
long parentId,
String path,

View File

@ -3,6 +3,7 @@ package xyz.zhouxy.plusone.system.domain.model.permission;
import java.util.Optional;
import lombok.Getter;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.domain.Entity;
import xyz.zhouxy.plusone.domain.IWithLabel;
import xyz.zhouxy.plusone.domain.IWithVersion;
@ -20,7 +21,7 @@ public class Action extends Entity<Long> implements IWithLabel, IWithVersion {
@Getter String label;
@Getter long version;
public Action(Long id, String resource, String identifier, String label, long version) {
private Action(Long id, String resource, String identifier, String label, long version) {
this.id = id;
this.resource = resource;
this.identifier = identifier;
@ -28,10 +29,12 @@ public class Action extends Entity<Long> implements IWithLabel, IWithVersion {
this.version = version;
}
@StaticFactoryMethod(Action.class)
static Action newInstance(String resource, String identifier, String label) {
return new Action(null, resource, identifier, label, 0L);
}
@StaticFactoryMethod(Action.class)
static Action existingInstance(Long id, String resource, String action, String label, Long version) {
return new Action(id, resource, action, label, version);
}

View File

@ -6,6 +6,7 @@ import java.util.Objects;
import java.util.Optional;
import lombok.Getter;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IWithVersion;
@ -37,6 +38,7 @@ public class Permission extends AggregateRoot<Long> implements IWithVersion {
// ==================== 实例化 ====================
@StaticFactoryMethod(Permission.class)
public static Permission newInstance(String resource) {
return new Permission(
null, resource,

View File

@ -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>

View File

@ -14,7 +14,8 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import cn.hutool.core.util.IdUtil;
import static xyz.zhouxy.plusone.domain.ValidatableStringRecord.getValueOrNull;
import xyz.zhouxy.plusone.commons.domain.ValidatableStringRecord;
import xyz.zhouxy.plusone.commons.util.OptionalUtil;
import xyz.zhouxy.plusone.jdbc.JdbcRepositorySupport;
/**
@ -196,23 +197,35 @@ public class AccountRepositoryImpl extends JdbcRepositorySupport<Account, Long>
@Override
protected final SqlParameterSource generateParamSource(Long id, @Nonnull Account entity) {
LocalDateTime now = LocalDateTime.now();
AccountInfo accountInfo = entity.getAccountInfo();
final LocalDateTime now = LocalDateTime.now();
final AccountInfo accountInfo = entity.getAccountInfo();
final Optional<String> email = entity.getEmail().map(ValidatableStringRecord::value);
final Optional<String> mobilePhone = entity.getMobilePhone().map(ValidatableStringRecord::value);
final String username = entity.getUsername().value();
final String password = entity.getPassword().value();
final String salt = entity.getPassword().getSalt();
final Optional<String> avatar = accountInfo.getAvatar().map(Object::toString);
final int sex = accountInfo.getSex().getId();
final Optional<String> nickname = accountInfo.getNickname().map(ValidatableStringRecord::value);
final int status = entity.getStatus().getId();
final Optional<Long> createdBy = entity.getCreatedBy();
final Optional<Long> updatedBy = entity.getUpdatedBy();
final long version = entity.getVersion();
return new MapSqlParameterSource()
.addValue("id", id)
.addValue("email", getValueOrNull(entity.getEmail()))
.addValue("mobilePhone", getValueOrNull(entity.getMobilePhone()))
.addValue("username", entity.getUsername().value())
.addValue("password", entity.getPassword().value())
.addValue("salt", entity.getPassword().getSalt())
.addValue("avatar", accountInfo.getAvatar().toString())
.addValue("sex", accountInfo.getSex().getId())
.addValue("nickname", getValueOrNull(accountInfo.getNickname()))
.addValue("status", entity.getStatus().getId())
.addValue("createdBy", entity.getCreatedBy())
.addValue("email", OptionalUtil.orElseNull(email))
.addValue("mobilePhone", OptionalUtil.orElseNull(mobilePhone))
.addValue("username", username)
.addValue("password", password)
.addValue("salt", salt)
.addValue("avatar", OptionalUtil.orElseNull(avatar))
.addValue("sex", sex)
.addValue("nickname", OptionalUtil.orElseNull(nickname))
.addValue("status", status)
.addValue("createdBy", OptionalUtil.orElseNull(createdBy))
.addValue("createTime", now)
.addValue("updatedBy", entity.getUpdatedBy())
.addValue("updatedBy", OptionalUtil.orElseNull(updatedBy))
.addValue("updateTime", now)
.addValue("version", entity.getVersion());
.addValue("version", version);
}
}

View File

@ -7,6 +7,7 @@ import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
@ -94,13 +95,12 @@ public class DictRepositoryImpl extends JdbcRepositorySupport<Dict, Long> implem
@Override
protected final Dict mapRow(ResultSet rs) throws SQLException {
long id = rs.getLong("id");
return new Dict(
id,
rs.getString("dict_type"),
rs.getString("dict_label"),
this.dictValueDAO.selectDictValuesByDictId(id),
rs.getLong("version"));
final long id = rs.getLong("id");
final String dictType = rs.getString("dict_type");
final String dictLabel = rs.getString("dict_label");
final Set<DictValue> values = this.dictValueDAO.selectDictValuesByDictId(id);
final long version = rs.getLong("version");
return new Dict(id, dictType, dictLabel, values, version);
}
@Override

View File

@ -17,7 +17,6 @@ import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import cn.hutool.core.util.IdUtil;
import xyz.zhouxy.plusone.commons.util.EnumUtil;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.jdbc.JdbcRepositorySupport;
@ -156,7 +155,7 @@ public class MenuRepositoryImpl extends JdbcRepositorySupport<Menu, Long> implem
protected final Menu mapRow(ResultSet rs) throws SQLException {
long menuId = rs.getLong("id");
return new Menu(
EnumUtil.valueOf(MenuType.class, rs.getInt("type")),
MenuType.valueOf(rs.getInt("type")),
menuId,
rs.getLong("parent_id"),
rs.getString("name"),
@ -181,7 +180,7 @@ public class MenuRepositoryImpl extends JdbcRepositorySupport<Menu, Long> implem
return new MapSqlParameterSource()
.addValue("id", id)
.addValue("parentId", entity.getParentId())
.addValue("type", entity.getType().value())
.addValue("type", entity.getType().getCode())
.addValue("name", entity.getName())
.addValue("path", entity.getPath())
.addValue("title", entity.getTitle())

View File

@ -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>

10
pom.xml
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>
@ -25,9 +25,9 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>2.7.10</spring-boot.version>
<spring-boot.version>2.7.18</spring-boot.version>
<sa-token.version>1.34.0</sa-token.version>
<hutool.version>5.8.16</hutool.version>
<hutool.version>5.8.20</hutool.version>
<mybatis-starter.version>3.0.1</mybatis-starter.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<commons-io.version>2.11.0</commons-io.version>
@ -40,7 +40,7 @@
<commons-pool2.version>2.11.1</commons-pool2.version>
<tencentcloud-sdk.version>3.1.681</tencentcloud-sdk.version>
<fastdfs.version>1.30-SNAPSHOT</fastdfs.version>
<okio.version>3.0.0</okio.version>
<okio.version>3.5.0</okio.version>
<okhttp.version>4.10.0</okhttp.version>
<plusone-basic.version>1.0.0-SNAPSHOT</plusone-basic.version>