liu_lake 6 місяців тому
батько
коміт
e5e8affb60

+ 5 - 5
db/db-system/src/main/java/pay/platform/mapper/SysRolePermissionMapper.java

@@ -2,11 +2,10 @@ package pay.platform.mapper;
 
 import com.mybatisflex.core.BaseMapper;
 import com.mybatisflex.core.query.QueryWrapper;
-import pay.platform.domain.SysRolePermission;
-import pay.platform.domain.table.SysPermissionTableDef;
-import pay.platform.domain.table.SysRolePermissionTableDef;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
+import pay.platform.domain.SysRolePermission;
+import pay.platform.domain.table.SysRolePermissionTableDef;
 
 import java.util.List;
 
@@ -43,8 +42,9 @@ public interface SysRolePermissionMapper extends BaseMapper<SysRolePermission> {
         QueryWrapper query = new QueryWrapper()
                 .select(SysRolePermissionTableDef.SYS_ROLE_PERMISSION.PERMISSION_ID)
                 .from(SysRolePermissionTableDef.SYS_ROLE_PERMISSION)
-                .where(SysRolePermissionTableDef.SYS_ROLE_PERMISSION.ROLE_ID.eq(roleId))
-                .eq(new QueryWrapper().select("count(id)").from(SysPermissionTableDef.SYS_PERMISSION).where(SysPermissionTableDef.SYS_PERMISSION.PARENT_ID.eq(SysRolePermissionTableDef.SYS_ROLE_PERMISSION.PERMISSION_ID)).toSQL(), 0);
+                .where(SysRolePermissionTableDef.SYS_ROLE_PERMISSION.ROLE_ID.eq(roleId));
+//                .eq(new QueryWrapper().select("count(id)")
+//                        .from(SysPermissionTableDef.SYS_PERMISSION).where(SysPermissionTableDef.SYS_PERMISSION.PARENT_ID.eq(SysRolePermissionTableDef.SYS_ROLE_PERMISSION.PERMISSION_ID)).toSQL(), 0);
         return selectListByQueryAs(query, String.class);
     }
 }

+ 32 - 0
web/admin-api/src/main/java/pay/platform/api/system/controller/PayOrderController.java

@@ -59,6 +59,38 @@ public class PayOrderController {
         return Result.OK(PayOrderService.removeById(id));
     }
 
+    /**
+     * 查询支付状态
+     *
+     * @param id 主键
+     * @return Result.OK({ @ code true } 删除成功 , { @ code false } 删除失败
+     */
+    @GetMapping("/status/{id}")
+    @Operation(summary = "根据主键查询支付状态")
+    @Parameters(value = {
+            @Parameter(name = "id", description = "id", required = true)
+    })
+    public Result<Boolean> payStatus(@PathVariable Serializable id) {
+        //TODO 查询支付状态
+        return Result.OK(true);
+    }
+
+    /**
+     * 支付回调商户
+     *
+     * @param id 主键
+     * @return Result.OK({ @ code true } 删除成功 , { @ code false } 删除失败
+     */
+    @GetMapping("/callback/{id}")
+    @Operation(summary = "根据主键回调商户")
+    @Parameters(value = {
+            @Parameter(name = "id", description = "id", required = true)
+    })
+    public Result<Boolean> orderCallback(@PathVariable Serializable id) {
+        //TODO 根据主键回调商户
+        return Result.OK(true);
+    }
+
 
     /**
      * 根据主键更新订单

+ 5 - 0
web/agent-api/pom.xml

@@ -106,6 +106,11 @@
             <artifactId>db-agent</artifactId>
             <version>1.0.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>pay.platform</groupId>
+            <artifactId>db-system</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 1 - 1
web/agent-api/src/main/java/pay/platform/api/system/controller/PayChannelController.java

@@ -24,7 +24,7 @@ import java.util.List;
  * @since 1.0
  */
 @RestController
