2 Commits

120 changed files with 929 additions and 1899 deletions

View File

@@ -33,4 +33,4 @@ indent_size=4
indent_size=2
[*.java]
indent_size=4
indent_size = 4

2
.gitignore vendored
View File

@@ -32,8 +32,6 @@ build/
### VS Code ###
.vscode/
log/
### bak ###
*.bak
*-secret.yaml

View File

@@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
* 默认异常的处理器
@@ -40,13 +40,14 @@ import xyz.zhouxy.plusone.commons.util.RestfulResult;
@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Slf4j
public class DefaultExceptionHandler extends BaseExceptionHandler {
public DefaultExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
set(IllegalArgumentException.class, 4010000, "格式错误", HttpStatus.FORBIDDEN);
set(DataAccessException.class, 6030000, "数据库错误", HttpStatus.INTERNAL_SERVER_ERROR, true);
set(MethodArgumentNotValidException.class,
4040401,
e -> e.getAllErrors()
e -> ((MethodArgumentNotValidException) e).getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.toList()

View File

@@ -1,28 +0,0 @@
package xyz.zhouxy.plusone.exception.handler;
import javax.annotation.Nonnull;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.exception.SysException;
@RestControllerAdvice
@Slf4j
public class SysExceptionHandler extends BaseExceptionHandler {
public SysExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
}
@ExceptionHandler({ SysException.class })
public ResponseEntity<RestfulResult> handleException(@Nonnull Exception e) {
log.error(e.getMessage(), e);
HttpStatus httpStatus = getHttpStatus(e);
return new ResponseEntity<>(RestfulResult.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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>plusone-basic</artifactId>
@@ -31,25 +31,20 @@
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-commons</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-validator</artifactId>
<version>0.1.3-SNAPSHOT</version>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-exception-handler</artifactId>
<version>0.0.8-SNAPSHOT</version>
<version>0.0.5-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@@ -2,8 +2,4 @@ package xyz.zhouxy.plusone.constant;
public class ErrorCodeConsts {
public static final int DEFAULT_ERROR_CODE = 9999999;
private ErrorCodeConsts() {
throw new IllegalStateException("Utility class");
}
}

View File

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

View File

@@ -6,10 +6,15 @@ import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 需要时,当查询数据不存在时抛出的异常
*
* <p>
* 暂时先这样,后续完善异常体系时可能会更改。
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see xyz.zhouxy.plusone.util.AssertResult
*/
@ResponseStatus(HttpStatus.NOT_FOUND)
public class DataNotExistException extends BizException {
public class DataNotExistException extends PlusoneException {
@java.io.Serial
private static final long serialVersionUID = 6536955800679703111L;

View File

@@ -4,27 +4,28 @@ import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 需要时当数据操作的结果不符合预期时抛出的异常
* 需要时当数据操作的行数不符合预期时抛出的异常
*
* <p>
* 暂时先这样后续完善异常体系时可能会更改
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see xyz.zhouxy.plusone.util.AssertResult
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class DataOperationResultException extends SysException {
public class DataOperationNumberException extends PlusoneException {
@java.io.Serial
private static final long serialVersionUID = -9220765735990318186L;
public static final int ERROR_CODE = 4110200;
public DataOperationResultException() {
super(ERROR_CODE, "数据操作结果不符合预期");
public DataOperationNumberException() {
super(ERROR_CODE, "数据操作的行数不符合预期");
}
public DataOperationResultException(String message) {
public DataOperationNumberException(String message) {
super(ERROR_CODE, message);
}
}

View File

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

View File

@@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author ZhouXY
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class UserOperationException extends BizException {
public class UserOperationException extends PlusoneException {
@java.io.Serial
private static final long serialVersionUID = 4371055414421991940L;

View File

@@ -1,10 +1,10 @@
package xyz.zhouxy.plusone.util;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.exception.DataOperationResultException;
import xyz.zhouxy.plusone.exception.DataOperationNumberException;
import java.util.Objects;
import java.util.function.Supplier;
/**
* 对数据库执行结果进行判断
@@ -17,34 +17,51 @@ public final class AssertResult {
throw new IllegalStateException("Utility class");
}
public static <E extends Throwable> void isTrue(boolean condition, Supplier<E> e) throws E {
if (!condition) {
throw e.get();
public static void update(boolean expression) {
if (!expression) {
throw new DataOperationNumberException();
}
}
public static <T> void equals(T result, T expectedValue) {
isTrue(Objects.equals(result, expectedValue), DataOperationResultException::new);
public static void update(boolean expression, String message) {
if (!expression) {
throw new DataOperationNumberException(message);
}
}
public static <T> void equals(T result, T expectedValue, String msgTemplate, Object... args) {
isTrue(!Objects.equals(result, expectedValue),
() -> new DataOperationResultException(String.format(msgTemplate, args)));
public static void update(Object i, int expectedValue) {
if (!Objects.equals(i, expectedValue)) {
throw new DataOperationNumberException();
}
}
public static void updateOneRow(int i) {
equals(i, 1);
public static void update(Object i, int expectedValue, String format) {
if (!Objects.equals(i, expectedValue)) {
throw new DataOperationNumberException(String.format(format, i));
}
}
public static void updateOneRow(int i, String format, Object... args) {
equals(i, 1, format, args);
public static void exist(boolean expression) {
if (!expression) {
throw new DataNotExistException();
}
}
public static void exist(boolean expression, String message) {
if (!expression) {
throw new DataNotExistException(message);
}
}
public static void nonNull(Object obj) {
isTrue(Objects.nonNull(obj), DataNotExistException::new);
if (Objects.isNull(obj)) {
throw new DataNotExistException();
}
}
public static void nonNull(Object obj, String message) {
isTrue(Objects.nonNull(obj), () -> new DataNotExistException(message));
if (Objects.isNull(obj)) {
throw new DataNotExistException(message);
}
}
}

View File

@@ -1,18 +0,0 @@
package xyz.zhouxy.plusone.util;
import java.util.concurrent.ThreadLocalRandom;
public final class RandomUtil {
private RandomUtil() {
throw new IllegalStateException("Utility class");
}
public static String randomStr(char[] sourceCharacters, int length) {
ThreadLocalRandom random = ThreadLocalRandom.current();
char[] result = new char[length];
for (int i = 0; i < length; i++) {
result[i] = sourceCharacters[random.nextInt(sourceCharacters.length)];
}
return String.valueOf(result);
}
}

View File

@@ -1,23 +0,0 @@
package xyz.zhouxy.plusone.util;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class RandomUtilTests {
@Test
void testRandom() {
String[] s = new String[20];
for (int i = 0; i < 20; i++) {
s[i] = RandomUtil.randomStr(
"0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~`!@#$%^&*()_-+={[\\|/:;\"',.<>?]}"
.toCharArray(),
28);
}
log.info("{}", Arrays.toString(s));
}
}

View File

@@ -15,6 +15,11 @@
<groupId>xyz.zhouxy</groupId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -23,10 +28,12 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<version>2.13.4</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,36 +1,32 @@
package xyz.zhouxy.plusone.constant;
import java.util.Collection;
import javax.annotation.Nonnull;
import xyz.zhouxy.plusone.commons.util.Enumeration;
import xyz.zhouxy.plusone.util.Enumeration;
import xyz.zhouxy.plusone.util.EnumerationValuesHolder;
/**
* 实体状态
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class EntityStatus extends Enumeration<EntityStatus> {
public class EntityStatus extends Enumeration<EntityStatus> {
private EntityStatus(int id, @Nonnull String name) {
super(id, name);
private EntityStatus(int value, String name) {
super(value, name);
}
// 常量
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 EnumerationValuesHolder<EntityStatus> ENUMERATION_VALUES = new EnumerationValuesHolder<>(
new EntityStatus[] { AVAILABLE, DISABLED });
@Nonnull
public static EntityStatus of(int id) {
return VALUE_SET.get(id);
public static EntityStatus of(int value) {
return ENUMERATION_VALUES.get(value);
}
@Nonnull
public static Collection<EntityStatus> constants() {
return VALUE_SET.getValues();
@Override
public String toString() {
return "EntityStatus" + super.toString();
}
}

View File

@@ -0,0 +1,27 @@
package xyz.zhouxy.plusone.constant;
/**
* 正则表达式常量
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class RegexConsts {
public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}";
public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$";
public static final String CAPTCHA = "^[0-9A-Za-z]{4,6}$";
public static final String EMAIL = "^\\w+([-+.]\\w+)*@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})*(\\.(?![0-9]+$)[a-zA-Z0-9][-0-9A-Za-z]{0,62})$";
public static final String MOBILE_PHONE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
public static final String USERNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
public static final String NICKNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
private RegexConsts() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,7 +1,6 @@
package xyz.zhouxy.plusone.domain;
import java.util.UUID;
import cn.hutool.core.lang.UUID;
import lombok.Getter;
/**
@@ -21,7 +20,7 @@ public abstract class DomainEvent {
private long happenedAt;
protected DomainEvent() {
this.identifier = UUID.randomUUID().toString();
this.identifier = UUID.randomUUID().toString(true);
this.happenedAt = System.currentTimeMillis();
}
}

View File

@@ -1,7 +1,6 @@
package xyz.zhouxy.plusone.domain;
import java.io.Serializable;
import java.util.Optional;
/**
* Repository 基础接口
@@ -11,7 +10,7 @@ import java.util.Optional;
*/
public interface IRepository<T extends AggregateRoot<ID>, ID extends Serializable> {
Optional<T> find(ID id);
T find(ID id);
T save(T entity);

View File

@@ -1,8 +1,5 @@
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;
@@ -14,15 +11,15 @@ import com.fasterxml.jackson.annotation.JsonValue;
public abstract class ValidatableStringRecord implements IValueObject {
protected String value;
protected final Pattern format;
protected final String format;
protected ValidatableStringRecord(Pattern format) {
protected ValidatableStringRecord(String format) {
this.format = format;
}
@JsonIgnore
protected boolean isValid() {
return format.matcher(value).matches();
return value.matches(format);
}
@JsonValue
@@ -34,8 +31,4 @@ public abstract class ValidatableStringRecord implements IValueObject {
public String toString() {
return value;
}
public static String getValueOrNull(Optional<? extends ValidatableStringRecord> s) {
return s.isPresent() ? s.get().value() : null;
}
}

View File

@@ -0,0 +1,36 @@
package xyz.zhouxy.plusone.util;
import java.util.regex.Pattern;
public class RegexUtil {
private RegexUtil() {
throw new IllegalStateException("Utility class");
}
public static boolean matches(CharSequence input, String regex) {
return Pattern.matches(regex, input);
}
public static boolean matchesOr(CharSequence input, String... regexs) {
boolean isMatched;
for (var regex : regexs) {
isMatched = Pattern.matches(regex, input);
if (isMatched) {
return true;
}
}
return false;
}
public static boolean matchesAnd(CharSequence input, String... regexs) {
boolean isMatched;
for (var regex : regexs) {
isMatched = Pattern.matches(regex, input);
if (!isMatched) {
return false;
}
}
return true;
}
}

View File

@@ -1,7 +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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>xyz.zhouxy</groupId>
@@ -86,11 +84,6 @@
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
</dependency>
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -4,61 +4,60 @@ import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import xyz.zhouxy.plusone.domain.Entity;
public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Serializable>
extends PlusoneJdbcDaoSupport {
public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Serializable> {
protected final NamedParameterJdbcTemplate jdbc;
protected RowMapper<T> rowMapper;
protected ResultSetExtractor<Optional<T>> resultSetExtractor;
protected ResultSetExtractor<T> resultSetExtractor;
protected JdbcEntityDaoSupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
super(namedParameterJdbcTemplate);
this.jdbc = namedParameterJdbcTemplate;
this.rowMapper = (ResultSet rs, int rowNum) -> mapRow(rs);
this.resultSetExtractor = (ResultSet rs) -> rs.next() ? Optional.of(mapRow(rs)) : Optional.empty();
this.resultSetExtractor = (ResultSet rs) -> rs.next() ? mapRow(rs) : null;
}
protected final Optional<T> queryForObject(String sql) {
return queryForObject(sql, this.resultSetExtractor);
protected final T queryForObject(String sql) {
return this.jdbc.query(sql, this.resultSetExtractor);
}
protected final Optional<T> queryForObject(String sql, SqlParameterSource paramSource) {
return queryForObject(sql, paramSource, this.resultSetExtractor);
}
protected final Optional<T> queryForObject(String sql, String paramName, Object value) {
return queryForObject(sql, new MapSqlParameterSource(paramName, value), this.resultSetExtractor);
protected final T queryForObject(String sql, SqlParameterSource paramSource) {
return this.jdbc.query(sql, paramSource, this.resultSetExtractor);
}
protected final List<T> queryForList(String sql) {
return queryForList(sql, this.rowMapper);
return this.jdbc.query(sql, this.rowMapper);
}
protected final List<T> queryForList(String sql, SqlParameterSource parameterSource) {
return queryForList(sql, parameterSource, this.rowMapper);
}
protected final List<T> queryForList(String sql, String paramName, Object value) {
return queryForList(sql, new MapSqlParameterSource(paramName, value), this.rowMapper);
return this.jdbc.query(sql, parameterSource, this.rowMapper);
}
protected final Stream<T> queryForStream(String sql, SqlParameterSource parameterSource) {
return queryForStream(sql, parameterSource, this.rowMapper);
return this.jdbc.queryForStream(sql, parameterSource, this.rowMapper);
}
protected final Stream<T> queryForStream(String sql, String paramName, Object value) {
return queryForStream(sql, new MapSqlParameterSource(paramName, value), this.rowMapper);
protected final <E> Stream<E> queryForStream(String sql, SqlParameterSource parameterSource, Class<E> elementType) {
return this.jdbc.queryForList(sql, parameterSource, elementType).stream();
}
protected final boolean queryExists(String sql, SqlParameterSource parameterSource) {
Boolean isExists = this.jdbc.query(sql, parameterSource, ResultSet::next);
return Boolean.TRUE.equals(isExists);
}
protected final int update(String sql, SqlParameterSource parameterSource) {
return this.jdbc.update(sql, parameterSource);
}
protected abstract T mapRow(ResultSet rs) throws SQLException;
@@ -67,7 +66,7 @@ public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Seri
this.rowMapper = rowMapper;
}
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<Optional<T>> resultSetExtractor) {
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<T> resultSetExtractor) {
this.resultSetExtractor = resultSetExtractor;
}
}

View File

@@ -1,6 +1,5 @@
package xyz.zhouxy.plusone.jdbc;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@@ -17,17 +16,15 @@ import xyz.zhouxy.plusone.spring.SpringContextHolder;
*/
public final class JdbcFactory {
private static final ApplicationContext CONTEXT = SpringContextHolder.getContext();
private JdbcFactory() {
throw new IllegalStateException("Utility class");
}
public static JdbcTemplate getJdbcTemplate() {
return CONTEXT.getBean(JdbcTemplate.class);
return SpringContextHolder.getContext().getBean(JdbcTemplate.class);
}
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return CONTEXT.getBean(NamedParameterJdbcTemplate.class);
return SpringContextHolder.getContext().getBean(NamedParameterJdbcTemplate.class);
}
}

View File

@@ -1,7 +1,6 @@
package xyz.zhouxy.plusone.jdbc;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nonnull;
@@ -21,7 +20,7 @@ public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID exte
protected abstract void doDelete(@Nonnull T entity);
protected abstract Optional<T> doFindById(@Nonnull ID id);
protected abstract T doFindById(@Nonnull ID id);
protected abstract T doInsert(@Nonnull T entity);
@@ -36,7 +35,7 @@ public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID exte
}
@Override
public final Optional<T> find(ID id) {
public final T find(ID id) {
if (id == null) {
throw new IllegalArgumentException("Id cannot be null.");
}

View File

@@ -1,173 +0,0 @@
package xyz.zhouxy.plusone.jdbc;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.CollectionUtils;
import xyz.zhouxy.plusone.commons.util.NumberUtil;
import xyz.zhouxy.plusone.exception.DataOperationResultException;
public abstract class PlusoneJdbcDaoSupport {
protected final NamedParameterJdbcTemplate jdbc;
protected PlusoneJdbcDaoSupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.jdbc = namedParameterJdbcTemplate;
}
protected final <T> T queryForObject(String sql, ResultSetExtractor<T> resultSetExtractor) {
return this.jdbc.query(sql, resultSetExtractor);
}
protected final <T> T queryForObject(String sql, SqlParameterSource paramSource,
ResultSetExtractor<T> resultSetExtractor) {
return this.jdbc.query(sql, paramSource, resultSetExtractor);
}
protected final <T> T queryForObject(String sql, String paramName, Object value,
ResultSetExtractor<T> resultSetExtractor) {
return this.jdbc.query(sql, new MapSqlParameterSource(paramName, value), resultSetExtractor);
}
protected final <T> List<T> queryForList(String sql, RowMapper<T> rowMapper) {
return this.jdbc.query(sql, rowMapper);
}
protected final <T> List<T> queryForList(String sql, SqlParameterSource parameterSource, RowMapper<T> rowMapper) {
return this.jdbc.query(sql, parameterSource, rowMapper);
}
protected final <T> List<T> queryForList(String sql, String paramName, Object value, RowMapper<T> rowMapper) {
return this.jdbc.query(sql, new MapSqlParameterSource(paramName, value), rowMapper);
}
protected final <T> Stream<T> queryForStream(String sql, SqlParameterSource parameterSource,
RowMapper<T> rowMapper) {
return this.jdbc.queryForStream(sql, parameterSource, rowMapper);
}
protected final <T> Stream<T> queryForStream(String sql, String paramName, Object value, RowMapper<T> rowMapper) {
return this.jdbc.queryForStream(sql, new MapSqlParameterSource(paramName, value), rowMapper);
}
protected final <T> Stream<T> queryForStream(String sql, SqlParameterSource parameterSource, Class<T> elementType) {
return this.jdbc.queryForList(sql, parameterSource, elementType).stream();
}
protected final <T> Stream<T> queryForStream(String sql, String paramName, Object value, Class<T> elementType) {
return queryForStream(sql, new MapSqlParameterSource(paramName, value), elementType);
}
protected final boolean queryForBool(String sql, SqlParameterSource parameterSource) {
Boolean result = this.jdbc.queryForObject(sql, parameterSource, Boolean.TYPE);
return Boolean.TRUE.equals(result);
}
protected final boolean queryForBool(String sql, String paramName, Object value) {
return queryForBool(sql, new MapSqlParameterSource(paramName, value));
}
protected final int update(String sql, SqlParameterSource parameterSource) {
return this.jdbc.update(sql, parameterSource);
}
protected final int update(String sql, String paramName, Object value) {
return update(sql, new MapSqlParameterSource(paramName, value));
}
protected final long batchUpdate(String sql, SqlParameterSource[] batchArgs) {
int[] i = this.jdbc.batchUpdate(sql, batchArgs);
return NumberUtil.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);
}
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);
}
protected static final <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) {
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) {
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,
Function<T, E> e) throws E {
if (!Objects.equals(result, expectedValue)) {
throw e.apply(result);
}
}
protected static final void assertUpdateOneRow(int result) {
assertResultEquals(result, 1);
}
protected static final void assertUpdateOneRow(int result, Function<Integer, String> errMsg) {
assertResultEquals(result, 1, errMsg);
}
protected static final 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)
throws E {
assertResultEqualsOrThrow(result, 1, e);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
T[] c,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
if (c == null || c.length == 0) {
return new SqlParameterSource[] {};
}
return buildSqlParameterSourceArray(Arrays.stream(c), paramSourceBuilder);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
Collection<T> c,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
if (CollectionUtils.isEmpty(c)) {
return new SqlParameterSource[] {};
}
return buildSqlParameterSourceArray(c.stream(), paramSourceBuilder);
}
protected static final <T> SqlParameterSource[] buildSqlParameterSourceArray(
Stream<T> stream,
@Nonnull Function<T, SqlParameterSource> paramSourceBuilder) {
Objects.requireNonNull(stream);
Objects.requireNonNull(paramSourceBuilder);
return stream.map(paramSourceBuilder).toArray(SqlParameterSource[]::new);
}
}

View File

@@ -1,5 +1,6 @@
package xyz.zhouxy.plusone.mail;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -14,10 +15,11 @@ import org.springframework.mail.javamail.JavaMailSender;
@Configuration
@EnableConfigurationProperties(PlusoneMailProperties.class)
@ConditionalOnClass(MailService.class)
@EnableAutoConfiguration
public class PlusoneMailAutoConfiguration {
@Bean
MailService mailService(JavaMailSender mailSender, PlusoneMailProperties mailProperties) {
public MailService mailService(JavaMailSender mailSender, PlusoneMailProperties mailProperties) {
MailMessageFactory mailMessageFactory = new MailMessageFactory(mailProperties);
return new SimpleMailService(mailSender, mailMessageFactory);
}

View File

@@ -1,92 +0,0 @@
package xyz.zhouxy.plusone.oss;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Objects;
import org.csource.common.MyException;
import org.csource.fastdfs.ClientGlobal;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import xyz.zhouxy.plusone.oss.FastDFSProperties.ConnectionPool;
@Configuration
@EnableConfigurationProperties(FastDFSProperties.class)
@ConditionalOnClass(FastDFSUtil.class)
public class FastDFSAutoConfig {
@Bean
@SuppressWarnings("all")
FastDFSUtil fastDFSUtil(FastDFSProperties props) throws IOException, FastDFSException {
List<String> trackerServerStrList = props.getTrackerServers();
if (CollectionUtils.isEmpty(trackerServerStrList)) {
throw new FastDFSException(
String.format("configure item %s is required - ", ClientGlobal.PROP_KEY_TRACKER_SERVERS));
}
try {
InetSocketAddress[] trackerServers = trackerServerStrList.stream()
.map(trackerServer -> {
String[] hostPort = trackerServer.trim().split(":");
String host = hostPort[0].trim();
int port = Integer.parseInt(hostPort[1].trim());
return new InetSocketAddress(host, port);
})
.toArray(InetSocketAddress[]::new);
ClientGlobal.initByTrackers(trackerServers);
var connectTimeoutInSecondsConf = props.getConnectTimeoutInSeconds();
if (connectTimeoutInSecondsConf != null) {
ClientGlobal.setG_connect_timeout(connectTimeoutInSecondsConf * 1000);
}
var networkTimeoutInSecondsConf = props.getNetworkTimeoutInSeconds();
if (networkTimeoutInSecondsConf != null) {
ClientGlobal.setG_network_timeout(networkTimeoutInSecondsConf * 1000);
}
var charsetConf = props.getCharset();
if (StringUtils.hasText(charsetConf)) {
ClientGlobal.setG_charset(charsetConf);
}
var httpAntiStealTokenConf = props.getHttpAntiStealToken();
if (httpAntiStealTokenConf != null) {
ClientGlobal.setG_anti_steal_token(httpAntiStealTokenConf);
}
var httpSecretKeyConf = props.getHttpSecretKey();
if (StringUtils.hasText(httpSecretKeyConf)) {
ClientGlobal.setG_secret_key(httpSecretKeyConf);
}
var httpTrackerHttpPortConf = props.getHttpTrackerHttpPort();
if (httpTrackerHttpPortConf != null) {
ClientGlobal.setG_tracker_http_port(httpTrackerHttpPortConf);
}
ConnectionPool connectionPool = props.getConnectionPool();
var poolEnabled = Objects.nonNull(connectionPool)
&& Boolean.TRUE.equals(connectionPool.getEnabled());
if (poolEnabled) {
var poolMaxCountPerEntry = connectionPool.getMaxCountPerEntry();
if (poolMaxCountPerEntry != null) {
ClientGlobal.g_connection_pool_max_count_per_entry = poolMaxCountPerEntry;
}
var poolMaxIdleTime = connectionPool.getMaxIdleTime();
if (poolMaxIdleTime != null) {
ClientGlobal.g_connection_pool_max_idle_time = poolMaxIdleTime * 1000;
}
var poolMaxWaitTimeInMS = connectionPool.getMaxWaitTimeInMs();
if (poolMaxWaitTimeInMS != null) {
ClientGlobal.g_connection_pool_max_wait_time_in_ms = poolMaxWaitTimeInMS;
}
}
return new FastDFSUtil();
} catch (MyException e) {
throw new FastDFSException(e);
}
}
}

View File

@@ -1,29 +0,0 @@
package xyz.zhouxy.plusone.oss;
/**
* 封装 FastDFS 的 {@link org.csource.common.MyException}
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public class FastDFSException extends Exception {
private static final long serialVersionUID = 7871031982887742468L;
public FastDFSException() {
}
public FastDFSException(String message) {
super(message);
}
public FastDFSException(Throwable cause) {
super(cause);
}
public FastDFSException(String message, Throwable cause) {
super(message, cause);
}
public FastDFSException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,121 +0,0 @@
package xyz.zhouxy.plusone.oss;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("fastdfs")
public class FastDFSProperties {
private Integer connectTimeoutInSeconds;
private Integer networkTimeoutInSeconds;
private String charset;
private Boolean httpAntiStealToken;
private String httpSecretKey;
private Integer httpTrackerHttpPort;
private List<String> trackerServers;
private ConnectionPool connectionPool;
public Integer getConnectTimeoutInSeconds() {
return connectTimeoutInSeconds;
}
public void setConnectTimeoutInSeconds(Integer connectTimeoutInSeconds) {
this.connectTimeoutInSeconds = connectTimeoutInSeconds;
}
public Integer getNetworkTimeoutInSeconds() {
return networkTimeoutInSeconds;
}
public void setNetworkTimeoutInSeconds(Integer networkTimeoutInSeconds) {
this.networkTimeoutInSeconds = networkTimeoutInSeconds;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public Boolean getHttpAntiStealToken() {
return httpAntiStealToken;
}
public void setHttpAntiStealToken(Boolean httpAntiStealToken) {
this.httpAntiStealToken = httpAntiStealToken;
}
public String getHttpSecretKey() {
return httpSecretKey;
}
public void setHttpSecretKey(String httpSecretKey) {
this.httpSecretKey = httpSecretKey;
}
public Integer getHttpTrackerHttpPort() {
return httpTrackerHttpPort;
}
public void setHttpTrackerHttpPort(Integer httpTrackerHttpPort) {
this.httpTrackerHttpPort = httpTrackerHttpPort;
}
public List<String> getTrackerServers() {
return trackerServers;
}
public void setTrackerServers(List<String> trackerServers) {
this.trackerServers = trackerServers;
}
public ConnectionPool getConnectionPool() {
return connectionPool;
}
public void setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionPool;
}
public static class ConnectionPool {
private Boolean enabled;
private Integer maxCountPerEntry;
private Integer maxIdleTime;
private Integer maxWaitTimeInMs;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Integer getMaxCountPerEntry() {
return maxCountPerEntry;
}
public void setMaxCountPerEntry(Integer maxCountPerEntry) {
this.maxCountPerEntry = maxCountPerEntry;
}
public Integer getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(Integer maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public Integer getMaxWaitTimeInMs() {
return maxWaitTimeInMs;
}
public void setMaxWaitTimeInMs(Integer maxWaitTimeInMs) {
this.maxWaitTimeInMs = maxWaitTimeInMs;
}
}
}

View File

@@ -1,126 +0,0 @@
package xyz.zhouxy.plusone.oss;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.Getter;
public class FastDFSUtil {
private final TrackerServer trackerServer;
private final StorageServer storageServer;
private static final Logger logger = LoggerFactory.getLogger(FastDFSUtil.class);
FastDFSUtil() throws IOException, MyException {
TrackerClient trackerClient = new TrackerClient();
this.trackerServer = trackerClient.getTrackerServer();
this.storageServer = trackerClient.getStoreStorage(trackerServer);
}
/**
* 上传文件到 Fast DFS失败将抛异常
*
* @param file 文件信息
* @return 2 elements string array if success:<br>
* <ul>
* <li>results[0]: the group name to store the file</li>
* </ul>
* <ul>
* <li>results[1]: the new created filename</li>
* </ul>
* return null if fail
* @throws FastDFSException
*/
public String[] upload(FastDFSFile file) throws FastDFSException {
logger.info("File Name: {}, File Length: {}", file.getName(), file.getContent().length);
NameValuePair[] metaList = new NameValuePair[1];
metaList[0] = new NameValuePair("author", file.getAuthor());
long startTime = System.currentTimeMillis();
StorageClient storageClient = null;
String[] uploadResults = null;
try {
storageClient = new StorageClient(this.trackerServer, this.storageServer);
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), metaList);
if (uploadResults == null) {
throw new FastDFSException("upload file fail, error code: " + storageClient.getErrorCode());
}
logger.info("Upload file successfully!!! group_name: {}, remoteFileName: {}, time used: {} ms",
uploadResults[0], uploadResults[1], System.currentTimeMillis() - startTime);
} catch (IOException e) {
throw new FastDFSException("IO Exception when uploadind the file:" + file.getName(), e);
} catch (MyException e) {
throw new FastDFSException(e);
}
return uploadResults;
}
public FileInfo getFile(String groupName, String remoteFileName) throws FastDFSException {
try {
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
return storageClient.get_file_info(groupName, remoteFileName);
} catch (IOException e) {
throw new FastDFSException("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
throw new FastDFSException("Non IO Exception: Get File from Fast DFS failed", e);
}
}
public InputStream downFile(String groupName, String remoteFileName) throws FastDFSException {
try {
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (IOException e) {
throw new FastDFSException("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
throw new FastDFSException("Non IO Exception: Get File from Fast DFS failed", e);
}
}
public void deleteFile(String groupName, String remoteFileName) throws FastDFSException {
StorageClient storageClient = new StorageClient(this.trackerServer, this.storageServer);
try {
int i = storageClient.delete_file(groupName, remoteFileName);
if (i == 0) {
logger.info("Delete file SUCCESSFULLY!!!");
} else {
throw new FastDFSException("Delete file failed, error code is: " + i);
}
} catch (IOException | MyException e) {
throw new FastDFSException(e);
}
}
@Getter
public static final class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
}
}
}

View File

@@ -11,12 +11,17 @@ import org.springframework.context.annotation.Configuration;
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Configuration
@EnableConfigurationProperties(SmsProperties.class)
@EnableConfigurationProperties(value = {
SmsProperties.class,
SmsCredentialProperties.class,
SmsClientProperties.class,
SmsHttpProperties.class,
SmsProxyProperties.class})
@ConditionalOnClass(SmsService.class)
public class PlusoneSmsAutoConfiguration {
@Bean
SmsService smsService(SmsProperties smsProperties) {
public SmsService smsService(SmsProperties smsProperties) {
return new TencentSmsServiceImpl(smsProperties);
}
}

View File

@@ -4,11 +4,14 @@ import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
/**
* SMS 相关参数
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Data
@ConfigurationProperties("plusone.sms")
public class SmsProperties {
private String region;
@@ -16,138 +19,33 @@ public class SmsProperties {
private SmsClientProperties client;
private String appId;
private Map<String, String> templates;
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public SmsCredentialProperties getCredential() {
return credential;
}
public void setCredential(SmsCredentialProperties credential) {
this.credential = credential;
}
public SmsClientProperties getClient() {
return client;
}
public void setClient(SmsClientProperties client) {
this.client = client;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public Map<String, String> getTemplates() {
return templates;
}
public void setTemplates(Map<String, String> templates) {
this.templates = templates;
}
public static class SmsCredentialProperties {
private String secretId;
private String secretKey;
public String getSecretId() {
return secretId;
}
public void setSecretId(String secretId) {
this.secretId = secretId;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
}
public static class SmsClientProperties {
private String signMethod;
private SmsHttpProperties http;
public String getSignMethod() {
return signMethod;
}
public void setSignMethod(String signMethod) {
this.signMethod = signMethod;
}
public SmsHttpProperties getHttp() {
return http;
}
public void setHttp(SmsHttpProperties http) {
this.http = http;
}
}
public static class SmsHttpProperties {
private SmsProxyProperties proxy;
private String reqMethod;
private Integer connTimeout;
public SmsProxyProperties getProxy() {
return proxy;
}
public void setProxy(SmsProxyProperties proxy) {
this.proxy = proxy;
}
public String getReqMethod() {
return reqMethod;
}
public void setReqMethod(String reqMethod) {
this.reqMethod = reqMethod;
}
public Integer getConnTimeout() {
return connTimeout;
}
public void setConnTimeout(Integer connTimeout) {
this.connTimeout = connTimeout;
}
}
public static class SmsProxyProperties {
private String host;
private Integer port;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}
}
@Data
@ConfigurationProperties("plusone.sms.credential")
class SmsCredentialProperties {
private String secretId;
private String secretKey;
}
@Data
@ConfigurationProperties("plusone.sms.client")
class SmsClientProperties {
private String signMethod;
private SmsHttpProperties http;
}
@Data
@ConfigurationProperties("plusone.sms.client.http")
class SmsHttpProperties {
private SmsProxyProperties proxy;
private String reqMethod;
private Integer connTimeout;
}
@Data
@ConfigurationProperties("plusone.sms.client.http.proxy")
class SmsProxyProperties {
private String host;
private Integer port;
}

View File

@@ -1,7 +1,5 @@
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;
@@ -10,11 +8,11 @@ import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.exception.SysException;
import xyz.zhouxy.plusone.sms.SmsProperties.SmsCredentialProperties;
import xyz.zhouxy.plusone.sms.SmsProperties.SmsHttpProperties;
import xyz.zhouxy.plusone.sms.SmsProperties.SmsProxyProperties;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.PlusoneException;
/**
* 使用腾讯 SMS 服务
@@ -61,13 +59,14 @@ public class TencentSmsServiceImpl implements SmsService {
var res = client.SendSms(req);
// 输出json格式的字符串回包
log.info(SendSmsResponse.toJsonString(res));
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
// System.out.println(res.getRequestId());
} catch (TencentCloudSDKException e) {
throw new SysException(e);
log.error(e.getMessage(), e);
throw new PlusoneException(ErrorCodeConsts.DEFAULT_ERROR_CODE, e);
}
}

View File

@@ -0,0 +1,34 @@
package xyz.zhouxy.plusone.sql;
import java.util.Objects;
import org.apache.ibatis.jdbc.AbstractSQL;
/**
* 扩展 MyBatis 中的 SQL 语句构造器
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public class SQL extends AbstractSQL<SQL> {
public SQL SET_IF(boolean condition, String sets) {
return condition ? SET(sets) : getSelf();
}
public SQL SET_IF_NOT_NULL(Object param, String sets) {
return Objects.nonNull(param) ? SET(sets) : getSelf();
}
public SQL WHERE_IF(boolean condition, String sqlCondition) {
return condition ? WHERE(sqlCondition) : getSelf();
}
public SQL WHERE_IF_NOT_NULL(Object param, String sqlCondition) {
return Objects.nonNull(param) ? WHERE(sqlCondition) : getSelf();
}
@Override
public SQL getSelf() {
return this;
}
}

View File

@@ -2,13 +2,14 @@ package xyz.zhouxy.plusone.validator;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
@RestControllerAdvice
public class InvalidInputExceptionHandler extends BaseExceptionHandler {
public InvalidInputExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
protected InvalidInputExceptionHandler() {
super(new ExceptionInfoHolder(ErrorCodeConsts.DEFAULT_ERROR_CODE));
set(InvalidInputException.class, InvalidInputException.ERROR_CODE, "无效的用户输入");
}
}

View File

@@ -1,4 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xyz.zhouxy.plusone.sms.PlusoneSmsAutoConfiguration,\
xyz.zhouxy.plusone.mail.PlusoneMailAutoConfiguration,\
xyz.zhouxy.plusone.oss.FastDFSAutoConfig

View File

@@ -1,16 +1,14 @@
package xyz.zhouxy.plusone.validatortest;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Test;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import xyz.zhouxy.plusone.validator.BaseValidator;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.validator.validator2.BaseValidator2;
class BaseValidatorTest {
class BaseValidator2Test {
@Test
void testValid() {
@@ -29,18 +27,17 @@ class LoginCommand {
private boolean rememberMe;
}
class LoginCommandValidator extends BaseValidator<LoginCommand> {
class LoginCommandValidator extends BaseValidator2<LoginCommand> {
public static final LoginCommandValidator INSTANCE = new LoginCommandValidator();
private LoginCommandValidator() {
ruleForString(LoginCommand::getAccount)
ruleFor(LoginCommand::getAccount)
.notNull("邮箱地址不能为空")
.matchesOr(new Pattern[] { PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE },
value -> new RuntimeException('"' + value + "\" 不是邮箱地址或手机号"));
ruleForString(LoginCommand::getPwd)
.matchesOr(new String[] { RegexConsts.EMAIL, RegexConsts.MOBILE_PHONE }, value -> new RuntimeException('"' + value + "\" 不是邮箱地址或手机号"));
ruleFor(LoginCommand::getPwd)
.notNull("密码不能为空")
.notEmpty("密码不能为空")
.matches(PatternConsts.PASSWORD, "密码格式错误");
.matches(RegexConsts.PASSWORD, "密码格式错误");
}
}

View File

@@ -7,7 +7,9 @@
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>xyz.zhouxy</groupId>
<artifactId>plusone-basic</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

View File

@@ -7,11 +7,18 @@
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>xyz.zhouxy</groupId>
<artifactId>plusone-start</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>plusone-start</name>
<description>参考 DDD 落地的脚手架</description>
<properties>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<dependency>

View File

@@ -1,32 +1,34 @@
{"properties": [
{
"name": "plusone.application.name",
"type": "java.lang.String",
"description": "A description for 'plusone.application.name'"
},
{
"name": "plusone.server.port",
"type": "java.lang.Integer",
"description": "A description for 'plusone.server.port'"
},
{
"name": "plusone.debug",
"type": "java.lang.Boolean",
"description": "A description for 'plusone.debug'"
},
{
"name": "plusone.mail.host",
"type": "java.lang.String",
"description": "A description for 'plusone.mail.host'"
},
{
"name": "plusone.mail.password",
"type": "java.lang.String",
"description": "A description for 'plusone.mail.password'"
},
{
"name": "plusone.exception.handle-all-exception",
"type": "java.lang.Boolean",
"description": "A description for 'plusone.exception.handle-all-exception'"
}
]}
{
"properties": [
{
"name": "plusone.application.name",
"type": "java.lang.String",
"description": "A description for 'plusone.application.name'"
},
{
"name": "plusone.server.port",
"type": "java.lang.Integer",
"description": "A description for 'plusone.server.port'"
},
{
"name": "plusone.debug",
"type": "java.lang.Boolean",
"description": "A description for 'plusone.debug'"
},
{
"name": "plusone.mail.host",
"type": "java.lang.String",
"description": "A description for 'plusone.mail.host'"
},
{
"name": "plusone.mail.password",
"type": "java.lang.String",
"description": "A description for 'plusone.mail.password'"
},
{
"name": "plusone.exception.handle-all-exception",
"type": "java.lang.Boolean",
"description": "A description for 'plusone.exception.handle-all-exception'"
}
]
}

View File

@@ -1,78 +0,0 @@
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
# password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 数据库
datasource:
url: jdbc:postgresql://localhost:5432/plusone
username: plusone
password: XXXXXXXXXXXXXXXX
plusone:
application:
name: plusone
server:
port: 8108
debug: true
# 短信发送相关参数
sms:
region: ap-guangzhou
credential:
secret-id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
secret-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
client:
# SDK 默认用 TC3-HMAC-SHA256 进行签名,非必要请不要修改这个字段
sign-method: HmacSHA256
http:
# proxy:
# host: xxx
# port: xxx
req-method: POST
conn-timeout: 60
app-id: 1111111111
templates:
'[code]': 0000000
# 邮件发送相关参数
mail:
host: smtp.163.com
#设置邮件发送者
from: example@163.com
password: XXXXXXXXXXXXXXXX
# 异常拦截机制是否拦截所有异常
exception:
handle-all-exception: false
fastdfs:
connect_timeout_in_seconds: 5
network_timeout_in_seconds: 30
charset: UTF-8
http_anti_steal_token: false
http_secret_key: FastDFS1234567890
http_tracker_http_port: 80
tracker_servers: 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122
connection_pool:
enabled: true
max_count_per_entry: 500
max_idle_time: 3600
max_wait_time_in_ms: 1000

View File

@@ -1,16 +1,79 @@
spring:
profiles:
active: public
plusone:
application:
name: plusone
server:
port: 8108
debug: true
# 短信发送相关参数
sms:
region: ap-guangzhou
credential:
secret-id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
secret-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
client:
# SDK 默认用 TC3-HMAC-SHA256 进行签名,非必要请不要修改这个字段
sign-method: HmacSHA256
http:
# proxy:
# host: xxx
# port: xxx
req-method: POST
conn-timeout: 60
app-id: 1111111111
templates:
code: 0000000
# 邮件发送相关参数
mail:
host: smtp.163.com
#设置邮件发送者
from: example@163.com
password: XXXXXXXXXXXXXXXX
subject:
'[code]': Plusone
code: Plusone
template:
'[code]': 【Plusone】验证码%s10分钟内有效请勿泄露。
code: 【Plusone】验证码%s10分钟内有效请勿泄露。
# 异常拦截机制是否拦截所有异常
exception:
handle-all-exception: false
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
# password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 数据库
datasource:
url: jdbc:postgresql://localhost:5432/plusone
username: plusone
password: XXXXXXXXXXXXXXXX
# 日志配置
logging:
file:
name: ${user.home}/logs/${plusone.application.name}.log
level:
root: info
'[xyz.zhouxy.plusone]': debug
root: INFO
xyz.zhouxy.plusone: DEBUG
org.springframework.jdbc.core: DEBUG
xyz.zhouxy.plusone.system.application.query: DEBUG

View File

@@ -10,23 +10,23 @@
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5level ${PID:- } - [%15.15t] %-50.50logger : %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<file>log/plusone.log</file>
<file>log/output.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>log/plusone.log.%i</fileNamePattern>
<fileNamePattern>log/output.log.%i</fileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>20MB</MaxFileSize>
</triggeringPolicy>
</appender>
</appender> -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<!-- <appender-ref ref="FILE" /> -->
</root>
</configuration>

View File

@@ -1,33 +0,0 @@
package xyz.zhouxy.plusone;
import java.io.FileInputStream;
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;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.oss.FastDFSException;
import xyz.zhouxy.plusone.oss.FastDFSUtil;
import xyz.zhouxy.plusone.oss.FastDFSUtil.FastDFSFile;
@SpringBootTest(classes = PlusoneApplication.class)
@Slf4j
class FastDFSTests {
@Resource
FastDFSUtil fastDFSUtil;
@Test
void testOSS() throws FileNotFoundException, IOException, FastDFSException {
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));
}
}
}

View File

@@ -5,14 +5,14 @@ import java.io.ObjectStreamClass;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.exception.*;
import xyz.zhouxy.plusone.exception.PlusoneException;
@Slf4j
class SerialTests {
@Test
void testSerialVersionUID() {
var cl = SysException.class;
var cl = PlusoneException.class;
var c = ObjectStreamClass.lookup(cl);
var uid = c.getSerialVersionUID();
log.info("\n @java.io.Serial" +

View File

@@ -1,7 +1,5 @@
package xyz.zhouxy.plusone;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
@@ -27,7 +25,6 @@ class TestAop {
command.setPrincipal("Code108");
command.setPassword("2333");
command.setRememberMe(false);
assertNotNull(service);
LoginInfoViewObject loginInfo = service.loginByPassword(command);
System.err.println(loginInfo);
}

View File

@@ -1,16 +0,0 @@
spring:
profiles:
active: secret
plusone:
# 邮件发送相关参数
mail:
subject:
'[code]': Plusone
template:
'[code]': 【Plusone】验证码%s10分钟内有效请勿泄露。
# 日志配置
logging:
level:
root: info
'[xyz.zhouxy.plusone]': debug

View File

@@ -1,32 +0,0 @@
package xyz.zhouxy.plusone.system.application.common.exception;
import xyz.zhouxy.plusone.exception.BizException;
/**
* 不支持的 Principal 类型出现时抛出的异常
*
* @author <a href="https://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public class UnsupportedPrincipalTypeException extends BizException {
private static final long serialVersionUID = 5207757290868470762L;
public static final int ERR_CODE = 4040201;
private static final String DEFAULT_ERROR_MSG = "不支持的 PrincipalType";
public UnsupportedPrincipalTypeException() {
super(ERR_CODE, DEFAULT_ERROR_MSG);
}
public UnsupportedPrincipalTypeException(String msg) {
super(ERR_CODE, msg);
}
public UnsupportedPrincipalTypeException(Throwable cause) {
super(ERR_CODE, cause);
}
public UnsupportedPrincipalTypeException(String msg, Throwable cause) {
super(ERR_CODE, msg, cause);
}
}

View File

@@ -1,20 +1,18 @@
package xyz.zhouxy.plusone.system.application.common.util;
import java.util.regex.Pattern;
import lombok.Getter;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import xyz.zhouxy.plusone.constant.RegexConsts;
public enum PrincipalType {
EMAIL(PatternConsts.EMAIL),
MOBILE_PHONE(PatternConsts.MOBILE_PHONE),
USERNAME(PatternConsts.USERNAME)
EMAIL(RegexConsts.EMAIL),
MOBILE_PHONE(RegexConsts.MOBILE_PHONE),
USERNAME(RegexConsts.USERNAME)
;
@Getter
private final Pattern regex;
private final String regex;
PrincipalType(Pattern regex) {
PrincipalType(String regex) {
this.regex = regex;
}
}

View File

@@ -2,11 +2,11 @@ package xyz.zhouxy.plusone.system.application.common.util;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.validator.InvalidInputException;
/**
* 根据字面值,判断并生成 {@link Principal} 值对象。
@@ -16,7 +16,7 @@ import xyz.zhouxy.plusone.system.domain.model.account.Username;
* @see Username
* @see Email
* @see MobilePhone
* @see UnsupportedPrincipalTypeException
* @see InvalidInputException
*/
public class PrincipalUtil {
@@ -30,11 +30,11 @@ public class PrincipalUtil {
}
PrincipalType[] principalTypes = PrincipalType.values();
for (var principalType : principalTypes) {
if (principalType.getRegex().matcher(principal).matches()) {
if (principal.matches(principalType.getRegex())) {
return principalType;
}
}
throw new UnsupportedPrincipalTypeException();
throw InvalidInputException.unsupportedPrincipalTypeException();
}
public static Principal getPrincipal(@Nullable String principal) {
@@ -48,7 +48,7 @@ public class PrincipalUtil {
if (principalType == PrincipalType.USERNAME) {
return Username.of(principal);
}
throw new UnsupportedPrincipalTypeException();
throw InvalidInputException.unsupportedPrincipalTypeException();
}
public static Principal getEmailOrMobilePhone(@Nullable String principal) {
@@ -59,6 +59,6 @@ public class PrincipalUtil {
if (principalType == PrincipalType.MOBILE_PHONE) {
return MobilePhone.of(principal);
}
throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
}
}

View File

@@ -7,10 +7,10 @@ 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.system.application.service.AccountContextService;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordWithoutLoginCommand;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
* 账号查询本身相关信息

View File

@@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.util.RestfulResult.success;
import java.util.List;
@@ -16,11 +16,12 @@ 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.system.application.query.params.AccountQueryParams;
import xyz.zhouxy.plusone.system.application.service.AccountManagementService;
import xyz.zhouxy.plusone.system.application.service.command.CreateAccountCommand;
import xyz.zhouxy.plusone.system.application.service.command.UpdateAccountCommand;
import xyz.zhouxy.plusone.util.AssertResult;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
* 账号管理
@@ -76,6 +77,7 @@ public class AccountManagementController {
adminAuthLogic.checkLogin();
adminAuthLogic.checkPermission("sys-account-details");
var accountDetails = service.queryAccountDetails(accountId);
AssertResult.nonNull(accountDetails);
return success("查询成功", accountDetails);
}
}

View File

@@ -1,6 +1,6 @@
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.util.RestfulResult.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.util.RestfulResult;
/**
* Admin 账号登录

View File

@@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.util.RestfulResult.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.util.RestfulResult;
/**
* 数据字典管理

View File

@@ -1,7 +1,7 @@
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.util.RestfulResult.success;
import javax.validation.Valid;
@@ -15,11 +15,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.system.application.query.params.MenuQueryParams;
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.util.RestfulResult;
/**
* 菜单管理
@@ -67,14 +66,7 @@ public class MenuManagementController {
public RestfulResult findById(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-menu-details");
var result = service.findById(id);
return success("查询成功", result);
}
@GetMapping
public RestfulResult queryMenuTree(MenuQueryParams queryParams) {
adminAuthLogic.checkPermission("sys-menu-query");
var result = service.queryMenuTree(queryParams);
return success("查询成功", result);
return RestfulResult.success("查询成功", result);
}
@GetMapping("queryByAccountId")

View File

@@ -1,6 +1,6 @@
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;
import static xyz.zhouxy.plusone.util.RestfulResult.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.util.RestfulResult;
/**
* 注册账号服务

View File

@@ -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.util.RestfulResult;
/**
* 角色管理服务

View File

@@ -3,10 +3,10 @@ package xyz.zhouxy.plusone.system.application.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import xyz.zhouxy.plusone.exception.BizException;
import xyz.zhouxy.plusone.exception.PlusoneException;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class AccountLoginException extends BizException {
public class AccountLoginException extends PlusoneException {
@java.io.Serial
private static final long serialVersionUID = -3040996790739138556L;
@@ -21,34 +21,18 @@ public class AccountLoginException extends BizException {
}
public static AccountLoginException accountNotExistException() {
return accountNotExistException("用户账户不存在");
}
public static AccountLoginException accountNotExistException(String msg) {
return new AccountLoginException(4030101, msg);
return new AccountLoginException(4030101, "用户账户不存在");
}
public static AccountLoginException otpErrorException() {
return otpErrorException("验证码错误");
}
public static AccountLoginException otpErrorException(String msg) {
return new AccountLoginException(4030501, msg);
return new AccountLoginException(4030501, "验证码错误");
}
public static AccountLoginException otpNotExistsException() {
return otpNotExistsException("验证码不存在或已过期");
}
public static AccountLoginException otpNotExistsException(String msg) {
return new AccountLoginException(4030502, msg);
return new AccountLoginException(4030502, "验证码不存在或已过期");
}
public static AccountLoginException passwordErrorException() {
return passwordErrorException("用户密码错误");
}
public static AccountLoginException passwordErrorException(String msg) {
return new AccountLoginException(4030200, msg);
return new AccountLoginException(4030200, "用户密码错误");
}
}

View File

@@ -3,10 +3,10 @@ package xyz.zhouxy.plusone.system.application.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import xyz.zhouxy.plusone.exception.BizException;
import xyz.zhouxy.plusone.exception.PlusoneException;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class AccountRegisterException extends BizException {
public class AccountRegisterException extends PlusoneException {
@java.io.Serial
private static final long serialVersionUID = 7580245181633370195L;

View File

@@ -1,24 +0,0 @@
package xyz.zhouxy.plusone.system.application.exception.handler;
import javax.annotation.Nonnull;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
@RestControllerAdvice
public class AccountLoginExceptionHandler extends BaseExceptionHandler {
public AccountLoginExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
}
@ExceptionHandler({ AccountLoginException.class })
public ResponseEntity<RestfulResult> handleException(@Nonnull Exception e) {
return buildExceptionResponse(e);
}
}

View File

@@ -15,7 +15,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
* Sa-Token 异常处理器
@@ -26,7 +26,6 @@ import xyz.zhouxy.plusone.commons.util.RestfulResult;
@Slf4j
public class SaTokenExceptionHandler extends BaseExceptionHandler {
public SaTokenExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
set(NotPermissionException.class, 4030103, "会话未能通过权限认证", HttpStatus.FORBIDDEN);
@@ -37,7 +36,7 @@ public class SaTokenExceptionHandler extends BaseExceptionHandler {
set(NotSafeException.class, 4020300, "会话未能通过二级认证", HttpStatus.UNAUTHORIZED);
set(NotLoginException.class,
4020400,
e -> switch (e.getType()) {
e -> switch (((NotLoginException) e).getType()) {
case NotLoginException.NOT_TOKEN -> "未提供 Token";
case NotLoginException.INVALID_TOKEN -> "Token 无效";
case NotLoginException.TOKEN_TIMEOUT -> "Token 已过期";

View File

@@ -4,10 +4,10 @@ import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import xyz.zhouxy.plusone.commons.util.PageDTO;
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
import xyz.zhouxy.plusone.system.application.query.result.AccountOverview;
import xyz.zhouxy.plusone.util.PageDTO;
/**
* 账号信息查询器

View File

@@ -2,12 +2,12 @@ package xyz.zhouxy.plusone.system.application.query;
import java.util.List;
import org.apache.ibatis.jdbc.SQL;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import xyz.zhouxy.plusone.sql.SQL;
import xyz.zhouxy.plusone.system.application.query.params.DictQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.DictOverview;
@@ -26,21 +26,14 @@ public class DictQueries {
}
public List<DictOverview> queryDictOverviewList(DictQueryParams queryParams) {
String sql = new SQL() {
{
SELECT("id", "dict_type", "dict_label",
"created_by", "create_time", "updated_by", "update_time", "count");
FROM("view_sys_dict_overview");
if (queryParams.getDictType() != null) {
WHERE("dict_type LIKE '%:dictType%'");
}
if (queryParams.getDictLabel() != null) {
WHERE("dict_label LIKE '%:dictLabel%'");
}
}
}.toString();
String sql = new SQL()
.SELECT("id", "dict_type", "dict_label",
"created_by", "create_time", "updated_by", "update_time", "count")
.FROM("view_sys_dict_overview")
.WHERE_IF(queryParams.getDictType() != null, "dict_type LIKE '%:dictType%'")
.WHERE_IF(queryParams.getDictLabel() != null, "dict_label LIKE '%:dictLabel%'")
.toString();
return this.jdbcTemplate
.query(sql, new BeanPropertySqlParameterSource(queryParams),
new BeanPropertyRowMapper<>(DictOverview.class));
.query(sql, new BeanPropertySqlParameterSource(queryParams), new BeanPropertyRowMapper<>(DictOverview.class));
}
}

View File

@@ -1,23 +0,0 @@
package xyz.zhouxy.plusone.system.application.query;
import java.util.List;
import org.springframework.stereotype.Component;
import xyz.zhouxy.plusone.system.application.query.params.MenuQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Component
public class MenuQueries {
public List<MenuViewObject> queryMenuTree(MenuQueryParams queryParams) {
// TODO【添加】 实现该查询
return null;
}
}

View File

@@ -0,0 +1,10 @@
package xyz.zhouxy.plusone.system.application.query;
/**
*
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public interface PermissionQueries {
// TODO【添加】 权限信息查询器
}

View File

@@ -7,7 +7,7 @@ import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.apache.ibatis.jdbc.SQL;
import xyz.zhouxy.plusone.sql.SQL;
import xyz.zhouxy.plusone.system.application.query.params.RoleQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.RoleOverview;
@@ -35,47 +35,30 @@ public class RoleQueries {
* @return 查询结果
*/
public List<RoleOverview> query(RoleQueryParams params) {
var sql = new SQL() {
{
SELECT("a.id AS id", "a.name AS name", "a.identifier AS identifier", "a.status AS status");
FROM("sys_role AS a", "(" +
new SQL() {
{
SELECT("id");
FROM("sys_role");
if (null != params.getId()) {
WHERE("id = :id");
}
if (null != params.getName()) {
WHERE("name = :name");
}
if (null != params.getIdentifier()) {
WHERE("identifier = :identifier");
}
if (null != params.getStatus()) {
WHERE("status = :status");
}
if (null != params.getCreateTimeStart()) {
WHERE("create_time >= :createTimeStart");
}
if (null != params.getCreateTimeEnd()) {
WHERE("create_time < :createTimeEnd");
}
if (null != params.getUpdateTimeStart()) {
WHERE("update_time >= :updateTimeStart");
}
if (null != params.getUpdateTimeEnd()) {
WHERE("update_time < :updateTimeEnd");
}
LIMIT(params.getSize());
OFFSET(params.getOffset());
}
}.toString() + ") AS b");
WHERE("a.id = b.id");
}
}.toString();
String b = new SQL()
.SELECT("id")
.FROM("sys_role")
.WHERE_IF_NOT_NULL(params.getId(), "id = :id")
.WHERE_IF_NOT_NULL(params.getName(), "name = :name")
.WHERE_IF_NOT_NULL(params.getIdentifier(), "identifier = :identifier")
.WHERE_IF_NOT_NULL(params.getStatus(), "status = :status")
.WHERE_IF_NOT_NULL(params.getCreateTimeStart(), "create_time >= :createTimeStart")
.WHERE_IF_NOT_NULL(params.getCreateTimeEnd(), "create_time < :createTimeEnd")
.WHERE_IF_NOT_NULL(params.getUpdateTimeStart(), "update_time >= :updateTimeStart")
.WHERE_IF_NOT_NULL(params.getUpdateTimeEnd(), "update_time < :updateTimeEnd")
.LIMIT(params.getSize())
.OFFSET(params.getOffset())
.toString();
var sql = """
SELECT a.id AS id, a.name AS name, a.identifier AS identifier, a.status AS status
FROM sys_role AS a, (
""" + b + """
) AS b
WHERE a.id = b.id
""";
return this.jdbcTemplate
.query(sql, new BeanPropertySqlParameterSource(params),
new BeanPropertyRowMapper<>(RoleOverview.class));
}
}

View File

@@ -5,7 +5,7 @@ import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
/**
* 账号信息查询参数

View File

@@ -4,7 +4,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
/**
* 数据字典查询参数

View File

@@ -1,18 +0,0 @@
package xyz.zhouxy.plusone.system.application.query.params;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import xyz.zhouxy.plusone.constant.EntityStatus;
@ToString
public class MenuQueryParams {
private @Getter @Setter String name;
private @Getter @Setter String path;
private @Getter @Setter String title;
private @Getter @Setter Boolean hidden;
private @Getter @Setter EntityStatus status;
private @Getter @Setter String component;
private @Getter @Setter Boolean cache;
private @Getter @Setter String resource;
}

View File

@@ -5,7 +5,7 @@ import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
import xyz.zhouxy.plusone.util.PagingAndSortingQueryParams;
/**
* 角色信息查询参数

View File

@@ -25,28 +25,60 @@ import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MenuViewObject implements IWithOrderNumber {
private @Getter @Setter Integer type;
@Getter
@Setter
Integer type;
private @Getter @Setter String typeName;
@Getter
@Setter
String typeName;
private @Getter @Setter Long id;
private @Getter @Setter Long parentId;
@Getter
@Setter
Long id;
@Getter
@Setter
Long parentId;
private @Getter @Setter String name;
@Getter
@Setter
String name;
// 若 type 为 MENU_ITEM 且 path 以 http:// 或 https:// 开头则被识别为外链
private @Getter @Setter String path;
private @Getter @Setter String title;
private @Getter @Setter String icon;
private @Getter @Setter boolean hidden;
private @Getter @Setter int orderNumber;
private @Getter @Setter Integer status;
private @Getter @Setter String remarks;
@Getter
@Setter
String path;
@Getter
@Setter
String title;
@Getter
@Setter
String icon;
@Getter
@Setter
boolean hidden;
@Getter
@Setter
int orderNumber;
@Getter
@Setter
Integer status;
@Getter
@Setter
String remarks;
// MENU_ITEM
private @Getter @Setter String component;
private @Getter @Setter Boolean cache;
private @Getter @Setter String resource;
private @Getter @Setter List<Action> actions;
@Getter
@Setter
String component;
@Getter
@Setter
Boolean cache;
@Getter
@Setter
String resource;
@Getter
@Setter
List<Action> actions;
// MENU_LIST
List<MenuViewObject> children;
@@ -77,7 +109,7 @@ public class MenuViewObject implements IWithOrderNumber {
viewObject.icon = menu.getIcon();
viewObject.hidden = menu.isHidden();
viewObject.orderNumber = menu.getOrderNumber();
viewObject.status = menu.getStatus().getId();
viewObject.status = menu.getStatus().getValue();
viewObject.remarks = menu.getRemarks();
if (viewObject.type == MenuType.MENU_ITEM.ordinal()) {
viewObject.component = menu.getComponent();

View File

@@ -1,5 +1,7 @@
package xyz.zhouxy.plusone.system.application.service;
import xyz.zhouxy.plusone.system.constant.AuthLogic;
import java.util.List;
import javax.annotation.Resource;
@@ -8,7 +10,7 @@ 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.UnsupportedPrincipalTypeException;
import cn.hutool.core.lang.Assert;
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;
@@ -17,12 +19,12 @@ import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordByOtpCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordWithoutLoginCommand;
import xyz.zhouxy.plusone.system.constant.AuthLogic;
import xyz.zhouxy.plusone.system.domain.model.account.Account;
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
import xyz.zhouxy.plusone.validator.InvalidInputException;
/**
* 账号对当前帐号进行操作
@@ -32,7 +34,7 @@ import xyz.zhouxy.plusone.system.domain.model.account.Principal;
@Service
public class AccountContextService {
private static final StpLogic adminAuthLogic = AuthLogic.adminAuthLogic;
private final static StpLogic adminAuthLogic = AuthLogic.adminAuthLogic;
@Resource
private AccountQueries accountQueries;
@@ -63,8 +65,7 @@ public class AccountContextService {
@Transactional
public void changePassword(ChangePasswordCommand command) {
adminAuthLogic.checkLogin();
Account account = accountRepository.find(adminAuthLogic.getLoginIdAsLong())
.orElseThrow(() -> AccountLoginException.accountNotExistException("当前所登录的账号不存在"));
Account account = accountRepository.find(adminAuthLogic.getLoginIdAsLong());
account.checkPassword(command.getPassword());
account.changePassword(command.getNewPassword(), command.getPasswordConfirmation());
accountRepository.save(account);
@@ -76,10 +77,9 @@ public class AccountContextService {
String principal = command.getAccount();
Principal emailOrMobilePhone = PrincipalUtil.getEmailOrMobilePhone(principal);
Account account = (emailOrMobilePhone instanceof Email
Account account = emailOrMobilePhone instanceof Email
? accountRepository.findByEmail((Email) emailOrMobilePhone)
: accountRepository.findByMobilePhone((MobilePhone) emailOrMobilePhone))
.orElseThrow(() -> AccountLoginException.accountNotExistException("当前所登录的账号不存在"));
: accountRepository.findByMobilePhone((MobilePhone) emailOrMobilePhone);
account.checkPassword(command.getOldPassword());
account.changePassword(command.getNewPassword(), command.getPasswordConfirmation());
accountRepository.save(account);
@@ -90,14 +90,13 @@ public class AccountContextService {
public void changePasswordByOtp(ChangePasswordByOtpCommand command) {
var principal = command.getAccount();
boolean accountExists = switch (command.getPrincipalType()) {
case EMAIL -> accountRepository.existsEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.existsMobilePhone(MobilePhone.of(principal));
default -> throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
Account account = switch (command.getPrincipalType()) {
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
default -> throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
};
if (accountExists) {
throw AccountLoginException.accountNotExistException();
}
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());
}
}

View File

@@ -14,8 +14,6 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
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.query.AccountQueries;
import xyz.zhouxy.plusone.system.application.query.params.AccountQueryParams;
@@ -26,12 +24,11 @@ import xyz.zhouxy.plusone.system.application.service.command.UpdateAccountComman
import xyz.zhouxy.plusone.system.domain.model.account.Account;
import xyz.zhouxy.plusone.system.domain.model.account.AccountInfo;
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.AccountStatus;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.util.AssertResult;
import xyz.zhouxy.plusone.util.PageDTO;
/**
* 账号管理
@@ -69,13 +66,9 @@ public class AccountManagementService {
mobilePhone,
command.getPassword(),
command.getPasswordConfirmation(),
AccountStatus.of(command.getStatus()),
command.getStatus(),
command.getRoleRefs(),
AccountInfo.builder()
.nickname(command.getNickname())
.avatar(command.getAvatar())
.sex(Sex.of(command.getSex()))
.build(),
AccountInfo.of(command.getNickname(), command.getAvatar(), command.getSex()),
adminAuthLogic.getLoginIdAsLong());
accountRepository.save(account);
}
@@ -83,17 +76,17 @@ public class AccountManagementService {
public void deleteAccounts(List<Long> ids) {
Account accountToDelete;
for (var id : ids) {
accountToDelete = accountRepository.find(id)
.orElseThrow(() -> new DataNotExistException("该账号不存在"));
accountToDelete = accountRepository.find(id);
AssertResult.nonNull(accountToDelete);
accountRepository.delete(accountToDelete);
}
}
public void updateAccountInfo(Long id, @Valid UpdateAccountCommand command) {
Assert.isTrue(Objects.equals(id, command.getId()), "参数错误: id 不匹配");
Account account = accountRepository.find(id)
.orElseThrow(() -> new DataNotExistException("该账号不存在"));
account.setAccountInfo(command.getNickname(), command.getAvatar(), Sex.of(command.getSex()));
Account account = accountRepository.find(id);
AssertResult.nonNull(account, "该账号不存在");
account.setAccountInfo(command.getNickname(), command.getAvatar(), command.getSex());
account.setUpdatedBy(adminAuthLogic.getLoginIdAsLong());
accountRepository.save(account);
}
@@ -103,13 +96,6 @@ public class AccountManagementService {
return accountQueries.queryAccountOverviewPage(queryParams);
}
/**
* 查询账号详细信息,如果查不到将抛出 {@link DataNotExistException}。
*
* @param accountId 账号 id
* @return 账号信息
* @throws DataNotExistException 查询不到数据时抛出异常
*/
@Transactional(propagation = Propagation.SUPPORTS)
public AccountDetails queryAccountDetails(@PathVariable("accountId") Long accountId) {
var accountDetails = accountQueries.queryAccountDetails(accountId);

View File

@@ -6,7 +6,6 @@ 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.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;
@@ -19,6 +18,7 @@ import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.validator.InvalidInputException;
import xyz.zhouxy.plusone.validator.ValidateDto;
/**
@@ -44,13 +44,14 @@ public class AdminLoginService {
@ValidateDto
public LoginInfoViewObject loginByPassword(LoginByPasswordCommand command) {
var principal = command.getPrincipal();
Account account = (switch (command.getPrincipalType()) {
Account account = switch (command.getPrincipalType()) {
case USERNAME -> accountRepository.findByUsername(Username.of(principal));
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
}).orElseThrow(AccountLoginException::accountNotExistException);
};
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
var isPasswordCorrect = account.checkPassword(command.getPassword());
Assert.isTrue(isPasswordCorrect, AccountLoginException::passwordErrorException);
Assert.isTrue(isPasswordCorrect, () -> AccountLoginException.passwordErrorException());
adminAuthLogic.login(account.getId().orElseThrow(), command.isRememberMe());
var accountDetails = accountQueries.queryAccountDetails(account.getId().orElseThrow());
@@ -60,11 +61,12 @@ public class AdminLoginService {
@ValidateDto
public LoginInfoViewObject loginByOtp(LoginByOtpCommand command) {
var principal = command.getPrincipal();
Account account = (switch (command.getPrincipalType()) {
Account account = switch (command.getPrincipalType()) {
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
default -> throw new UnsupportedPrincipalTypeException("输入邮箱地址或手机号");
}).orElseThrow(AccountLoginException::accountNotExistException);
default -> throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
};
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());

View File

@@ -10,7 +10,6 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.system.application.query.DictQueries;
import xyz.zhouxy.plusone.system.application.query.params.DictQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.DictOverview;
@@ -46,21 +45,21 @@ public class DictManagementService {
public void deleteDicts(List<Long> ids) {
Dict dictToDelete;
for (Long id : ids) {
dictToDelete = dictRepository.find(id).orElseThrow(DataNotExistException::new);
dictToDelete = dictRepository.find(id);
dictRepository.delete(dictToDelete);
}
}
public void updateDict(Long id, @Valid UpdateDictCommand command) {
Assert.isTrue(Objects.equals(id, command.getId()), "id 不匹配");
Dict dictToUpdate = dictRepository.find(command.getId()).orElseThrow(DataNotExistException::new);
Dict dictToUpdate = dictRepository.find(command.getId());
dictToUpdate.updateDict(command.getDictType(), command.getDictLabel(), command.getKeyLabelMap());
dictRepository.save(dictToUpdate);
}
@Transactional(propagation = Propagation.SUPPORTS)
public Dict findDictDetails(Long dictId) {
return dictRepository.find(dictId).orElseThrow(DataNotExistException::new);
return dictRepository.find(dictId);
}
@Transactional(propagation = Propagation.SUPPORTS)

View File

@@ -13,12 +13,8 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
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.query.MenuQueries;
import xyz.zhouxy.plusone.system.application.query.params.MenuQueryParams;
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;
@@ -26,6 +22,7 @@ import xyz.zhouxy.plusone.system.domain.model.menu.Menu;
import xyz.zhouxy.plusone.system.domain.model.menu.MenuConstructor;
import xyz.zhouxy.plusone.system.domain.model.menu.MenuRepository;
import xyz.zhouxy.plusone.system.domain.service.MenuService;
import xyz.zhouxy.plusone.util.AssertResult;
/**
* 菜单管理
@@ -38,12 +35,10 @@ public class MenuManagementService {
private final MenuService menuService;
private final MenuRepository menuRepository;
private final MenuQueries menuQueries;
MenuManagementService(MenuService menuService, MenuRepository menuRepository, MenuQueries menuQueries) {
this.menuService = menuService;
MenuManagementService(MenuService roleRepository, MenuRepository menuRepository) {
this.menuService = roleRepository;
this.menuRepository = menuRepository;
this.menuQueries = menuQueries;
}
// ==================== create ====================
@@ -71,7 +66,7 @@ public class MenuManagementService {
command.getIcon(),
command.getHidden(),
command.getOrderNumber(),
EntityStatus.of(command.getStatus()),
command.getStatus(),
command.getRemarks());
}
@@ -84,7 +79,7 @@ public class MenuManagementService {
command.getIcon(),
command.getHidden(),
command.getOrderNumber(),
EntityStatus.of(command.getStatus()),
command.getStatus(),
command.getComponent(),
command.getResource(),
command.getCache(),
@@ -93,16 +88,15 @@ public class MenuManagementService {
// ==================== delete ====================
public void deleteMenu(Long id) {
Menu menuToDelete = menuRepository.find(id)
.orElseThrow(DataNotExistException::new);
Menu menuToDelete = menuRepository.find(id);
AssertResult.nonNull(menuToDelete);
menuRepository.delete(menuToDelete);
}
// ==================== update ====================
public void updateMenu(Long id, @Valid UpdateMenuCommand command) {
Assert.isTrue(Objects.equals(id, command.getId()), "id 不匹配");
Menu menuToUpdate = menuRepository.find(command.getId())
.orElseThrow(DataNotExistException::new);
Menu menuToUpdate = menuRepository.find(command.getId());
menuToUpdate.updateMenuInfo(
command.getMenuType(),
command.getParentId(),
@@ -112,7 +106,7 @@ public class MenuManagementService {
command.getIcon(),
command.getHidden(),
command.getOrderNumber(),
EntityStatus.of(command.getStatus()),
command.getStatus(),
command.getComponent(),
command.getResource(),
command.getCache(),
@@ -124,12 +118,7 @@ public class MenuManagementService {
@Transactional(propagation = Propagation.SUPPORTS)
public MenuViewObject findById(Long id) {
var menu = menuRepository.find(id);
return MenuViewObject.of(menu.orElseThrow(DataNotExistException::new));
}
@Transactional(propagation = Propagation.SUPPORTS)
public List<MenuViewObject> queryMenuTree(MenuQueryParams queryParams) {
return menuQueries.queryMenuTree(queryParams);
return MenuViewObject.of(menu);
}
@Transactional(propagation = Propagation.SUPPORTS)

View File

@@ -5,7 +5,6 @@ import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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;
@@ -18,6 +17,7 @@ import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Password;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.validator.InvalidInputException;
/**
* 注册账号服务
@@ -69,7 +69,7 @@ public class RegisterAccountService {
throw AccountRegisterException.emailAlreadyExists(mobilePhone.value());
}
} else {
throw new UnsupportedPrincipalTypeException();
throw InvalidInputException.unsupportedPrincipalTypeException();
}
verifyService.checkCode(emailOrMobilePhone, command.getCode());
@@ -78,12 +78,10 @@ public class RegisterAccountService {
Username.of(username),
email,
mobilePhone,
Password.newPassword(command.getPassword(), command.getReenteredPassword()),
Password.newPassword(command.getPassword(), command.getPasswordConfirmation()),
AccountStatus.AVAILABLE,
Set.of(DEFAULT_ROLE_ID),
AccountInfo.builder()
.nickname(command.getNickname())
.build());
AccountInfo.of(command.getNickname(), command.getAvatar(), command.getSex()));
accountRepository.save(accountToSave);
}
@@ -94,7 +92,7 @@ public class RegisterAccountService {
} else if (principalType == PrincipalType.MOBILE_PHONE) {
verifyService.sendCodeToMobilePhone(MobilePhone.of(principal));
} else {
throw new UnsupportedPrincipalTypeException();
throw InvalidInputException.unsupportedPrincipalTypeException();
}
}
}

View File

@@ -10,8 +10,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.exception.DataNotExistException;
import xyz.zhouxy.plusone.system.application.query.RoleQueries;
import xyz.zhouxy.plusone.system.application.query.params.RoleQueryParams;
import xyz.zhouxy.plusone.system.application.query.result.RoleOverview;
@@ -55,7 +53,7 @@ public class RoleManagementService {
Role roleToCreate = Role.newInstance(
command.getName(),
command.getIdentifier(),
EntityStatus.of(command.getStatus()),
command.getStatus(),
command.getRemarks(),
menuRefs,
permissionRefs);
@@ -64,11 +62,11 @@ public class RoleManagementService {
public void updateRole(@Valid UpdateRoleCommand command) {
Long roleId = command.getId();
Role roleToUpdate = _roleRepository.find(roleId).orElseThrow(DataNotExistException::new);
Role roleToUpdate = _roleRepository.find(roleId);
roleToUpdate.update(
command.getName(),
command.getIdentifier(),
EntityStatus.of(command.getStatus()),
command.getStatus(),
command.getRemarks(),
Set.copyOf(_menuRepository.findByIdIn(command.getMenus())),
Set.copyOf(_menuRepository.findPermissionsByIdIn(command.getPermissions())));
@@ -76,7 +74,7 @@ public class RoleManagementService {
}
public void delete(Long id) {
Role role = _roleRepository.find(id).orElseThrow(DataNotExistException::new);
Role role = _roleRepository.find(id);
_roleRepository.delete(role);
}
@@ -87,7 +85,7 @@ public class RoleManagementService {
@Transactional(propagation = Propagation.SUPPORTS)
public Role findById(Long id) {
return _roleRepository.find(id).orElseThrow(DataNotExistException::new);
return _roleRepository.find(id);
}
@Transactional(propagation = Propagation.SUPPORTS)

View File

@@ -10,8 +10,10 @@ import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.URL;
import lombok.Data;
import xyz.zhouxy.plusone.commons.constant.RegexConsts;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.domain.model.account.AccountStatus;
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
/**
* 创建账号命令
@@ -39,7 +41,7 @@ public class CreateAccountCommand implements ICommand {
String passwordConfirmation;
@NotNull
Integer status;
AccountStatus status;
Set<Long> roleRefs;
@@ -50,5 +52,5 @@ public class CreateAccountCommand implements ICommand {
@URL
String avatar;
Integer sex;
Sex sex;
}

View File

@@ -5,6 +5,7 @@ import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
@@ -41,7 +42,7 @@ public class CreateMenuCommand implements ICommand {
private Integer orderNumber;
@NotNull
private Integer status;
private EntityStatus status;
private String component;

View File

@@ -6,6 +6,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.ICommand;
/**
@@ -22,7 +23,7 @@ public class CreateRoleCommand implements ICommand {
String identifier;
@NotNull
Integer status;
EntityStatus status;
String remarks;
Set<Long> menus;

View File

@@ -3,9 +3,12 @@ package xyz.zhouxy.plusone.system.application.service.command;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.URL;
import lombok.Data;
import xyz.zhouxy.plusone.commons.constant.RegexConsts;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
/**
* 注册账号命令
@@ -29,8 +32,14 @@ public class RegisterAccountCommand implements ICommand {
@NotBlank
@Pattern(regexp = RegexConsts.PASSWORD)
String password;
String reenteredPassword;
String passwordConfirmation;
@Pattern(regexp = RegexConsts.NICKNAME)
String nickname;
@NotBlank
@URL
String avatar;
Sex sex;
}

View File

@@ -6,8 +6,9 @@ import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.URL;
import lombok.Data;
import xyz.zhouxy.plusone.commons.constant.RegexConsts;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.domain.model.account.Sex;
/**
* 更新账号信息命令
@@ -26,5 +27,5 @@ public class UpdateAccountCommand implements ICommand {
@URL
String avatar;
Integer sex;
Sex sex;
}

View File

@@ -5,6 +5,7 @@ import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
@@ -45,7 +46,7 @@ public class UpdateMenuCommand implements ICommand {
private Integer orderNumber;
@NotNull
private Integer status;
private EntityStatus status;
private String component;

View File

@@ -6,6 +6,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import xyz.zhouxy.plusone.constant.EntityStatus;
import xyz.zhouxy.plusone.domain.ICommand;
/**
@@ -26,7 +27,7 @@ public class UpdateRoleCommand implements ICommand {
String identifier;
@NotNull
Integer status;
EntityStatus status;
@NotBlank
String remarks;

View File

@@ -1,11 +1,13 @@
package xyz.zhouxy.plusone.system.application.service.command.validator;
import java.util.List;
import java.util.regex.Pattern;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.system.application.service.command.LoginByOtpCommand;
import xyz.zhouxy.plusone.util.RegexUtil;
import xyz.zhouxy.plusone.validator.BaseValidator;
import xyz.zhouxy.plusone.validator.DtoValidator;
@@ -13,13 +15,15 @@ import xyz.zhouxy.plusone.validator.DtoValidator;
@DtoValidator(LoginByOtpCommand.class)
public class LoginByOtpCommandValidator extends BaseValidator<LoginByOtpCommand> {
public LoginByOtpCommandValidator() {
ruleForString(LoginByOtpCommand::getPrincipal)
.notNull("输入邮箱地址或手机号")
.notEmpty("输入邮箱地址或手机号")
.matchesOr(List.of(PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE), "输入用户名、邮箱地址或手机号");
ruleForString(LoginByOtpCommand::getOtp)
.notNull("验证码不能为空")
.notEmpty("验证码不能为空")
.matches(PatternConsts.CAPTCHA, "验证码格式不正确");
withRule(loginCommand -> {
String principal = loginCommand.getPrincipal();
return StringUtils.hasText(principal)
&&
RegexUtil.matchesOr(principal, RegexConsts.EMAIL, RegexConsts.MOBILE_PHONE);
}, "输入邮箱地址或手机号");
withRule(loginCommand -> {
String otp = loginCommand.getOtp();
return StringUtils.hasText(otp) && Pattern.matches(RegexConsts.CAPTCHA, otp);
}, "验证码不符合要求");
}
}

View File

@@ -1,10 +1,13 @@
package xyz.zhouxy.plusone.system.application.service.command.validator;
import java.util.List;
import org.springframework.stereotype.Component;
import java.util.regex.Pattern;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import xyz.zhouxy.plusone.constant.RegexConsts;
import xyz.zhouxy.plusone.system.application.service.command.LoginByPasswordCommand;
import xyz.zhouxy.plusone.util.RegexUtil;
import xyz.zhouxy.plusone.validator.BaseValidator;
import xyz.zhouxy.plusone.validator.DtoValidator;
@@ -12,14 +15,17 @@ import xyz.zhouxy.plusone.validator.DtoValidator;
@DtoValidator(LoginByPasswordCommand.class)
public class LoginByPasswordCommandValidator extends BaseValidator<LoginByPasswordCommand> {
public LoginByPasswordCommandValidator() {
ruleForString(LoginByPasswordCommand::getPrincipal)
.notNull("输入用户名、邮箱地址或手机号")
.notEmpty("输入用户名、邮箱地址或手机号")
.matchesOr(List.of(PatternConsts.USERNAME, PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE),
"输入用户名、邮箱地址或手机号");
ruleForString(LoginByPasswordCommand::getPassword)
.notNull("密码不能为空")
.notEmpty("密码不能为空")
.matches(PatternConsts.PASSWORD, "密码格式不正确");
withRule(loginCommand -> {
String principal = loginCommand.getPrincipal();
return StringUtils.hasText(principal)
&&
RegexUtil.matchesOr(principal, RegexConsts.USERNAME, RegexConsts.EMAIL, RegexConsts.MOBILE_PHONE, principal);
}, "输入用户名、邮箱地址或手机号");
withRule(loginCommand -> {
String password = loginCommand.getPassword();
return StringUtils.hasText(password)
&&
Pattern.matches(RegexConsts.PASSWORD, password);
}, "密码格式不正确");
}
}

View File

@@ -14,6 +14,10 @@
<groupId>xyz.zhouxy</groupId>
<artifactId>plusone-basic-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,14 +1,11 @@
package xyz.zhouxy.plusone.system.util;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.annotation.Nonnull;
import xyz.zhouxy.plusone.exception.SysException;
import xyz.zhouxy.plusone.util.RandomUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.PlusoneException;
/**
* 密码工具类
@@ -16,8 +13,7 @@ import xyz.zhouxy.plusone.util.RandomUtil;
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class PasswordUtil {
private static final char[] SALT_BASE_CHAR_ARRAY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]|\\:;\"',.<>?/"
.toCharArray();
private static final String SALT_BASE_STRING = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]|\\:;\"',.<>?/";
/**
* 将密码和随机盐混合,并进行哈希加密。
@@ -32,12 +28,12 @@ public final class PasswordUtil {
int i = length > 0 ? length / 2 : 0;
var passwordWithSalt = salt.substring(0, i)
+ password
+ salt.substring(i);
try {
return sha512Hex(passwordWithSalt);
} catch (NoSuchAlgorithmException e) {
throw new SysException(e);
+ salt.substring(1);
String sha512Hex = DigestUtil.sha512Hex(passwordWithSalt);
if (sha512Hex == null) {
throw new PlusoneException(ErrorCodeConsts.DEFAULT_ERROR_CODE, "未知错误:哈希加密失败!");
}
return sha512Hex;
}
/**
@@ -46,20 +42,11 @@ public final class PasswordUtil {
* @return 生成的随机盐
*/
public static String generateRandomSalt() {
return RandomUtil.randomStr(SALT_BASE_CHAR_ARRAY, 24);
return RandomUtil.randomString(SALT_BASE_STRING, 24);
}
private PasswordUtil() {
// 不允许实例化
throw new IllegalStateException("Utility class");
}
@Nonnull
@SuppressWarnings("null")
private static String sha512Hex(String data) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
messageDigest.update(data.getBytes(StandardCharsets.UTF_8));
byte[] result = messageDigest.digest();
return new BigInteger(1, result).toString(16);
}
}

View File

@@ -27,7 +27,7 @@ public class AccountCreated extends DomainEvent {
public AccountCreated(Account account) {
this.username = account.getUsername();
this.email = account.getEmail().orElse(null);
this.mobilePhone = account.getMobilePhone().orElse(null);
this.email = account.getEmail();
this.mobilePhone = account.getMobilePhone();
}
}

View File

@@ -27,7 +27,7 @@ public class AccountLocked extends DomainEvent {
public AccountLocked(Account account) {
this.username = account.getUsername();
this.email = account.getEmail().orElse(null);
this.mobilePhone = account.getMobilePhone().orElse(null);
this.email = account.getEmail();
this.mobilePhone = account.getMobilePhone();
}
}

View File

@@ -27,8 +27,8 @@ public class AccountPasswordChanged extends DomainEvent {
public AccountPasswordChanged(Account account) {
this.username = account.getUsername();
this.email = account.getEmail().orElse(null);
this.mobilePhone = account.getMobilePhone().orElse(null);
this.email = account.getEmail();
this.mobilePhone = account.getMobilePhone();
}
}

View File

@@ -25,6 +25,6 @@ public class EmailChanged extends DomainEvent {
public EmailChanged(Account account) {
this.username = account.getUsername();
this.email = account.getEmail().orElse(null);
this.email = account.getEmail();
}
}

View File

@@ -25,6 +25,6 @@ public class MobilePhoneChanged extends DomainEvent {
public MobilePhoneChanged(Account account) {
this.username = account.getUsername();
this.mobilePhone = account.getMobilePhone().orElse(null);
this.mobilePhone = account.getMobilePhone();
}
}

View File

@@ -27,7 +27,7 @@ public class UsernameChanged extends DomainEvent {
public UsernameChanged(Account account) {
this.username = account.getUsername();
this.email = account.getEmail().orElse(null);
this.mobilePhone = account.getMobilePhone().orElse(null);
this.email = account.getEmail();
this.mobilePhone = account.getMobilePhone();
}
}

View File

@@ -5,6 +5,8 @@ import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IWithVersion;
@@ -28,17 +30,17 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
// ===================== 字段 ====================
private Long id;
private Username username;
private Email email;
private MobilePhone mobilePhone;
private @Getter Username username;
private @Getter Email email;
private @Getter MobilePhone mobilePhone;
private Password password;
private AccountStatus status;
private AccountInfo accountInfo;
private @Getter AccountStatus status;
private @Getter AccountInfo accountInfo;
private Set<Long> roleRefs = new HashSet<>();
private Long createdBy;
private Long updatedBy;
private long version;
private @Getter Long createdBy;
private @Getter @Setter Long updatedBy;
private @Getter long version;
public void setUsername(Username username) {
this.username = username;
@@ -93,19 +95,11 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
}
public void setAccountInfo(Nickname nickname, URL avatar, Sex sex) {
this.accountInfo = AccountInfo.builder()
.nickname(nickname)
.avatar(avatar)
.sex(sex)
.build();
this.accountInfo = AccountInfo.of(nickname, avatar, sex);
}
public void setAccountInfo(String nickname, String avatar, Sex sex) {
this.accountInfo = AccountInfo.builder()
.nickname(nickname)
.avatar(avatar)
.sex(sex)
.build();
this.accountInfo = AccountInfo.of(nickname, avatar, sex);
}
/**
@@ -229,48 +223,11 @@ public class Account extends AggregateRoot<Long> implements IWithVersion {
return Optional.ofNullable(id);
}
public Username getUsername() {
return username;
}
public Optional<Email> getEmail() {
return Optional.ofNullable(email);
}
public Optional<MobilePhone> getMobilePhone() {
return Optional.ofNullable(mobilePhone);
public Set<Long> getRoleIds() {
return Set.copyOf(this.roleRefs);
}
Password getPassword() {
return password;
}
public AccountStatus getStatus() {
return status;
}
public AccountInfo getAccountInfo() {
return accountInfo;
}
public Set<Long> getRoleIds() {
return Set.copyOf(roleRefs);
}
public Optional<Long> getCreatedBy() {
return Optional.ofNullable(createdBy);
}
public Optional<Long> getUpdatedBy() {
return Optional.ofNullable(updatedBy);
}
public void setUpdatedBy(long updatedBy) {
this.updatedBy = updatedBy;
}
@Override
public long getVersion() {
return this.version;
}
}

View File

@@ -3,11 +3,8 @@ package xyz.zhouxy.plusone.system.domain.model.account;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.ToString;
import xyz.zhouxy.plusone.domain.IValueObject;
@@ -16,77 +13,31 @@ import xyz.zhouxy.plusone.domain.IValueObject;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Getter
@ToString
public class AccountInfo implements IValueObject {
@Nullable
private final Nickname nickname;
@Nullable
private final URL avatar;
@Nonnull
private final Sex sex;
private AccountInfo(@Nullable Nickname nickname, @Nullable URL avatar, @Nullable Sex sex) {
private AccountInfo(Nickname nickname, URL avatar, Sex sex) {
this.nickname = nickname;
this.avatar = avatar;
this.sex = Objects.nonNull(sex) ? sex : Sex.UNSET;
}
public Optional<Nickname> getNickname() {
return Optional.ofNullable(nickname);
public static AccountInfo of(Nickname nickname, URL avatar, Sex sex) {
return new AccountInfo(nickname, avatar, sex);
}
public Optional<URL> getAvatar() {
return Optional.ofNullable(avatar);
}
@Nonnull
public Sex getSex() {
return this.sex;
}
// Builder
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Nickname nickname;
private URL avatar;
private Sex sex;
public Builder nickname(@Nullable Nickname nickname) {
this.nickname = nickname;
return this;
}
public Builder nickname(@Nullable String nickname) {
return nickname(Nickname.ofNullable(nickname));
}
public Builder avatar(@Nullable URL avatar) {
this.avatar = avatar;
return this;
}
public Builder avatar(@Nullable String avatar) {
URL avatarURL;
try {
avatarURL = Objects.nonNull(avatar) ? new URL(avatar) : null;
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
return avatar(avatarURL);
}
public Builder sex(@Nullable Sex sex) {
this.sex = sex;
return this;
}
public AccountInfo build() {
return new AccountInfo(this.nickname, this.avatar, this.sex);
public static AccountInfo of(String nickname, String avatar, Sex sex) {
URL avatarURL;
try {
avatarURL = Objects.nonNull(avatar) ? new URL(avatar) : null;
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
return new AccountInfo(Nickname.ofNullable(nickname), avatarURL, sex);
}
}

View File

@@ -1,7 +1,6 @@
package xyz.zhouxy.plusone.system.domain.model.account;
import java.util.Collection;
import java.util.Optional;
import xyz.zhouxy.plusone.domain.IRepository;
@@ -15,11 +14,11 @@ public interface AccountRepository extends IRepository<Account, Long> {
Collection<Account> findByRoleId(Long roleId);
Optional<Account> findByEmail(Email email);
Account findByEmail(Email email);
Optional<Account> findByMobilePhone(MobilePhone mobilePhone);
Account findByMobilePhone(MobilePhone mobilePhone);
Optional<Account> findByUsername(Username username);
Account findByUsername(Username username);
boolean existsUsername(Username username);

Some files were not shown because too many files have changed in this diff Show More