提交 00c11956 作者: 方治民

feat: 大量基础代码更新

1. enable 字段调整为 disable 更符合前端直接使用
2. 新增 Specifications 工具类实现,减少复杂查询或过滤删除数据的重复代码
3. 使用 OptionVo 替换 KeyValueVo 更具通用性
4. 分页及选项查询增加 deleted 过滤
5. 管理员用户默认允许查询所有菜单权限
6. 其他细节优化处理
上级 624d4b3b
...@@ -9,6 +9,7 @@ import java.io.Serializable; ...@@ -9,6 +9,7 @@ import java.io.Serializable;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
/** /**
...@@ -22,7 +23,7 @@ import org.hibernate.annotations.Comment; ...@@ -22,7 +23,7 @@ import org.hibernate.annotations.Comment;
@Getter @Getter
@Setter @Setter
@ToString @ToString
@Builder @SuperBuilder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@FieldNameConstants @FieldNameConstants
......
...@@ -84,7 +84,7 @@ public class ExampleController { ...@@ -84,7 +84,7 @@ public class ExampleController {
String text = i18n.get("example.hello"); String text = i18n.get("example.hello");
List<String> data = Arrays.asList(text.split(" ")); List<String> data = Arrays.asList(text.split(" "));
PageVo<String> vo = PageVo.build(data, data.size()); PageVo<String> vo = PageVo.build(data);
return Result.ok(vo); return Result.ok(vo);
} }
......
...@@ -17,6 +17,7 @@ import lombok.experimental.SuperBuilder; ...@@ -17,6 +17,7 @@ import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLDeleteAll; import org.hibernate.annotations.SQLDeleteAll;
import org.hibernate.annotations.Where;
/** /**
* 权限 * 权限
...@@ -34,6 +35,7 @@ import org.hibernate.annotations.SQLDeleteAll; ...@@ -34,6 +35,7 @@ import org.hibernate.annotations.SQLDeleteAll;
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@SQLDelete(sql = DELETE_SQL + BasicEntity.Where.WHERE_ID) @SQLDelete(sql = DELETE_SQL + BasicEntity.Where.WHERE_ID)
@SQLDeleteAll(sql = DELETE_SQL) @SQLDeleteAll(sql = DELETE_SQL)
@Where(clause = BasicEntity.Where.EXIST)
@Entity @Entity
@Table( @Table(
name = TABLE_NAME, name = TABLE_NAME,
...@@ -84,8 +86,9 @@ public class Permission extends BasicEntity implements Serializable { ...@@ -84,8 +86,9 @@ public class Permission extends BasicEntity implements Serializable {
@Comment("是否隐藏") @Comment("是否隐藏")
Boolean hidden; Boolean hidden;
@Comment("是否启用") @Comment("是否禁用")
Boolean enable; @Column(columnDefinition = "bool default false")
Boolean disabled;
@Comment("权限父级ID") @Comment("权限父级ID")
String pid; String pid;
......
...@@ -61,8 +61,9 @@ public class Role extends BasicEntity implements Serializable { ...@@ -61,8 +61,9 @@ public class Role extends BasicEntity implements Serializable {
@Column(nullable = false) @Column(nullable = false)
String name; String name;
@Comment("是否启用") @Comment("是否禁用")
Boolean enable; @Column(columnDefinition = "bool default false")
Boolean disabled;
@JsonIgnore @JsonIgnore
@Builder.Default @Builder.Default
......
...@@ -3,6 +3,7 @@ package com.yiring.auth.domain.role; ...@@ -3,6 +3,7 @@ package com.yiring.auth.domain.role;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
/** /**
...@@ -13,4 +14,4 @@ import org.springframework.stereotype.Repository; ...@@ -13,4 +14,4 @@ import org.springframework.stereotype.Repository;
*/ */
@Repository @Repository
public interface RoleRepository extends JpaRepository<Role, Serializable> {} public interface RoleRepository extends JpaRepository<Role, Serializable>, JpaSpecificationExecutor<Role> {}
...@@ -38,7 +38,7 @@ import org.hibernate.annotations.SQLDeleteAll; ...@@ -38,7 +38,7 @@ import org.hibernate.annotations.SQLDeleteAll;
@Entity @Entity
@Table( @Table(
name = User.TABLE_NAME, name = User.TABLE_NAME,
indexes = { @Index(columnList = User.Fields.enabled), @Index(columnList = BasicEntity.Fields.deleted) }, indexes = { @Index(columnList = User.Fields.disabled), @Index(columnList = BasicEntity.Fields.deleted) },
uniqueConstraints = { uniqueConstraints = {
@UniqueConstraint(columnNames = { User.Fields.username, BasicEntity.Fields.deleted }), @UniqueConstraint(columnNames = { User.Fields.username, BasicEntity.Fields.deleted }),
@UniqueConstraint(columnNames = { User.Fields.mobile, BasicEntity.Fields.deleted }), @UniqueConstraint(columnNames = { User.Fields.mobile, BasicEntity.Fields.deleted }),
...@@ -72,8 +72,8 @@ public class User extends BasicEntity implements Serializable { ...@@ -72,8 +72,8 @@ public class User extends BasicEntity implements Serializable {
@Comment("密码") @Comment("密码")
String password; String password;
@Comment("是否用") @Comment("是否用")
Boolean enabled; Boolean disabled;
@JsonIgnore @JsonIgnore
@Builder.Default @Builder.Default
......
...@@ -3,6 +3,7 @@ package com.yiring.auth.domain.user; ...@@ -3,6 +3,7 @@ package com.yiring.auth.domain.user;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
...@@ -14,31 +15,7 @@ import org.springframework.stereotype.Repository; ...@@ -14,31 +15,7 @@ import org.springframework.stereotype.Repository;
*/ */
@Repository @Repository
public interface UserRepository extends JpaRepository<User, Serializable> { public interface UserRepository extends JpaRepository<User, Serializable>, JpaSpecificationExecutor<User> {
/**
* 根据用户名称查询用户信息
*
* @param username 用户名
* @return 用户信息
*/
User findByUsername(String username);
/**
* 根据手机号查询用户信息
*
* @param mobile 手机号
* @return 用户信息
*/
User findByMobile(String mobile);
/**
* 根据邮箱查询用户信息
*
* @param email 邮箱
* @return 用户信息
*/
User findByEmail(String email);
/** /**
* 根据用户名/手机号/邮箱查询用户信息 * 根据用户名/手机号/邮箱查询用户信息
* *
......
...@@ -51,6 +51,6 @@ public class RegisterParam implements Serializable { ...@@ -51,6 +51,6 @@ public class RegisterParam implements Serializable {
@Parameter(description = "邮箱", example = "developer@yiring.com") @Parameter(description = "邮箱", example = "developer@yiring.com")
String email; String email;
@Parameter(description = "是否启用", example = "true") @Parameter(description = "是否禁用", example = "false")
Boolean enable; Boolean disabled;
} }
...@@ -68,8 +68,8 @@ public class PermissionParam implements Serializable { ...@@ -68,8 +68,8 @@ public class PermissionParam implements Serializable {
@Schema(description = "是否隐藏", example = "false") @Schema(description = "是否隐藏", example = "false")
Boolean hidden; Boolean hidden;
@Schema(description = "是否启用", example = "true") @Schema(description = "是否禁用", example = "false")
Boolean enable; Boolean disabled;
@Schema(description = "父级ID", example = "0") @Schema(description = "父级ID", example = "0")
@Builder.Default @Builder.Default
......
...@@ -29,6 +29,11 @@ public class Auths { ...@@ -29,6 +29,11 @@ public class Auths {
final UserRepository userRepository; final UserRepository userRepository;
/** /**
* 管理员用户
*/
public static final String ADMIN_USER = "admin";
/**
* 管理员角色标识 * 管理员角色标识
*/ */
public static final List<String> ADMIN_ROLES = List.of("admin", "super-admin", "platform-admin", "data-admin"); public static final List<String> ADMIN_ROLES = List.of("admin", "super-admin", "platform-admin", "data-admin");
...@@ -48,7 +53,11 @@ public class Auths { ...@@ -48,7 +53,11 @@ public class Auths {
} }
Optional<User> optional = userRepository.findById(Objects.toString(id)); Optional<User> optional = userRepository.findById(Objects.toString(id));
if (optional.isEmpty()) { if (
optional.isEmpty() ||
Boolean.TRUE.equals(optional.get().getDeleted()) ||
Boolean.TRUE.equals(optional.get().getDisabled())
) {
StpUtil.logout(id); StpUtil.logout(id);
throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.INVALID_TOKEN); throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.INVALID_TOKEN);
} }
...@@ -99,10 +108,13 @@ public class Auths { ...@@ -99,10 +108,13 @@ public class Auths {
* @return 是否为管理员 * @return 是否为管理员
*/ */
public boolean isAdmin(User user) { public boolean isAdmin(User user) {
return user return (
.getRoles() ADMIN_USER.equals(user.getUsername()) ||
.stream() user
.anyMatch(role -> Boolean.TRUE.equals(role.getEnable()) && ADMIN_ROLES.contains(role.getUid())); .getRoles()
.stream()
.anyMatch(role -> Boolean.FALSE.equals(role.getDisabled()) && ADMIN_ROLES.contains(role.getUid()))
);
} }
/** /**
......
...@@ -128,6 +128,7 @@ public class Permissions { ...@@ -128,6 +128,7 @@ public class Permissions {
.stream() .stream()
.map(Role::getPermissions) .map(Role::getPermissions)
.flatMap(Set::stream) .flatMap(Set::stream)
.filter(permission -> Boolean.FALSE.equals(permission.getDeleted()))
.distinct() .distinct()
.sorted(Comparator.comparing(Permission::getTree, Comparator.comparingInt(String::length))) .sorted(Comparator.comparing(Permission::getTree, Comparator.comparingInt(String::length)))
.collect(Collectors.toList()); .collect(Collectors.toList());
......
...@@ -59,8 +59,8 @@ public class PermissionVo implements Serializable { ...@@ -59,8 +59,8 @@ public class PermissionVo implements Serializable {
@Parameter(description = "是否隐藏", example = "false") @Parameter(description = "是否隐藏", example = "false")
Boolean hidden; Boolean hidden;
@Parameter(description = "是否启用", example = "true") @Parameter(description = "是否禁用", example = "false")
Boolean enable; Boolean disabled;
@Parameter(description = "父级ID", example = "0") @Parameter(description = "父级ID", example = "0")
String pid; String pid;
......
...@@ -47,8 +47,8 @@ public class UserVo implements Serializable { ...@@ -47,8 +47,8 @@ public class UserVo implements Serializable {
@Schema(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg") @Schema(description = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@Schema(description = "是否启用", example = "true") @Schema(description = "是否禁用", example = "false")
Boolean enabled; Boolean disabled;
@Schema(description = "是否删除", example = "false") @Schema(description = "是否删除", example = "false")
Boolean deleted; Boolean deleted;
......
...@@ -85,7 +85,7 @@ public class AuthController { ...@@ -85,7 +85,7 @@ public class AuthController {
.realName(param.getRealName()) .realName(param.getRealName())
.username(param.getUsername()) .username(param.getUsername())
.password(SaSecureUtil.sha256(param.getPassword())) .password(SaSecureUtil.sha256(param.getPassword()))
.enabled(param.getEnable()) .disabled(param.getDisabled())
.build(); .build();
userRepository.saveAndFlush(user); userRepository.saveAndFlush(user);
return Result.ok(); return Result.ok();
...@@ -100,22 +100,22 @@ public class AuthController { ...@@ -100,22 +100,22 @@ public class AuthController {
throw BusinessException.i18n("Code.100003"); throw BusinessException.i18n("Code.100003");
} }
// 检查密码
String cps = SaSecureUtil.sha256(param.getPassword());
if (!cps.equals(user.getPassword())) {
throw BusinessException.i18n("Code.100003");
}
// 检查用户是否已被删除 // 检查用户是否已被删除
if (Boolean.TRUE.equals(user.getDeleted())) { if (Boolean.TRUE.equals(user.getDeleted())) {
throw BusinessException.i18n("Code.100004"); throw BusinessException.i18n("Code.100004");
} }
// 检查用户是否被允许登录 // 检查用户是否被允许登录
if (!Boolean.TRUE.equals(user.getEnabled())) { if (Boolean.TRUE.equals(user.getDisabled())) {
throw BusinessException.i18n("Code.100005"); throw BusinessException.i18n("Code.100005");
} }
// 检查密码
String cps = SaSecureUtil.sha256(param.getPassword());
if (!cps.equals(user.getPassword())) {
throw BusinessException.i18n("Code.100003");
}
// 更新用户信息 // 更新用户信息
user.setLastLoginIp(Commons.getClientIpAddress(request)); user.setLastLoginIp(Commons.getClientIpAddress(request));
user.setLastLoginTime(LocalDateTime.now()); user.setLastLoginTime(LocalDateTime.now());
......
...@@ -118,7 +118,7 @@ public class PermissionController { ...@@ -118,7 +118,7 @@ public class PermissionController {
public Result<PageVo<PermissionVo>> page(@ParameterObject @Validated PageParam param) { public Result<PageVo<PermissionVo>> page(@ParameterObject @Validated PageParam param) {
Page<Permission> page = permissionRepository.findAll(PageParam.toPageable(param)); Page<Permission> page = permissionRepository.findAll(PageParam.toPageable(param));
List<PermissionVo> data = Permissions.toPermissionVos(page.toList()); List<PermissionVo> data = Permissions.toPermissionVos(page.toList());
PageVo<PermissionVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<PermissionVo> vo = PageVo.build(data, page.getTotalElements(), page.getTotalPages());
return Result.ok(vo); return Result.ok(vo);
} }
...@@ -168,7 +168,6 @@ public class PermissionController { ...@@ -168,7 +168,6 @@ public class PermissionController {
private void save(Permission entity, PermissionParam param) { private void save(Permission entity, PermissionParam param) {
BeanUtils.copyProperties(param, entity, Permission.Fields.meta); BeanUtils.copyProperties(param, entity, Permission.Fields.meta);
entity.setTree(getTreeNode(param.getPid())); entity.setTree(getTreeNode(param.getPid()));
entity.setEnable(true);
permissionRepository.saveAndFlush(entity); permissionRepository.saveAndFlush(entity);
} }
} }
...@@ -65,7 +65,6 @@ public class RoleController { ...@@ -65,7 +65,6 @@ public class RoleController {
} }
Role entity = new Role(); Role entity = new Role();
entity.setEnable(true);
BeanUtils.copyProperties(param, entity); BeanUtils.copyProperties(param, entity);
roleRepository.saveAndFlush(entity); roleRepository.saveAndFlush(entity);
return Result.ok(); return Result.ok();
...@@ -141,7 +140,7 @@ public class RoleController { ...@@ -141,7 +140,7 @@ public class RoleController {
public Result<PageVo<RoleVo>> page(@ParameterObject @Validated PageParam param) { public Result<PageVo<RoleVo>> page(@ParameterObject @Validated PageParam param) {
Page<Role> page = roleRepository.findAll(PageParam.toPageable(param)); Page<Role> page = roleRepository.findAll(PageParam.toPageable(param));
List<RoleVo> data = new ArrayList<>(Permissions.toRoleVos(page.toSet())); List<RoleVo> data = new ArrayList<>(Permissions.toRoleVos(page.toSet()));
PageVo<RoleVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<RoleVo> vo = PageVo.build(data, page.getTotalElements(), page.getTotalPages());
return Result.ok(vo); return Result.ok(vo);
} }
......
...@@ -12,6 +12,7 @@ import com.yiring.common.param.IdParam; ...@@ -12,6 +12,7 @@ import com.yiring.common.param.IdParam;
import com.yiring.common.param.IdsParam; import com.yiring.common.param.IdsParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import com.yiring.common.utils.Specifications;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.Extension;
...@@ -77,10 +78,10 @@ public class UserController { ...@@ -77,10 +78,10 @@ public class UserController {
@Operation(summary = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<UserVo>> page(@ParameterObject @Validated PageParam param) { public Result<PageVo<UserVo>> page(@ParameterObject @Validated PageParam param) {
Page<User> page = userRepository.findAll(PageParam.toPageable(param)); Page<User> page = userRepository.findAll(Specifications.exist(), PageParam.toPageable(param));
List<UserVo> data = page.get().map(user -> Commons.transform(user, UserVo.class)).collect(Collectors.toList()); List<UserVo> data = page.get().map(user -> Commons.transform(user, UserVo.class)).collect(Collectors.toList());
PageVo<UserVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<UserVo> vo = PageVo.build(data, page.getTotalElements(), page.getTotalPages());
return Result.ok(vo); return Result.ok(vo);
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
package com.yiring.auth.web.user; package com.yiring.auth.web.user;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.permission.PermissionRepository;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.auth.util.Auths; import com.yiring.auth.util.Auths;
import com.yiring.auth.util.Permissions; import com.yiring.auth.util.Permissions;
...@@ -43,6 +44,7 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -43,6 +44,7 @@ import org.springframework.web.bind.annotation.RestController;
public class UserViewController { public class UserViewController {
final Auths auths; final Auths auths;
final PermissionRepository permissionRepository;
@Operation(summary = "获取登录用户信息") @Operation(summary = "获取登录用户信息")
@GetMapping("getUserInfo") @GetMapping("getUserInfo")
...@@ -64,8 +66,16 @@ public class UserViewController { ...@@ -64,8 +66,16 @@ public class UserViewController {
@GetMapping("getMenuList") @GetMapping("getMenuList")
public Result<ArrayList<MenuVo>> getMenuList() { public Result<ArrayList<MenuVo>> getMenuList() {
User user = auths.getLoginUser(); User user = auths.getLoginUser();
List<Permission> permissions = Permissions
.toPermissions(user.getRoles()) // FIXED: admin 用户默认可以查询到所有菜单
List<Permission> list;
if (Auths.ADMIN_USER.equalsIgnoreCase(user.getUsername())) {
list = permissionRepository.findAll();
} else {
list = Permissions.toPermissions(user.getRoles());
}
List<Permission> permissions = list
.stream() .stream()
.filter(permission -> !Permission.Type.BUTTON.equals(permission.getType())) .filter(permission -> !Permission.Type.BUTTON.equals(permission.getType()))
.collect(Collectors.toList()); .collect(Collectors.toList());
......
...@@ -2,10 +2,7 @@ ...@@ -2,10 +2,7 @@
package com.yiring.common.domain; package com.yiring.common.domain;
import com.yiring.common.snowflake.SnowflakeId; import com.yiring.common.snowflake.SnowflakeId;
import jakarta.persistence.EntityListeners; import jakarta.persistence.*;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
...@@ -38,7 +35,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener; ...@@ -38,7 +35,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted") @Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@EntityListeners(AuditingEntityListener.class) @EntityListeners(AuditingEntityListener.class)
@MappedSuperclass @MappedSuperclass
public abstract class BasicEntity { public class BasicEntity {
@Comment("主键") @Comment("主键")
@Id @Id
...@@ -62,8 +59,9 @@ public abstract class BasicEntity { ...@@ -62,8 +59,9 @@ public abstract class BasicEntity {
@LastModifiedDate @LastModifiedDate
LocalDateTime updateTime; LocalDateTime updateTime;
@Comment("删除时间")
@Builder.Default @Builder.Default
@Comment("删除时间")
@Column(columnDefinition = "bool default false")
Boolean deleted = Boolean.FALSE; Boolean deleted = Boolean.FALSE;
public interface Where { public interface Where {
......
/* (C) 2023 YiRing, Inc. */
package com.yiring.common.utils;
import com.yiring.common.domain.BasicEntity;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.domain.Specification;
/**
* @author Jim
* @version 0.1
* 2023/3/17 16:36
*/
@SuppressWarnings("unused")
@UtilityClass
public class Specifications {
/**
* 构建谓词条件,支持扩展条件
*
* @param fn 回调函数
* @param <T> Entity 类型
* @return Specification
*/
public static <T> Specification<T> of(
CallbackFunction<Root<T>, CriteriaQuery<?>, CriteriaBuilder, List<Predicate>> fn
) {
return build(fn, null);
}
/**
* 构建存在的谓词条件,支持扩展条件
*
* @param fn 回调函数
* @param <T> Entity 类型
* @return Specification
*/
public static <T> Specification<T> exist(
CallbackFunction<Root<T>, CriteriaQuery<?>, CriteriaBuilder, List<Predicate>> fn
) {
return build(fn, false);
}
/**
* 构建存在的谓词条件
*
* @param <T> Entity 类型
* @return Specification
*/
public static <T> Specification<T> exist() {
return build(null, false);
}
/**
* 构建存在的 Example
*
* @param type Entity 类型
* @return Example
*/
@SneakyThrows
public static <T extends BasicEntity> Example<T> exist(Class<T> type) {
Constructor<T> declaredConstructor = type.getDeclaredConstructor();
T probe = declaredConstructor.newInstance();
probe.setDeleted(false);
return Example.of(probe);
}
/**
* 构建存在的谓词条件
*
* @param fn 回调函数
* @param deleted 是否删除
* @param <T> Entity 类型
* @return Specification
*/
private static <T> Specification<T> build(
CallbackFunction<Root<T>, CriteriaQuery<?>, CriteriaBuilder, List<Predicate>> fn,
Boolean deleted
) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (Objects.nonNull(deleted)) {
predicates.add(cb.equal(root.get(BasicEntity.Fields.deleted), deleted));
}
if (Objects.nonNull(fn)) {
fn.apply(root, query, cb, predicates);
}
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
};
}
/**
* 构建存在的谓词条件
*
* @param root 根
* @param cb 构建器
* @return 谓词
*/
public static List<Predicate> buildExistPredicates(Root<?> root, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get(BasicEntity.Fields.deleted), false));
return predicates;
}
@FunctionalInterface
public interface CallbackFunction<R, Q, C, P> {
void apply(R root, Q query, C cb, P predicates);
}
}
...@@ -4,43 +4,41 @@ package com.yiring.common.vo; ...@@ -4,43 +4,41 @@ package com.yiring.common.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
import lombok.*; import lombok.*;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
/** /**
* 键值对输出 * 选项 VO
* *
* @author ifzm * @author ifzm
* @version 0.1 * @version 0.1
* 2022/3/24 17:29 * 2022/3/24 17:29
*/ */
@Schema(name = "KeyValueVo", description = "键值对响应输出") @Schema(name = "OptionVo", description = "选项 VO")
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class KeyValueVo implements Serializable { public class OptionVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = -5238793972067296346L; private static final long serialVersionUID = 7178232019485233157L;
@Schema(description = "key", example = "key")
String key;
@Schema(description = "value", example = "value") @Schema(description = "value", example = "value")
String value; String value;
/**
* 扩展字段,可用于文本输出
*/
@Schema(description = "label", example = "label") @Schema(description = "label", example = "label")
String label; String label;
/** @Schema(description = "disabled", example = "false")
* 扩展字段,可用于显示禁用状态 Boolean disabled;
*/
@Schema(description = "是否启用", example = "true") @Schema(description = "extra")
String enable; Object extra;
@Schema(description = "children", example = "[]")
List<OptionVo> children;
} }
...@@ -37,6 +37,9 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -37,6 +37,9 @@ public class PageVo<T extends Serializable> implements Serializable {
@Schema(description = "数据总数", example = "100") @Schema(description = "数据总数", example = "100")
Long total; Long total;
@Schema(description = "分页页数", example = "10")
Integer pages;
/** /**
* 通常在带有时效性的数据查询时有用途(可选参数) * 通常在带有时效性的数据查询时有用途(可选参数)
*/ */
...@@ -51,7 +54,7 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -51,7 +54,7 @@ public class PageVo<T extends Serializable> implements Serializable {
*/ */
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
public static <R extends Serializable> PageVo<R> build(List<R> data) { public static <R extends Serializable> PageVo<R> build(List<R> data) {
return build(data, data.size()); return build(data, data.size(), 1);
} }
/** /**
...@@ -62,8 +65,8 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -62,8 +65,8 @@ public class PageVo<T extends Serializable> implements Serializable {
* @return PageVo * @return PageVo
*/ */
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
public static <R extends Serializable> PageVo<R> build(List<R> data, long total) { public static <R extends Serializable> PageVo<R> build(List<R> data, long total, int pages) {
return build(data, total, null); return build(data, total, pages, null);
} }
/** /**
...@@ -75,10 +78,11 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -75,10 +78,11 @@ public class PageVo<T extends Serializable> implements Serializable {
* @return PageVo * @return PageVo
*/ */
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
public static <R extends Serializable> PageVo<R> build(List<R> data, long total, LocalDateTime latest) { public static <R extends Serializable> PageVo<R> build(List<R> data, long total, int pages, LocalDateTime latest) {
PageVo<R> vo = new PageVo<>(); PageVo<R> vo = new PageVo<>();
vo.setData(data); vo.setData(data);
vo.setTotal(total); vo.setTotal(total);
vo.setPages(pages);
vo.setLatest(latest); vo.setLatest(latest);
return vo; return vo;
} }
...@@ -95,7 +99,7 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -95,7 +99,7 @@ public class PageVo<T extends Serializable> implements Serializable {
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
public static <S, T extends Serializable> PageVo<T> toPageVo(Page<S> page, Class<T> type) { public static <S, T extends Serializable> PageVo<T> toPageVo(Page<S> page, Class<T> type) {
List<T> data = Commons.transform(page.toList(), type); List<T> data = Commons.transform(page.toList(), type);
return build(data, page.getTotalElements()); return build(data, page.getTotalElements(), 1);
} }
/** /**
...@@ -103,6 +107,6 @@ public class PageVo<T extends Serializable> implements Serializable { ...@@ -103,6 +107,6 @@ public class PageVo<T extends Serializable> implements Serializable {
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static <T extends Serializable> PageVo<T> empty() { public static <T extends Serializable> PageVo<T> empty() {
return PageVo.build(Collections.emptyList(), 0); return build(Collections.emptyList(), 0, 1);
} }
} }
...@@ -69,7 +69,7 @@ public class Category extends BasicEntity implements Serializable { ...@@ -69,7 +69,7 @@ public class Category extends BasicEntity implements Serializable {
@Comment("分类父级 ID") @Comment("分类父级 ID")
String pid; String pid;
@Comment("分类是否启用") @Comment("是否禁用")
@Column(nullable = false) @Column(nullable = false, columnDefinition = "bool default false")
Boolean enable; Boolean disabled;
} }
...@@ -42,7 +42,8 @@ import org.hibernate.annotations.SQLDeleteAll; ...@@ -42,7 +42,8 @@ import org.hibernate.annotations.SQLDeleteAll;
@Index(columnList = BasicEntity.Fields.deleted), @Index(columnList = BasicEntity.Fields.deleted),
@Index(columnList = Dict.Fields.name), @Index(columnList = Dict.Fields.name),
@Index(columnList = Dict.Fields.code), @Index(columnList = Dict.Fields.code),
} },
uniqueConstraints = { @UniqueConstraint(columnNames = { Dict.Fields.code, BasicEntity.Fields.deleted }) }
) )
@Comment("数据字典") @Comment("数据字典")
public class Dict extends BasicEntity implements Serializable { public class Dict extends BasicEntity implements Serializable {
...@@ -58,12 +59,16 @@ public class Dict extends BasicEntity implements Serializable { ...@@ -58,12 +59,16 @@ public class Dict extends BasicEntity implements Serializable {
String name; String name;
@Comment("字典编号") @Comment("字典编号")
@Column(nullable = false, unique = true) @Column(nullable = false)
String code; String code;
@Comment("字典描述") @Comment("字典描述")
String description; String description;
@Comment("是否禁用")
@Column(columnDefinition = "bool default false")
Boolean disabled;
@JsonIgnore @JsonIgnore
@Builder.Default @Builder.Default
@Comment("字典选项集合") @Comment("字典选项集合")
......
...@@ -39,7 +39,7 @@ import org.hibernate.annotations.SQLDeleteAll; ...@@ -39,7 +39,7 @@ import org.hibernate.annotations.SQLDeleteAll;
indexes = { indexes = {
@Index(columnList = BasicEntity.Fields.deleted), @Index(columnList = BasicEntity.Fields.deleted),
@Index(columnList = DictItem.Fields.name), @Index(columnList = DictItem.Fields.name),
@Index(columnList = DictItem.Fields.enable), @Index(columnList = DictItem.Fields.disabled),
} }
) )
@Comment("数据字典选项") @Comment("数据字典选项")
...@@ -70,7 +70,7 @@ public class DictItem extends BasicEntity implements Serializable { ...@@ -70,7 +70,7 @@ public class DictItem extends BasicEntity implements Serializable {
@Comment("字典选项排序序号") @Comment("字典选项排序序号")
Integer serial; Integer serial;
@Comment("字典选项是否启用") @Comment("是否禁用")
@Column(nullable = false) @Column(columnDefinition = "bool default false")
Boolean enable; Boolean disabled;
} }
...@@ -52,7 +52,7 @@ public class DictItemParam implements Serializable { ...@@ -52,7 +52,7 @@ public class DictItemParam implements Serializable {
@Parameter(description = "字典选项排序序号") @Parameter(description = "字典选项排序序号")
Integer serial; Integer serial;
@Parameter(description = "字典选项是否启用", example = "true") @Parameter(description = "是否禁用", example = "false")
@NotNull @NotNull
Boolean enable; Boolean disabled;
} }
...@@ -33,6 +33,6 @@ public class SelectorDictItemParam implements Serializable { ...@@ -33,6 +33,6 @@ public class SelectorDictItemParam implements Serializable {
@Parameter(description = "字典 ID", example = "1") @Parameter(description = "字典 ID", example = "1")
String dictCode; String dictCode;
@Parameter(description = "字典选项是否启用", example = "true") @Parameter(description = "是否禁用", example = "false")
Boolean enable; Boolean disabled;
} }
...@@ -41,6 +41,6 @@ public class DictItemVo implements Serializable { ...@@ -41,6 +41,6 @@ public class DictItemVo implements Serializable {
@Schema(description = "字典选项排序序号", example = "1") @Schema(description = "字典选项排序序号", example = "1")
Integer serial; Integer serial;
@Schema(description = "字典选项是否启用", example = "true") @Schema(description = "是否禁用", example = "false")
Boolean enable; Boolean disabled;
} }
...@@ -8,8 +8,9 @@ import com.yiring.common.param.IdParam; ...@@ -8,8 +8,9 @@ import com.yiring.common.param.IdParam;
import com.yiring.common.param.IdsParam; import com.yiring.common.param.IdsParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import com.yiring.common.utils.Specifications;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import com.yiring.common.vo.KeyValueVo; import com.yiring.common.vo.OptionVo;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import com.yiring.dict.domain.Dict; import com.yiring.dict.domain.Dict;
import com.yiring.dict.domain.DictRepository; import com.yiring.dict.domain.DictRepository;
...@@ -112,19 +113,27 @@ public class DictController { ...@@ -112,19 +113,27 @@ public class DictController {
@Operation(summary = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<DictVo>> page(@ParameterObject @Validated PageParam param) { public Result<PageVo<DictVo>> page(@ParameterObject @Validated PageParam param) {
Page<Dict> page = dictRepository.findAll(PageParam.toPageable(param)); Page<Dict> page = dictRepository.findAll(Specifications.exist(), PageParam.toPageable(param));
List<DictVo> data = Commons.transform(page.getContent(), DictVo.class); List<DictVo> data = Commons.transform(page.getContent(), DictVo.class);
PageVo<DictVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<DictVo> vo = PageVo.build(data, page.getTotalElements(), page.getTotalPages());
return Result.ok(vo); return Result.ok(vo);
} }
@Operation(summary = "选项查询") @Operation(summary = "选项查询")
@GetMapping("selector") @GetMapping("selector")
public Result<ArrayList<KeyValueVo>> selector() { public Result<ArrayList<OptionVo>> selector() {
List<Dict> dictList = dictRepository.findAll(); List<Dict> dictList = dictRepository.findAll(Specifications.exist());
ArrayList<KeyValueVo> vos = dictList ArrayList<OptionVo> vos = dictList
.stream() .stream()
.map(dict -> KeyValueVo.builder().key(dict.getId()).value(dict.getCode()).label(dict.getName()).build()) .map(dict ->
OptionVo
.builder()
.value(dict.getId())
.label(dict.getName())
.extra(dict.getCode())
.disabled(dict.getDisabled())
.build()
)
.collect(Collectors.toCollection(ArrayList::new)); .collect(Collectors.toCollection(ArrayList::new));
return Result.ok(vos); return Result.ok(vos);
} }
......
...@@ -10,8 +10,9 @@ import com.yiring.common.param.IdParam; ...@@ -10,8 +10,9 @@ import com.yiring.common.param.IdParam;
import com.yiring.common.param.IdsParam; import com.yiring.common.param.IdsParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
import com.yiring.common.utils.Specifications;
import com.yiring.common.validation.group.Group; import com.yiring.common.validation.group.Group;
import com.yiring.common.vo.KeyValueVo; import com.yiring.common.vo.OptionVo;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import com.yiring.dict.domain.Dict; import com.yiring.dict.domain.Dict;
import com.yiring.dict.domain.DictItem; import com.yiring.dict.domain.DictItem;
...@@ -23,7 +24,6 @@ import io.swagger.v3.oas.annotations.Operation; ...@@ -23,7 +24,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
...@@ -111,24 +111,22 @@ public class DictItemController { ...@@ -111,24 +111,22 @@ public class DictItemController {
@Operation(summary = "分页查询") @Operation(summary = "分页查询")
@GetMapping("page") @GetMapping("page")
public Result<PageVo<DictItemVo>> page(@ParameterObject @Validated PageParam param) { public Result<PageVo<DictItemVo>> page(@ParameterObject @Validated PageParam param) {
Page<DictItem> page = dictItemRepository.findAll(PageParam.toPageable(param)); Page<DictItem> page = dictItemRepository.findAll(Specifications.exist(), PageParam.toPageable(param));
List<DictItemVo> data = Commons.transform(page.toList(), DictItemVo.class); List<DictItemVo> data = Commons.transform(page.toList(), DictItemVo.class);
PageVo<DictItemVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<DictItemVo> vo = PageVo.build(data, page.getTotalElements(), page.getTotalPages());
return Result.ok(vo); return Result.ok(vo);
} }
@Operation(summary = "选项查询") @Operation(summary = "选项查询")
@GetMapping("selector") @GetMapping("selector")
public Result<ArrayList<KeyValueVo>> selector(@ParameterObject @Validated SelectorDictItemParam param) { public Result<ArrayList<OptionVo>> selector(@ParameterObject @Validated SelectorDictItemParam param) {
if (StrUtil.isBlank(param.getDictId()) && StrUtil.isBlank(param.getDictCode())) { if (StrUtil.isBlank(param.getDictId()) && StrUtil.isBlank(param.getDictCode())) {
throw BusinessException.i18n("Code.101001"); throw BusinessException.i18n("Code.101001");
} }
Specification<DictItem> specification = (root, query, cb) -> { Specification<DictItem> specification = Specifications.exist((root, query, cb, predicates) -> {
List<Predicate> predicates = new ArrayList<>(); if (Objects.nonNull(param.getDisabled())) {
predicates.add(cb.equal(root.get(DictItem.Fields.disabled), param.getDisabled()));
if (Objects.nonNull(param.getEnable())) {
predicates.add(cb.equal(root.get(DictItem.Fields.enable), param.getEnable()));
} }
if (Objects.nonNull(param.getDictId())) { if (Objects.nonNull(param.getDictId())) {
predicates.add(cb.equal(root.get(DictItem.Fields.dict).get(BasicEntity.Fields.id), param.getDictId())); predicates.add(cb.equal(root.get(DictItem.Fields.dict).get(BasicEntity.Fields.id), param.getDictId()));
...@@ -136,14 +134,20 @@ public class DictItemController { ...@@ -136,14 +134,20 @@ public class DictItemController {
if (Objects.nonNull(param.getDictCode())) { if (Objects.nonNull(param.getDictCode())) {
predicates.add(cb.equal(root.get(DictItem.Fields.dict).get(Dict.Fields.code), param.getDictCode())); predicates.add(cb.equal(root.get(DictItem.Fields.dict).get(Dict.Fields.code), param.getDictCode()));
} }
});
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
};
List<DictItem> items = dictItemRepository.findAll(specification); List<DictItem> items = dictItemRepository.findAll(specification);
ArrayList<KeyValueVo> vos = items ArrayList<OptionVo> vos = items
.stream() .stream()
.map(item -> KeyValueVo.builder().key(item.getId()).value(item.getValue()).label(item.getName()).build()) .map(item ->
OptionVo
.builder()
.value(item.getId())
.label(item.getName())
.extra(item.getValue())
.disabled(item.getDisabled())
.build()
)
.collect(Collectors.toCollection(ArrayList::new)); .collect(Collectors.toCollection(ArrayList::new));
return Result.ok(vos); return Result.ok(vos);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论