11 Commits

75 changed files with 374 additions and 672 deletions

View File

@@ -19,10 +19,3 @@
目前项目还没完成,开发中……
相关的文档和介绍完善中……
## 编码规约
### 关于 null
1. 方法默认参数不为 `null`**可以**在 Javadoc 中对参数进行说明,如“...(must not be {@code null}[ or empty string])”。方法内部**必须**做参数校验,如使用 Assert 等工具类。
2. 如果参数允许为空,**必须**为参数添加 `@Nullable` 注解,并在 Javadoc 中对参数进行说明,如“...(may be {@code null})”。
3. 除极特殊的情况,方法默认**不返回 `null`**,如可能返回一个对象表示值的缺失,则**必须**使用 `Optional`**绝不在返回类型为 Optional 的方法中返回 `null`**。
4. 在极其特殊的情况下,方法需要返回 `null`**必须**给方法加上 `@Nullable` 注解,并在 Javadoc 中详细说明。

View File

@@ -1,13 +1,10 @@
package xyz.zhouxy.plusone.exception.handler;
import javax.annotation.Nonnull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import xyz.zhouxy.plusone.commons.exception.handler.AllExceptionHandler;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
/**
* AllExceptionHandlerConfig
@@ -17,7 +14,7 @@ import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler.Excepti
public class AllExceptionHandlerConfig {
@Bean
AllExceptionHandler getAllExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
AllExceptionHandler getAllExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
return new AllExceptionHandler(exceptionInfoHolder);
}
}

View File

@@ -1,7 +1,5 @@
package xyz.zhouxy.plusone.exception.handler;
import javax.annotation.Nonnull;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@@ -13,7 +11,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
/**
@@ -43,7 +40,7 @@ import xyz.zhouxy.plusone.commons.util.RestfulResult;
@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Slf4j
public class DefaultExceptionHandler extends BaseExceptionHandler {
public DefaultExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
public DefaultExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
set(IllegalArgumentException.class, 4010000, "格式错误", HttpStatus.FORBIDDEN);
set(DataAccessException.class, 6030000, "数据库错误", HttpStatus.INTERNAL_SERVER_ERROR, true);

View File

@@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.exception.SysException;
@@ -16,7 +15,7 @@ import xyz.zhouxy.plusone.exception.SysException;
@Slf4j
public class SysExceptionHandler extends BaseExceptionHandler {
public SysExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
public SysExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
}

View File

@@ -49,7 +49,7 @@
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-exception-handler</artifactId>
<version>0.0.9-SNAPSHOT</version>
<version>0.0.8-SNAPSHOT</version>
</dependency>
</dependencies>

View File

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

View File

@@ -1,5 +1,9 @@
package xyz.zhouxy.plusone.util;
import java.util.Objects;
import java.util.regex.Pattern;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -23,4 +27,17 @@ public class PlusoneStrUtil {
return StringUtils.hasLength(value) ? value : EMPTY_STR;
}
public static String checkString(String value, String regex, String message) {
Assert.notNull(value, message);
Assert.isTrue(Pattern.matches(regex, value), message);
return value;
}
public static String checkStringNullable(String value, String regex, String message) {
return Objects.nonNull(value) ? checkString(value, regex, message) : null;
}
public static String checkStringOrDefault(String value, String regex, String message) {
return StringUtils.hasText(value) ? checkString(value, regex, message) : "";
}
}

View File

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

View File

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

View File

@@ -17,8 +17,8 @@ import lombok.Getter;
*/
@Getter
public abstract class DomainEvent {
private final String identifier;
private final long happenedAt;
private String identifier;
private long happenedAt;
protected DomainEvent() {
this.identifier = UUID.randomUUID().toString();

View File

@@ -5,8 +5,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import xyz.zhouxy.plusone.commons.annotation.Overridable;
/**
* 实体
*
@@ -28,29 +26,7 @@ public abstract class Entity<ID extends Serializable> {
public abstract Optional<ID> getId();
protected final void addDomainEvent(DomainEvent domainEvent) {
protected void addDomainEvent(DomainEvent domainEvent) {
domainEvents.add(domainEvent);
}
public final List<DomainEvent> getDomainEvents() {
return List.copyOf(domainEvents);
}
@Override
@Overridable
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
@Overridable
public int hashCode() {
return super.hashCode();
}
@Override
@Overridable
public String toString() {
return super.toString();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,41 @@
package xyz.zhouxy.plusone.domain;
import java.util.Optional;
import java.util.regex.Pattern;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* 带校验的字符串值对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public abstract class ValidatableStringRecord implements IValueObject {
protected String value;
protected final Pattern format;
protected ValidatableStringRecord(Pattern format) {
this.format = format;
}
@JsonIgnore
protected boolean isValid() {
return format.matcher(value).matches();
}
@JsonValue
public String value() {
return value;
}
@Override
public String toString() {
return value;
}
public static String getValueOrNull(Optional<? extends ValidatableStringRecord> s) {
return s.isPresent() ? s.get().value() : null;
}
}

View File

@@ -5,7 +5,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
@Configuration
public class PlusoneExceptionHandlerConfig {

View File

@@ -3,9 +3,6 @@ package xyz.zhouxy.plusone.jdbc;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.lang.Nullable;
import xyz.zhouxy.plusone.commons.exception.IWithCode;
import xyz.zhouxy.plusone.commons.exception.IWithIntCode;
/**
* 扩展了 {@link BeanPropertySqlParameterSource},在将 POJO 转换为
* {@link org.springframework.jdbc.core.namedparam.SqlParameterSource} 时,
@@ -28,14 +25,8 @@ public class BeanPropertyParamSource extends BeanPropertySqlParameterSource {
@Nullable
public Object getValue(String paramName) throws IllegalArgumentException {
Object value = super.getValue(paramName);
if (value instanceof Enum<?> e) {
if (value instanceof IWithCode<?> c) {
return c.getCode();
}
if (value instanceof IWithIntCode c) {
return c.getCode();
}
return e.ordinal();
if (value instanceof Enum) {
return ((Enum<?>) value).ordinal();
}
return value;
}

View File

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

View File

@@ -7,7 +7,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import xyz.zhouxy.plusone.spring.SpringContextHolder;
/**
* 通过静态方法获取 Spring 容器中的 {@link JdbcTemplate} 和
* 全局单例,由 Spring 装配好的对象。
* 可通过静态方法获取 Spring 容器中的 {@link JdbcTemplate} 和
* {@link NamedParameterJdbcTemplate} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>

View File

@@ -8,9 +8,6 @@ import javax.annotation.Nonnull;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import com.google.common.base.Preconditions;
import xyz.zhouxy.plusone.commons.annotation.Overridable;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IRepository;
@@ -22,33 +19,35 @@ public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID exte
super(namedParameterJdbcTemplate);
}
@Overridable
protected abstract void doDelete(@Nonnull T entity);
@Overridable
protected abstract Optional<T> doFindById(@Nonnull ID id);
@Overridable
protected abstract T doInsert(@Nonnull T entity);
@Overridable
protected abstract T doUpdate(@Nonnull T entity);
@Override
public final void delete(T entity) {
Preconditions.checkArgument(entity != null, "Cannot delete null.");
if (entity == null) {
throw new IllegalArgumentException("Cannot delete null.");
}
doDelete(entity);
}
@Override
public final Optional<T> find(ID id) {
Preconditions.checkArgument(id != null, "Id cannot be null.");
if (id == null) {
throw new IllegalArgumentException("Id cannot be null.");
}
return doFindById(id);
}
@Override
public final T save(T entity) {
Preconditions.checkArgument(entity != null, "Cannot save null.");
if (entity == null) {
throw new IllegalArgumentException("Cannot save null.");
}
return entity.getId().isPresent() ? doUpdate(entity) : doInsert(entity);
}

View File

@@ -16,7 +16,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.CollectionUtils;
import xyz.zhouxy.plusone.commons.util.Numbers;
import xyz.zhouxy.plusone.commons.util.NumberUtil;
import xyz.zhouxy.plusone.exception.DataOperationResultException;
public abstract class PlusoneJdbcDaoSupport {
@@ -88,19 +88,19 @@ public abstract class PlusoneJdbcDaoSupport {
protected final long batchUpdate(String sql, SqlParameterSource[] batchArgs) {
int[] i = this.jdbc.batchUpdate(sql, batchArgs);
return Numbers.sum(i);
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 Numbers.sum(i);
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 Numbers.sum(i);
return NumberUtil.sum(i);
}
protected static final <T> void assertResultEquals(T result, T expectedValue) {

View File

@@ -8,7 +8,7 @@ import java.sql.SQLException;
*
* <p>
* 通过在 {@link #map(ResultSet)} 中配置 {@link ResultSet} 到对象的映射,
* 可将 {@link #rowMapper(ResultSet, int)} 的方法用,
* 可将 {@link #rowMapper(ResultSet, int)} 的方法用,
* 直接当成 {@link org.springframework.jdbc.core.RowMapper} 对象传给
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* 的查询方法,

View File

@@ -1,17 +1,8 @@
package xyz.zhouxy.plusone.oss;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
@@ -23,11 +14,7 @@ import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.io.Files;
import lombok.Getter;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.exception.SysException;
public class FastDFSUtil {
@@ -57,7 +44,7 @@ public class FastDFSUtil {
* @throws FastDFSException
*/
public String[] upload(FastDFSFile file) throws FastDFSException {
logger.info("File Name: {}, File Length: {}", file.getFileName(), file.getContent().length);
logger.info("File Name: {}, File Length: {}", file.getName(), file.getContent().length);
NameValuePair[] metaList = new NameValuePair[1];
metaList[0] = new NameValuePair("author", file.getAuthor());
@@ -76,7 +63,7 @@ public class FastDFSUtil {
uploadResults[0], uploadResults[1], System.currentTimeMillis() - startTime);
} catch (IOException e) {
throw new FastDFSException("IO Exception when uploadind the file:" + file.getFileName(), e);
throw new FastDFSException("IO Exception when uploadind the file:" + file.getName(), e);
} catch (MyException e) {
throw new FastDFSException(e);
}
@@ -122,72 +109,18 @@ public class FastDFSUtil {
}
}
@Getter
public static final class FastDFSFile {
@Getter
private final String fileName;
private final byte[] content;
@Getter
private final String ext;
@Getter
private final String md5;
@Getter
private final String author;
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
private FastDFSFile(@Nonnull File file, @Nullable String author) throws IOException {
this.fileName = file.getName();
this.content = Files.toByteArray(file);
this.ext = Files.getFileExtension(fileName);
this.md5 = md5Hex(content);
this.author = author;
}
private FastDFSFile(@Nonnull String fileName, @Nonnull byte[] content, @Nullable String author) {
this.fileName = fileName;
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = Files.getFileExtension(fileName);
this.md5 = md5Hex(content);
this.author = author;
}
@Nonnull
@StaticFactoryMethod(FastDFSFile.class)
public static FastDFSFile of(@Nonnull File file) throws IOException {
return new FastDFSFile(file, null);
}
@Nonnull
@StaticFactoryMethod(FastDFSFile.class)
public static FastDFSFile of(@Nonnull File file, @Nullable String author) throws IOException {
return new FastDFSFile(file, author);
}
@Nonnull
@StaticFactoryMethod(FastDFSFile.class)
public static FastDFSFile of(@Nonnull String fileName, @Nonnull byte[] content) {
return new FastDFSFile(fileName, content, null);
}
@Nonnull
@StaticFactoryMethod(FastDFSFile.class)
public static FastDFSFile of(@Nonnull String fileName, @Nonnull byte[] content, @Nullable String author) {
return new FastDFSFile(fileName, content, author);
}
public byte[] getContent() {
return Arrays.copyOf(content, content.length);
}
@Nonnull
private static String md5Hex(byte[] data) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(data);
byte[] result = messageDigest.digest();
var sha512Hex = new BigInteger(1, result).toString(16);
return Objects.requireNonNull(sha512Hex);
} catch (NoSuchAlgorithmException e) {
throw new SysException(e);
}
this.ext = ext;
}
}
}

