ZhouXY108 2023-07-07 15:34:45 +08:00
commit 68f6ce5b8f
22 changed files with 186 additions and 173 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,9 @@ 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}
@ -17,17 +20,23 @@ import org.springframework.lang.Nullable;
*/
public class BeanPropertyParamSource extends BeanPropertySqlParameterSource {
public BeanPropertyParamSource(Object object) {
super(object);
}
public BeanPropertyParamSource(Object object) {
super(object);
}
@Override
@Nullable
public Object getValue(String paramName) throws IllegalArgumentException {
Object value = super.getValue(paramName);
if (value instanceof Enum) {
return ((Enum<?>) value).ordinal();
}
return value;
}
@Override
@Nullable
public Object getValue(String paramName) throws IllegalArgumentException {
Object value = super.getValue(paramName);
if (value instanceof Enum<?> e) {
if (value instanceof IWithCode<?> c) {
return c.getCode();
}
if (value instanceof IWithIntCode c) {
return c.getCode();
}
return e.ordinal();
}
return value;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ 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;
@ -129,12 +130,28 @@ public class Menu extends AggregateRoot<Long> implements IWithOrderNumber, IWith
this.version = version;
}
public enum MenuType {
MENU_LIST, MENU_ITEM;
public enum MenuType implements IWithIntCode {
MENU_LIST(0), MENU_ITEM(1);
private final int code;
private MenuType(int code) {
this.code = code;
}
@JsonValue
public int value() {
return ordinal();
@Override
public int getCode() {
return code;
}
public static MenuType valueOf(int code) {
for (MenuType value : values()) {
if (value.code == code) {
return value;
}
}
throw new EnumConstantNotPresentException(MenuType.class, Integer.toString(code));
}
}
}

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>2.7.11</spring-boot.version>
<spring-boot.version>2.7.13</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>