-@RequestMapping("/sys/pay/channel")
+@RequestMapping("/agent/pay/channel")
 @Tag(name = "通道控制层")
 public class PayChannelController {
 

+ 1 - 1
web/agent-api/src/main/java/pay/platform/api/system/controller/PayMerchantController.java

@@ -28,7 +28,7 @@ import java.util.List;
  * @since 1.0
  */
 @RestController
-@RequestMapping("/sys/pay/merchant")
+@RequestMapping("/agent/pay/merchant")
 @Tag(name = "商户控制层")
 public class PayMerchantController {
 

+ 85 - 0
web/agent-api/src/main/java/pay/platform/api/system/controller/SysPermissionController.java

@@ -0,0 +1,85 @@
+package pay.platform.api.system.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import pay.platform.api.system.cache.RouteCache;
+import pay.platform.api.system.model.dto.PermissionTree;
+import pay.platform.api.system.model.query.PermissionTreeQuery;
+import pay.platform.api.system.model.vo.VueMenuRouteVO;
+import pay.platform.api.system.servcie.SysPermissionService;
+import pay.platform.core.common.Result;
+import pay.platform.core.security.model.UserInfo;
+import pay.platform.core.security.util.SecurityUtil;
+import pay.platform.domain.SysPermission;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 系统:菜单与权限
+ */
+@RestController
+@RequestMapping("/sys/permission")
+@Tag(name = "系统:菜单与权限")
+@RequiredArgsConstructor
+public class SysPermissionController {
+
+    private final SysPermissionService sysPermissionService;
+
+    private final RouteCache routeCache;
+
+    /**
+     * 获取当前用户路由
+     *
+     * @param request .
+     * @return .
+     */
+    @GetMapping("/routes")
+    @Operation(summary = "获取当前用户路由")
+    public Result<List<VueMenuRouteVO>> getCurrentRoute(HttpServletRequest request) {
+        UserInfo user = SecurityUtil.getCurrentUser();
+        Optional<List<VueMenuRouteVO>> routeCacheOptional = routeCache.getCache(user.getUserid());
+        if (routeCacheOptional.isPresent()) {
+            return Result.OK(routeCacheOptional.get());
+        }
+        List<VueMenuRouteVO> menuRoute = sysPermissionService.queryRouteByUserid(user.getUserid());
+
+        routeCache.putCache(user.getUserid(), menuRoute);
+        return Result.OK(menuRoute);
+    }
+
+    /**
+     * 获取菜单与权限
+     *
+     * @return .
+     */
+    @GetMapping("/tree")
+    @Operation(summary = "获取菜单与权限树形列表")
+    @PreAuthorize("@permission.hashPermission('menu:query','role:query')")
+    public Result<List<PermissionTree>> treesPermission(@ParameterObject PermissionTreeQuery query) {
+        Optional<List<SysPermission>> permissionOpt = sysPermissionService.listPermission(query);
+        Optional<List<PermissionTree>> treeOpt = permissionOpt
+                .flatMap(e -> Optional.ofNullable(sysPermissionService.buildTree(e)));
+        List<PermissionTree> tree = treeOpt.orElse(Collections.emptyList());
+        return Result.OK(tree);
+    }
+
+    @GetMapping("/tree/menu")
+    @Operation(summary = "获取菜单树形列表")
+    public Result<List<PermissionTree>> tressMenus(@ParameterObject PermissionTreeQuery query) {
+        Optional<List<SysPermission>> permissionOpt = sysPermissionService.listMenu(query);
+        Optional<List<PermissionTree>> treeOpt = permissionOpt
+                .flatMap(e -> Optional.ofNullable(sysPermissionService.buildTree(e)));
+        List<PermissionTree> tree = treeOpt.orElse(Collections.emptyList());
+        return Result.OK(tree);
+    }
+
+}

+ 66 - 0
web/agent-api/src/main/java/pay/platform/api/system/model/dto/PermissionTree.java

@@ -0,0 +1,66 @@
+package pay.platform.api.system.model.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import pay.platform.core.common.domain.TreeData;
+import pay.platform.core.enums.MenuTypeEnums;
+import pay.platform.core.enums.ValueEnum;
+import pay.platform.domain.SysPermission;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 权限树
+ */
+@Getter
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public class PermissionTree extends SysPermission implements TreeData<PermissionTree> {
+
+    @Schema(description = "子菜单")
+    private List<PermissionTree> children = new ArrayList<>();
+
+    public PermissionTree setChildren(List<PermissionTree> children) {
+        this.children = children;
+        return this;
+    }
+
+
+    @Schema(description = "是否显示")
+    public boolean isShowLink() {
+        return getShowLink() == 1;
+    }
+
+    @Schema(description = "是否显示父类")
+    public boolean isShowParent() {
+        return getShowParent() == 1;
+    }
+
+    @Schema(description = "是否缓存")
+    public boolean isKeepAlive() {
+        return getKeepAlive() == 1;
+    }
+
+    @Schema(description = "是否iframe")
+    public boolean isFrame() {
+        return getIsFrame() == 1;
+    }
+
+    @Schema(description = "菜单类型名称")
+    public String getMenuTypeName() {
+        if (ValueEnum.hash(MenuTypeEnums.class, getMenuType())) {
+            return ValueEnum.valueToEnum(MenuTypeEnums.class, getMenuType()).getName();
+        }
+        return null;
+    }
+
+    @Schema(description = "是否启用")
+    public boolean isEnable() {
+        return getEnable() == 1;
+    }
+}

+ 326 - 0
web/agent-api/src/main/java/pay/platform/api/system/servcie/SysPermissionService.java

@@ -0,0 +1,326 @@
+package pay.platform.api.system.servcie;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.mybatisflex.core.query.QueryWrapper;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pay.platform.api.system.model.dto.PermissionTree;
+import pay.platform.api.system.model.query.PermissionTreeQuery;
+import pay.platform.api.system.model.vo.PermissionVO;
+import pay.platform.api.system.model.vo.VueMenuRouteMetaVO;
+import pay.platform.api.system.model.vo.VueMenuRouteVO;
+import pay.platform.core.common.Result;
+import pay.platform.core.common.domain.BaseService;
+import pay.platform.core.enums.MenuTypeEnums;
+import pay.platform.domain.SysPermission;
+import pay.platform.domain.table.SysPermissionTableDef;
+import pay.platform.mapper.SysPermissionMapper;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 系统 - 菜单与权限 服务层实现。
+ *
+ * @author ADMIN
+ * @since 1.0
+ */
+@Slf4j
+@Service
+public class SysPermissionService extends BaseService<SysPermissionMapper, SysPermission> {
+
+    @Resource
+    @Lazy
+    private SysUserService sysUserService;
+
+    /**
+     * 获取权限ID集合 BY 角色ID结合
+     *
+     * @param roleIds 角色ID集合
+     * @return 权限ID集合
+     */
+    @Nonnull
+    public Set<String> listPermissionIdsByRoleIds(@Nonnull List<String> roleIds) {
+        List<SysPermission> permissions = mapper.listByRoleIds(roleIds, true);
+        if (null == permissions) {
+            return Collections.emptySet();
+        }
+        return permissions.stream().map(SysPermission::getId).collect(Collectors.toSet());
+    }
+
+    /**
+     * 根据ID查询已启用的权限code
+     *
+     * @param permissionIds ID
+     * @return 已启用的权限code
+     */
+    @Nonnull
+    public Set<String> listPermissionPreByIds(@Nonnull List<String> permissionIds) {
+        List<SysPermission> permissions = mapper.listPermission(permissionIds, true);
+        if (permissions == null) {
+            return Collections.emptySet();
+        }
+        return permissions.stream().map(SysPermission::getPerms).collect(Collectors.toSet());
+    }
+
+    /**
+     * 获取全部的权限信息
+     *
+     * @return .
+     */
+    @Nonnull
+    public Set<String> allPermissionPre() {
+        List<SysPermission> permissions = mapper.listPermission(null, true);
+        if (null == permissions) {
+            return Collections.emptySet();
+        }
+        return permissions.stream().map(SysPermission::getPerms).collect(Collectors.toSet());
+    }
+
+    /**
+     * 获取全部的ID
+     *
+     * @return .
+     */
+    @Nonnull
+    public Set<String> allPermissionIds() {
+        List<SysPermission> permissions = mapper.list(true);
+        if (null == permissions) {
+            return Collections.emptySet();
+        }
+        return permissions.stream().map(SysPermission::getId).collect(Collectors.toSet());
+    }
+
+    /**
+     * 根据用户获取菜单路由
+     *
+     * @param userid 用户ID
+     * @return 菜单路由
+     */
+    public List<VueMenuRouteVO> queryRouteByUserid(String userid) {
+        Set<String> roleIds = new HashSet<>();
+        roleIds.add("151835715022966784394251");
+        Set<String> permissionsIds = listPermissionIdsByRoleIds(List.copyOf(roleIds));
+        return queryRouteByIds(List.copyOf(permissionsIds));
+    }
+
+    /**
+     * 根据ID查询已启用的路由信息
+     *
+     * @param ids ID
+     * @return 路由信息
+     */
+    @Nonnull
+    public List<VueMenuRouteVO> queryRouteByIds(@Nonnull List<String> ids) {
+        List<SysPermission> sysPermissions = mapper.listMenuByIds(ids, true);
+        if (null == sysPermissions) {
+            return Collections.emptyList();
+        }
+        List<PermissionTree> trees = buildTree(sysPermissions);
+        return buildRoute(trees);
+    }
+
+    /**
+     * 将菜单路由权限集合构建为树形
+     *
+     * @param permissions 菜单路由权限
+     * @return .
+     */
+    public List<PermissionTree> buildTree(@Nonnull List<SysPermission> permissions) {
+        List<PermissionTree> permissionTrees = BeanUtil.copyToList(permissions, PermissionTree.class);
+        List<PermissionTree> trees = new ArrayList<>();
+        Set<String> ids = new HashSet<>();
+        for (PermissionTree permissionTree : permissionTrees) {
+            if (StrUtil.isBlank(permissionTree.getParentId())) {
+                trees.add(permissionTree);
+            }
+            for (PermissionTree tree : permissionTrees) {
+                if (permissionTree.getId().equals(tree.getParentId())) {
+                    if (permissionTree.getChildren() == null) {
+                        permissionTree.setChildren(new ArrayList<>());
+                    }
+                    permissionTree.getChildren().add(tree);
+
+                    ids.add(tree.getId());
+                }
+            }
+        }
+        if (trees.isEmpty()) {
+            trees = permissionTrees.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
+        }
+        return trees;
+    }
+
+    /**
+     * 构建菜单路由
+     *
+     * @param permissionTrees 菜单路由权限树集合
+     * @return 菜单路由
+     */
+    public List<VueMenuRouteVO> buildRoute(@Nonnull List<PermissionTree> permissionTrees) {
+        List<VueMenuRouteVO> menuRouteList = new ArrayList<>();
+        for (PermissionTree permissionTree : permissionTrees) {
+            menuRouteList.add(buildRoute(permissionTree));
+        }
+        return menuRouteList;
+    }
+
+
+    /**
+     * 构建菜单路由
+     *
+     * @param permissionTree 菜单路由权限树
+     * @return 菜单路由
+     */
+    public VueMenuRouteVO buildRoute(PermissionTree permissionTree) {
+        VueMenuRouteVO menuRoute = new VueMenuRouteVO();
+        Integer menuType = permissionTree.getMenuType();
+        //  路由地址 必须有个 `/`
+        menuRoute.setPath(StrUtil.addPrefixIfNot(permissionTree.getPath(), "/"));
+        // 路由名字(必须保持唯一)
+        menuRoute.setName(permissionTree.getName());
+        // 元信息
+        VueMenuRouteMetaVO vo = new VueMenuRouteMetaVO();
+        // 菜单名称
+        vo.setTitle(permissionTree.getTitle());
+        // 菜单图标
+        vo.setIcon(permissionTree.getIcon());
+        // 是否显示
+        vo.setShowLink(permissionTree.getShowLink() == 1);
+        // 目录
+        if (MenuTypeEnums.dir.getValue().equals(menuType)) {
+            // 菜单排序,值越高排的越后(只针对顶级路由)
+            // TODO 其实这里已经时有序取出了,所以可以不需要给RouteMeta设置rank
+            vo.setRank(permissionTree.getRank());
+        }
+        //菜单
+        else if (MenuTypeEnums.menu.getValue().equals(menuType)) {
+            // 按需加载需要展示的页面 不需要 '/'
+            menuRoute.setComponent(StrUtil.removePrefix(permissionTree.getComponent(), "/"));
+            //  是否显示父级菜单
+            vo.setShowParent(permissionTree.getShowParent() == 1);
+            // 是否缓存该路由页面
+            vo.setKeepAlive(permissionTree.getKeepAlive() == 1);
+            // 需要内嵌的iframe链接地址
+            if (permissionTree.getIsFrame() == 1) {
+                vo.setFrameSrc(permissionTree.getPath());
+            }
+        }
+        menuRoute.setMeta(vo);
+
+        if (CollectionUtil.isNotEmpty(permissionTree.getChildren())) {
+            menuRoute.setChildren(buildRoute(permissionTree.getChildren()));
+        }
+        return menuRoute;
+    }
+    /*==============================================================================*/
+
+
+    /**
+     * 查询菜单与权限列表
+     *
+     * @param query .
+     * @return .
+     */
+
+    public Optional<List<SysPermission>> listPermission(PermissionTreeQuery query) {
+        QueryWrapper queryWrapper = buildQuery(query);
+        return Optional.ofNullable(list(queryWrapper));
+    }
+
+    /**
+     * 只查询菜单列表
+     *
+     * @param query .
+     * @return .
+     */
+    public Optional<List<SysPermission>> listMenu(PermissionTreeQuery query) {
+        QueryWrapper queryWrapper = buildQuery(query);
+        queryWrapper.ne(SysPermission::getMenuType, MenuTypeEnums.permission.getValue());
+        return Optional.ofNullable(list(queryWrapper));
+    }
+
+    /**
+     * 保存
+     *
+     * @param vo .
+     * @return .
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Optional<PermissionTree> save(PermissionVO vo) {
+        SysPermission permission = BeanUtil.toBean(vo, SysPermission.class);
+        log.info("【菜单与权限】新增菜单与权限,参数: {}", permission);
+        save(permission);
+        PermissionTree bean = BeanUtil.toBean(permission, PermissionTree.class);
+        return Optional.of(bean);
+    }
+
+    /**
+     * 根据ID更新
+     *
+     * @param vo .
+     * @param id .
+     * @return ,
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Optional<PermissionTree> updateById(PermissionVO vo, @Nonnull String id) {
+        SysPermission permission = BeanUtil.toBean(vo, SysPermission.class);
+        permission.setId(id);
+        log.info("【菜单与权限】根据ID更新菜单与权限,参数: id--->{} data:---> {}", id, permission);
+        updateById(permission);
+        PermissionTree bean = BeanUtil.toBean(permission, PermissionTree.class);
+        return Optional.of(bean);
+    }
+
+    /**
+     * 根据ID删除
+     *
+     * @param query 查询条件
+     * @return .
+     */
+    public QueryWrapper buildQuery(@Nullable PermissionTreeQuery query) {
+        QueryWrapper queryWrapper = QueryWrapper.create().from(SysPermissionTableDef.SYS_PERMISSION);
+        queryWrapper.orderBy(SysPermission::getRank, true);
+        if (null == query) {
+            return queryWrapper;
+        }
+        queryWrapper.like(SysPermission::getTitle, query.getTitle(), StrUtil.isNotBlank(query.getTitle()));
+        queryWrapper.eq(SysPermission::getEnable, query.getEnable(), null != query.getEnable());
+        return queryWrapper;
+    }
+
+    /**
+     * 批量删除
+     *
+     * @param ids ID集合
+     * @return .
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result<String> deleteByIds(List<String> ids) {
+        if (CollectionUtil.isEmpty(ids)) {
+            return Result.NG("删除失败");
+        }
+        QueryWrapper queryWrapper = QueryWrapper.create().from(SysPermissionTableDef.SYS_PERMISSION);
+        queryWrapper.in(SysPermission::getParentId, ids);
+        List<SysPermission> sysPermissionList = mapper.selectListByQuery(queryWrapper);
+        if (CollectionUtil.isNotEmpty(sysPermissionList)) {
+            List<String> list = sysPermissionList.stream().map(SysPermission::getId).toList();
+            ids = new ArrayList<>(ids);
+            ids.addAll(list);
+        }
+        boolean remove = removeByIds(ids);
+        if (remove) {
+            return Result.OK("删除成功");
+        }
+        return Result.NG("删除失败");
+
+    }
+}

+ 313 - 0
web/agent-api/src/main/java/pay/platform/api/system/servcie/SysUserService.java

@@ -0,0 +1,313 @@
+package pay.platform.api.system.servcie;
+
+import cn.bzvs.otp.OtpAuthUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryWrapper;
+import com.mybatisflex.core.util.StringUtil;
+import jakarta.annotation.Nonnull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import pay.platform.api.system.model.query.UserQuery;
+import pay.platform.api.system.model.vo.UserVO;
+import pay.platform.core.common.Result;
+import pay.platform.core.common.domain.BasePage;
+import pay.platform.core.common.domain.BaseService;
+import pay.platform.core.util.PasswordUtil;
+import pay.platform.domain.SysUser;
+import pay.platform.domain.table.SysUserTableDef;
+import pay.platform.mapper.SysRoleMapper;
+import pay.platform.mapper.SysUserMapper;
+import pay.platform.mapper.SysUserRoleMapper;
+
+import java.util.*;
+
+/**
+ * 系统 - 用户 服务层实现。
+ *
+ * @author ADMIN
+ * @since 1.0
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SysUserService extends BaseService<SysUserMapper, SysUser> {
+
+    private final SysRoleMapper sysRoleMapper;
+
+    private final SysUserRoleMapper sysUserRoleMapper;
+
+    /**
+     * 根据用户名获取用户信息
+     *
+     * @param username 用户名
+     * @return .
+     */
+    public Optional<SysUser> loadUserByUsername(String username) {
+        QueryWrapper queryWrapper = QueryWrapper.create().from(SysUserTableDef.SYS_USER).where(SysUserTableDef.SYS_USER.USERNAME.eq(username));
+        SysUser sysUser = mapper.selectOneByQuery(queryWrapper);
+        return Optional.ofNullable(sysUser);
+    }
+
+    /**
+     * 获取用户角色编码
+     *
+     * @param username 用户名称
+     * @return 角色编码
+     */
+    @Nonnull
+    public Set<String> getRoleCodeByUsername(String username) {
+        Set<String> res = mapper.getRoleCodeByUsername(username);
+        return res == null ? Collections.emptySet() : res;
+    }
+
+    /**
+     * 获取全部的角色编码
+     *
+     * @return .
+     */
+    public Set<String> allRoleCode() {
+        Set<String> res = sysRoleMapper.allRoleCode();
+        return res == null ? Collections.emptySet() : res;
+    }
+
+
+    /**
+     * 根据用户ID 获取对应的角色ID集合
+     *
+     * @param userId 角色ID
+     * @return 角色ID集合
+     */
+    public Set<String> queryRoleIdsByUserId(String userId) {
+        Set<String> res = mapper.queryUserRole(userId);
+        return res == null ? Collections.emptySet() : res;
+    }
+    /*===================================================*/
+
+    /**
+     * 分页查询
+     *
+     * @param query .
+     * @return .
+     */
+    public BasePage<UserVO> queryPage(UserQuery query) {
+        QueryWrapper queryWrapper = QueryWrapper.create().from(SysUserTableDef.SYS_USER).where("1 = 1").and(SysUserTableDef.SYS_USER.USERNAME.like(query.getUsername(), StringUtil.isNotBlank(query.getUsername()))).and(SysUserTableDef.SYS_USER.NICKNAME.eq(query.getNickname(), StringUtil.isNotBlank(query.getNickname()))).and(SysUserTableDef.SYS_USER.ENABLE.eq(query.getEnable(), query.getEnable() != null)).orderBy(SysUserTableDef.SYS_USER.UPDATE_TIME, false);
+        Page<SysUser> page = mapper.paginate(new Page<>(query.getCurrent(), query.getSize()), queryWrapper);
+        List<UserVO> vos = BeanUtil.copyToList(page.getRecords(), UserVO.class);
+        return new BasePage<>(page.getPageNumber(), page.getPageSize(), page.getTotalRow(), vos);
+    }
+
+    /**
+     * 列表查询
+     *
+     * @param query .
+     * @return .
+     */
+    public List<UserVO> queryList(UserQuery query) {
+        QueryWrapper queryWrapper = QueryWrapper.create().from(SysUserTableDef.SYS_USER).where("1 = 1").and(SysUserTableDef.SYS_USER.USERNAME.like(query.getUsername(), StringUtil.isNotBlank(query.getUsername()))).and(SysUserTableDef.SYS_USER.NICKNAME.eq(query.getNickname(), StringUtil.isNotBlank(query.getNickname()))).and(SysUserTableDef.SYS_USER.ENABLE.eq(query.getEnable(), query.getEnable() != null));
+        List<SysUser> list = mapper.selectListByQuery(queryWrapper);
+        return BeanUtil.copyToList(list, UserVO.class);
+    }
+
+    /**
+     * 帐号是否存在
+     *
+     * @param username .
+     * @return .
+     */
+    public Result<Boolean> hashUsername(String username) {
+        return Result.OK(loadUserByUsername(username).isPresent());
+    }
+
+    /**
+     * 根据ID获取详情
+     *
+     * @param id .
+     * @return .
+     */
+    public Result<UserVO> detail(String id) {
+        SysUser user = getById(id);
+        UserVO res = BeanUtil.toBean(user, UserVO.class);
+        res.setPassword(null);
+        return Result.OK(res);
+    }
+
+    /**
+     * 根据用户名获取详情
+     *
+     * @param username .
+     * @return .
+     */
+    public Result<UserVO> detailByUsername(String username) {
+        Optional<SysUser> user = loadUserByUsername(username);
+        if (user.isPresent()) {
+            UserVO res = BeanUtil.toBean(user, UserVO.class);
+            res.setPassword(null);
+            return Result.OK(res);
+        } else {
+            return Result.NG("用户不存在");
+        }
+    }
+
+    /**
+     * 根据用户ID获取角色ID集合
+     *
+     * @param id .
+     * @return .
+     */
+
+    public Result<Set<String>> getRoleIdsByUserId(String id) {
+        Set<String> roleIds = queryRoleIdsByUserId(id);
+        return Result.OK(roleIds);
+    }
+
+    /**
+     * 保存用户
+     *
+     * @param vo 用户信息
+     * @return .
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Result<UserVO> save(UserVO vo) {
+        String username = vo.getUsername();
+        if (hashUsername(username).getResult()) {
+            return Result.NG("帐号已存在");
+        }
+        String password = vo.getPassword();
+        if (StrUtil.isBlank(password)) {
+            return Result.NG("密码不能为空");
+        }
+        String newPassword = PasswordUtil.encoder(vo.getPassword());
+        SysUser user = BeanUtil.toBean(vo, SysUser.class);
+        user.setPassword(newPassword);
+        save(user);
+        List<String> roleIds = vo.getRoleIds();
+        if (CollectionUtil.isNotEmpty(roleIds)) {
+            sysUserRoleMapper.saveUserRole(user.getId(), new HashSet<>(roleIds));
+        }
+        return Result.OK(vo);
+    }
+
+    /**
+     * 更新用户
+     *
+     * @param id .
+     * @param vo .
+     * @return .
+     */
+    public Result<UserVO> updateById(String id, UserVO vo) {
+        SysUser user = getById(id);
+        if (null == user) {
+            return Result.NG("用户不存在");
+        }
+        BeanUtil.copyProperties(vo, user);
+        updateById(user);
+        List<String> roleIds = vo.getRoleIds();
+        sysUserRoleMapper.delUserRoleByUserid(user.getId());
+        if (CollectionUtil.isNotEmpty(roleIds)) {
+            sysUserRoleMapper.saveUserRole(user.getId(), new HashSet<>(roleIds));
+        }
+        return Result.OK(vo);
+    }
+
+    /**
+     * 删除用户
+     *
+     * @param ids 用户集合
+     * @return .
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result<String> deleteByIds(List<String> ids) {
+        if (CollectionUtil.isEmpty(ids)) {
+            return Result.NG("参数不能为空");
+        }
+        removeByIds(ids);
+        sysUserRoleMapper.delUserRoleByUserids(ids);
+        return Result.OK();
+    }
+
+    /**
+     * 重置密码
+     *
+     * @param username .
+     * @param passwd   .
+     * @return .
+     */
+
+    public Result<String> resetPassword(@Nonnull String username, @Nonnull String passwd) {
+        String newPassword = PasswordUtil.encoder(passwd);
+        mapper.resetPassword(username, newPassword);
+        return Result.OK();
+    }
+
+    /**
+     * 修改密码
+     *
+     * @param username  .
+     * @param oldPasswd .
+     * @param newPasswd .
+     * @return .
+     */
+    public Result<String> changePasswd(@Nonnull String username, @Nonnull String oldPasswd, @Nonnull String newPasswd) {
+        Optional<SysUser> sysUser = loadUserByUsername(username);
+        if (sysUser.isPresent()) {
+            return Result.NG("用户不存在");
+        }
+        SysUser user = sysUser.get();
+        if (!PasswordUtil.matches(oldPasswd, user.getPassword())) {
+            return Result.NG("原密码错误");
+        }
+        String newPassword = PasswordUtil.encoder(newPasswd);
+        user.setPassword(newPassword);
+        mapper.update(user);
+        return Result.OK();
+    }
+
+    /**
+     * 是否有谷歌验证器
+     *
+     * @param id ID
+     * @return true/false
+     */
+    public boolean hasGoogleOtp(String id) {
+        SysUser sysUser = getById(id);
+        return StringUtils.hasText(sysUser.getOtp());
+    }
+
+    /**
+     * 创建谷歌验证器
+     *
+     * @param id ID
+     * @return otp
+     */
+    public String createGoogleOtp(String id) {
+        SysUser sysUser = getById(id);
+        String secret = OtpAuthUtil.createSecret();
+        sysUser.setOtp(secret);
+        updateById(sysUser);
+        return OtpAuthUtil.getOtpQrCodeUrl("", secret);
+    }
+
+    /**
+     * 校验谷歌验证器
+     *
+     * @param id   ID
+     * @param code 验证码
+     * @return true/false
+     */
+    public boolean checkGoogleOtp(String id, String code) {
+        SysUser sysUser = getById(id);
+        String secret = sysUser.getOtp();
+        if (Boolean.FALSE.equals(StringUtils.hasText(secret))) {
+            log.warn("用户未设置谷歌验证器");
+            return false;
+        }
+        return OtpAuthUtil.validateCode(secret, code);
+    }
+}