View File

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

View File

@@ -1,15 +1,13 @@
package xyz.zhouxy.plusone.validator;
import javax.annotation.Nonnull;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
@RestControllerAdvice
public class InvalidInputExceptionHandler extends BaseExceptionHandler {
public InvalidInputExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
public InvalidInputExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
set(InvalidInputException.class, InvalidInputException.ERROR_CODE, "无效的用户输入");
}

View File

@@ -3,10 +3,14 @@ package xyz.zhouxy.plusone;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;
@SpringBootApplication
@Slf4j
public class PlusoneApplication {
public static void main(String[] args) {
log.debug("Plusone started!");
SpringApplication.run(PlusoneApplication.class, args);
}

View File

@@ -1,11 +1,12 @@
package xyz.zhouxy.plusone;
import java.io.File;
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;
@@ -23,7 +24,10 @@ class FastDFSTests {
@Test
void testOSS() throws FileNotFoundException, IOException, FastDFSException {
String[] upload = fastDFSUtil.upload(FastDFSFile.of(new File("D:\\ZhouXY\\Desktop\\666.png")));
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

@@ -1,138 +0,0 @@
package xyz.zhouxy.plusone.system.application.common.model;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.OptionalDouble;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.exception.BizException;
public class AuthenticationInfo<T> {
/** token 值 */
private final String token;
/** 此 token 对应的LoginId未登录时不会构建此类的实例 */
private final T loginId;
/** 账号类型 */
private final String loginType;
/** 登录设备类型 */
private final String deviceType;
/** 自定义数据 */
@Nonnull
private final Map<String, Object> metadata;
private AuthenticationInfo(String token, T loginId, String loginType, String deviceType,
@Nonnull Map<String, Object> metadata) {
this.token = token;
this.loginId = loginId;
this.loginType = loginType;
this.deviceType = deviceType;
this.metadata = metadata;
}
public String getToken() {
return token;
}
public T getLoginId() {
return loginId;
}
public String getLoginType() {
return loginType;
}
public String getDeviceType() {
return deviceType;
}
private Object getObjFromMetadata(String key) {
if (this.metadata.containsKey(key)) {
return this.metadata.get(key);
}
throw new BizException(String.format("不存在 key 为 \"%s\"的元数据。", key));
}
public OptionalDouble getMetadataAsDouble(String key) {
Object valObj = getObjFromMetadata(key);
if (valObj instanceof Double val) {
return OptionalDouble.of(val.doubleValue());
}
return OptionalDouble.empty();
}
public OptionalLong getMetadataAsLong(String key) {
Object valObj = getObjFromMetadata(key);
if (valObj instanceof Long val) {
return OptionalLong.of(val.longValue());
}
return OptionalLong.empty();
}
public OptionalInt getMetadataAsInt(String key) {
Object valObj = getObjFromMetadata(key);
if (valObj instanceof Integer val) {
return OptionalInt.of(val.intValue());
}
return OptionalInt.empty();
}
public Optional<String> getMetadataAsString(String key) {
Object valObj = getObjFromMetadata(key);
if (valObj instanceof String val) {
return Optional.of(val);
}
return Optional.empty();
}
@SuppressWarnings("unchecked")
public <C> Optional<C> getMetadata(String key, Class<C> type) {
Object valObj = getObjFromMetadata(key);
if (valObj == null || !type.isAssignableFrom(valObj.getClass())) {
return Optional.empty();
}
return Optional.of((C) valObj);
}
// ==========================================
// builder
public static <T> Builder<T> builder(String token, T loginId, String loginType, String deviceType) {
return new Builder<>(token, loginId, loginType, deviceType);
}
public static final class Builder<T> {
private final String token;
private final T loginId;
private final String loginType;
private final String deviceType;
@Nonnull
private final Map<String, Object> meta = new ConcurrentHashMap<>();
private Builder(String token, T loginId, String loginType, String deviceType) {
this.token = token;
this.loginId = loginId;
this.loginType = loginType;
this.deviceType = deviceType;
}
public <C> Builder<T> putMetadata(String key, @Nullable C value) {
this.meta.put(key, value);
return this;
}
public AuthenticationInfo<T> build() {
return new AuthenticationInfo<>(token, loginId, loginType, deviceType, meta);
}
}
}

View File

@@ -1,21 +0,0 @@
package xyz.zhouxy.plusone.system.application.common.model;
public class PlusoneContext {
private static final ThreadLocal<AuthenticationInfo<Long>> context = new ThreadLocal<>();
public static void setContext(AuthenticationInfo<Long> value) {
context.set(value);
}
public static AuthenticationInfo<Long> getContext() {
return context.get();
}
public static void remove() {
context.remove();
}
private PlusoneContext() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
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;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
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;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
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;
@@ -15,6 +15,7 @@ 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;
@@ -66,7 +67,14 @@ public class MenuManagementController {
public RestfulResult findById(@PathVariable("id") Long id) {
adminAuthLogic.checkPermission("sys-menu-details");
var result = service.findById(id);
return RestfulResult.success("查询成功", result);
return success("查询成功", result);
}
@GetMapping
public RestfulResult queryMenuTree(MenuQueryParams queryParams) {
adminAuthLogic.checkPermission("sys-menu-query");
var result = service.queryMenuTree(queryParams);
return success("查询成功", result);
}
@GetMapping("queryByAccountId")

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
package xyz.zhouxy.plusone.system.application.controller;
import static xyz.zhouxy.plusone.commons.util.RestfulResult.success;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.web.controller;
package xyz.zhouxy.plusone.system.application.controller;
import javax.validation.Valid;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.common.exception;
package xyz.zhouxy.plusone.system.application.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.common.exception;
package xyz.zhouxy.plusone.system.application.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package xyz.zhouxy.plusone.system.application.common.exception;
package xyz.zhouxy.plusone.system.application.exception;
import xyz.zhouxy.plusone.validator.InvalidInputException;

View File

@@ -1,12 +1,14 @@
package xyz.zhouxy.plusone.system.application.common.exception.handler;
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.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
@RestControllerAdvice
public class AccountLoginExceptionHandler extends BaseExceptionHandler {
@@ -16,7 +18,7 @@ public class AccountLoginExceptionHandler extends BaseExceptionHandler {
}
@ExceptionHandler({ AccountLoginException.class })
public ResponseEntity<RestfulResult> handleException(Exception e) {
public ResponseEntity<RestfulResult> handleException(@Nonnull Exception e) {
return buildExceptionResponse(e);
}
}

View File

@@ -1,6 +1,4 @@
package xyz.zhouxy.plusone.system.application.common.exception.handler;
import javax.annotation.Nonnull;
package xyz.zhouxy.plusone.system.application.exception.handler;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -16,7 +14,7 @@ import cn.dev33.satoken.exception.NotSafeException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.commons.util.RestfulResult;
/**
@@ -29,7 +27,7 @@ import xyz.zhouxy.plusone.commons.util.RestfulResult;
public class SaTokenExceptionHandler extends BaseExceptionHandler {
public SaTokenExceptionHandler(@Nonnull ExceptionInfoHolder exceptionInfoHolder) {
public SaTokenExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(exceptionInfoHolder);
set(NotPermissionException.class, 4030103, "会话未能通过权限认证", HttpStatus.FORBIDDEN);
set(NotRoleException.class, 4030103, "会话未能通过角色认证", HttpStatus.FORBIDDEN);

View File

@@ -2,8 +2,6 @@ package xyz.zhouxy.plusone.system.application.query;
import java.util.List;
import javax.annotation.Nonnull;
import org.apache.ibatis.annotations.Mapper;
import xyz.zhouxy.plusone.commons.util.PageDTO;
@@ -25,7 +23,6 @@ public interface AccountQueries {
return PageDTO.of(content, total);
}
@Nonnull
List<AccountOverview> queryAccountOverview(AccountQueryParams queryParams);
long count(AccountQueryParams queryParams);

View File

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

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

View File

@@ -12,7 +12,7 @@ import xyz.zhouxy.plusone.commons.util.PagingAndSortingQueryParams;
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@ToString(callSuper = true)
@ToString
public class AccountQueryParams extends PagingAndSortingQueryParams {
public AccountQueryParams() {
@@ -29,6 +29,7 @@ public class AccountQueryParams extends PagingAndSortingQueryParams {
"update_time");
}
// TODO【添加】 注解参数校验
private @Getter @Setter Long id;
private @Getter @Setter String username;
private @Getter @Setter String email;

View File

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

@@ -25,60 +25,28 @@ import xyz.zhouxy.plusone.system.domain.model.menu.Menu.MenuType;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MenuViewObject implements IWithOrderNumber {
@Getter
@Setter
Integer type;
private @Getter @Setter Integer type;
@Getter
@Setter
String typeName;
private @Getter @Setter String typeName;
@Getter
@Setter
Long id;
@Getter
@Setter
Long parentId;
private @Getter @Setter Long id;
private @Getter @Setter Long parentId;
@Getter
@Setter
String name;
private @Getter @Setter String name;
// 若 type 为 MENU_ITEM 且 path 以 http:// 或 https:// 开头则被识别为外链
@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;
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;
// MENU_ITEM
@Getter
@Setter
String component;
@Getter
@Setter
Boolean cache;
@Getter
@Setter
String resource;
@Getter
@Setter
List<Action> actions;
private @Getter @Setter String component;
private @Getter @Setter Boolean cache;
private @Getter @Setter String resource;
private @Getter @Setter List<Action> actions;
// MENU_LIST
List<MenuViewObject> children;

View File

@@ -8,9 +8,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.dev33.satoken.stp.StpLogic;
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;

View File

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

View File

@@ -6,10 +6,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.hutool.core.lang.Assert;
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
import xyz.zhouxy.plusone.system.application.query.result.LoginInfoViewObject;
import xyz.zhouxy.plusone.system.application.service.command.LoginByOtpCommand;

View File

@@ -9,8 +9,8 @@ import org.springframework.util.Assert;
import cn.hutool.core.util.RandomUtil;
import xyz.zhouxy.plusone.mail.MailService;
import xyz.zhouxy.plusone.sms.SmsService;
import xyz.zhouxy.plusone.system.application.common.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.common.exception.AccountRegisterException;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;

View File

@@ -16,7 +16,9 @@ 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.common.exception.UnsupportedMenuTypeException;
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;
@@ -36,10 +38,12 @@ public class MenuManagementService {
private final MenuService menuService;
private final MenuRepository menuRepository;
private final MenuQueries menuQueries;
MenuManagementService(MenuService roleRepository, MenuRepository menuRepository) {
this.menuService = roleRepository;
MenuManagementService(MenuService menuService, MenuRepository menuRepository, MenuQueries menuQueries) {
this.menuService = menuService;
this.menuRepository = menuRepository;
this.menuQueries = menuQueries;
}
// ==================== create ====================
@@ -123,6 +127,11 @@ public class MenuManagementService {
return MenuViewObject.of(menu.orElseThrow(DataNotExistException::new));
}
@Transactional(propagation = Propagation.SUPPORTS)
public List<MenuViewObject> queryMenuTree(MenuQueryParams queryParams) {
return menuQueries.queryMenuTree(queryParams);
}
@Transactional(propagation = Propagation.SUPPORTS)
public List<MenuViewObject> queryByAccountId(Long accountId) {
var menus = menuService.queryAllMenuListByAccountId(accountId);

View File

@@ -5,10 +5,10 @@ import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.zhouxy.plusone.system.application.common.exception.AccountRegisterException;
import xyz.zhouxy.plusone.system.application.common.exception.UnsupportedPrincipalTypeException;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
import xyz.zhouxy.plusone.system.application.service.command.RegisterAccountCommand;
import xyz.zhouxy.plusone.system.domain.model.account.Account;
import xyz.zhouxy.plusone.system.domain.model.account.AccountInfo;

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
package xyz.zhouxy.plusone.system.util;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.util.Assert;
import com.google.common.hash.Hashing;
import javax.annotation.Nonnull;
import xyz.zhouxy.plusone.exception.SysException;
import xyz.zhouxy.plusone.util.RandomUtil;
/**
@@ -25,18 +26,18 @@ public final class PasswordUtil {
* @param salt 随机盐
* @return 哈希加密的结果
*/
public static String hashPassword(String password, String salt) {
Assert.notNull(password, "Password must not be null");
Assert.notNull(salt, "Salt must not be null");
return Hashing.sha512().newHasher()
.putInt(Arrays.hashCode(salt.toCharArray()))
.putString(password, StandardCharsets.UTF_8)
.putInt(password.length())
.putBoolean(password.length() % 2 == 0)
.putString(salt, StandardCharsets.UTF_8)
.putInt(Arrays.hashCode(password.toCharArray()))
.hash()
.toString();
@Nonnull
public static String hashPassword(@Nonnull String password, @Nonnull String salt) {
int length = salt.length();
int i = length > 0 ? length / 2 : 0;
var passwordWithSalt = salt.substring(0, i)
+ password
+ salt.substring(i);
try {
return sha512Hex(passwordWithSalt);
} catch (NoSuchAlgorithmException e) {
throw new SysException(e);
}
}
/**
@@ -52,4 +53,13 @@ public final class 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

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

View File

@@ -23,7 +23,7 @@ public final class AccountStatus extends Enumeration<AccountStatus> implements I
public static final AccountStatus AVAILABLE = new AccountStatus(0, "账号正常");
public static final AccountStatus LOCKED = new AccountStatus(1, "账号被锁定");
private static final ValueSet<AccountStatus> VALUE_SET = ValueSet.of(
private static final ValueSet<AccountStatus> VALUE_SET = new ValueSet<>(
AVAILABLE,
LOCKED);
@@ -31,6 +31,7 @@ public final class AccountStatus extends Enumeration<AccountStatus> implements I
return VALUE_SET.get(id);
}
@Nonnull
public static Collection<AccountStatus> constants() {
return VALUE_SET.getValues();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>2.7.13</spring-boot.version>
<spring-boot.version>2.7.10</spring-boot.version>
<sa-token.version>1.34.0</sa-token.version>
<hutool.version>5.8.16</hutool.version>
<mybatis-starter.version>3.0.1</mybatis-starter.version>