提交 bb485532 作者: 方治民

合并分支 'master' 到 'master'

Master

查看合并请求 chemical-kesai/kshg-api!3
...@@ -24,8 +24,9 @@ ...@@ -24,8 +24,9 @@
## TODO ## TODO
<!-- prettier-ignore --> <!-- prettier-ignore -->
- [x] 项目构建 - [x] 完成项目构建,开发文档编写
- [x] 用户及权限模块(菜单/按钮) - [x] [conventional-changelog](https://www.cnblogs.com/mengfangui/p/12634845.html)
- [x] 用户及权限模块(目录/菜单/按钮),预览初始化权限配置 [SQL 脚本](./basic-auth/src/main/resources/init-test-mysql.sql)
- [x] 通用文件上传模块 - [x] 通用文件上传模块
- [ ] 通用字典管理模块 - [ ] 通用字典管理模块
- [ ] XXL-JOB 定时任务模块 - [ ] XXL-JOB 定时任务模块
...@@ -30,4 +30,7 @@ dependencies { ...@@ -30,4 +30,7 @@ dependencies {
implementation project(":basic-common:minio") implementation project(":basic-common:minio")
// FIX: minio dep // FIX: minio dep
implementation "com.squareup.okhttp3:okhttp:${okhttpVersion}" implementation "com.squareup.okhttp3:okhttp:${okhttpVersion}"
// fastjson
implementation "com.alibaba:fastjson:${fastJsonVersion}"
} }
...@@ -6,6 +6,7 @@ import com.yiring.app.constant.Code; ...@@ -6,6 +6,7 @@ import com.yiring.app.constant.Code;
import com.yiring.app.exception.CodeException; import com.yiring.app.exception.CodeException;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.core.Status; import com.yiring.common.core.Status;
import com.yiring.common.exception.FailStatusException;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -33,7 +34,7 @@ public class GlobalExceptionHandler { ...@@ -33,7 +34,7 @@ public class GlobalExceptionHandler {
* 参数校验异常 * 参数校验异常
* *
* @param e 异常信息 * @param e 异常信息
* @return 统一的校验失败信息 {@link Status#BAD_REQUEST * @return 统一的校验失败信息 {@link Status#EXPECTATION_FAILED
*/ */
@ExceptionHandler( @ExceptionHandler(
value = { BindException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class } value = { BindException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class }
...@@ -57,7 +58,7 @@ public class GlobalExceptionHandler { ...@@ -57,7 +58,7 @@ public class GlobalExceptionHandler {
} }
} }
return Result.no(Status.BAD_REQUEST, error); return Result.no(Status.EXPECTATION_FAILED, error);
} }
/** /**
...@@ -74,7 +75,6 @@ public class GlobalExceptionHandler { ...@@ -74,7 +75,6 @@ public class GlobalExceptionHandler {
/** /**
* 未登录异常(鉴权失败) * 未登录异常(鉴权失败)
* *
* @param e 异常信息
* @return 异常信息反馈 {@link Status#UNAUTHORIZED * @return 异常信息反馈 {@link Status#UNAUTHORIZED
*/ */
@ExceptionHandler(value = NotLoginException.class) @ExceptionHandler(value = NotLoginException.class)
...@@ -83,12 +83,6 @@ public class GlobalExceptionHandler { ...@@ -83,12 +83,6 @@ public class GlobalExceptionHandler {
} }
/** /**
* 取消请求异常(忽略)
*/
@ExceptionHandler(value = ClientAbortException.class)
public void clientAbortExceptionHandler() {}
/**
* 自定义业务异常 * 自定义业务异常
*/ */
@ExceptionHandler(value = CodeException.class) @ExceptionHandler(value = CodeException.class)
...@@ -98,6 +92,20 @@ public class GlobalExceptionHandler { ...@@ -98,6 +92,20 @@ public class GlobalExceptionHandler {
} }
/** /**
* 失败状态异常
*/
@ExceptionHandler(value = FailStatusException.class)
public Result<String> customCodeExceptionHandler(FailStatusException e) {
return Result.no(e.getStatus());
}
/**
* 取消请求异常(忽略)
*/
@ExceptionHandler(value = ClientAbortException.class)
public void clientAbortExceptionHandler() {}
/**
* 其他异常 * 其他异常
* *
* @param e 异常信息 * @param e 异常信息
......
spring: spring:
datasource: datasource:
url: jdbc:h2:mem:mockdb;DB_CLOSE_ON_EXIT=FALSE url: jdbc:h2:file:~/h2_basic;DB_CLOSE_ON_EXIT=FALSE
username: sa username: sa
password: 123456 password: 123456
jpa: jpa:
......
server: server:
port: 8181 port: 8181
servlet: servlet:
context-path: /basic-api context-path: /api
spring: spring:
application: application:
......
...@@ -17,4 +17,7 @@ dependencies { ...@@ -17,4 +17,7 @@ dependencies {
// hutool-core // hutool-core
implementation "cn.hutool:hutool-core:${hutoolVersion}" implementation "cn.hutool:hutool-core:${hutoolVersion}"
// fastjson
implementation "com.alibaba:fastjson:${fastJsonVersion}"
} }
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.domain.permission; package com.yiring.auth.domain.permission;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.*; import javax.persistence.*;
import lombok.*; import lombok.*;
...@@ -61,6 +63,9 @@ public class Permission implements Serializable { ...@@ -61,6 +63,9 @@ public class Permission implements Serializable {
@Comment("路径") @Comment("路径")
String path; String path;
@Comment("重定向")
String redirect;
@Comment("组件") @Comment("组件")
String component; String component;
...@@ -79,9 +84,22 @@ public class Permission implements Serializable { ...@@ -79,9 +84,22 @@ public class Permission implements Serializable {
@Comment("树节点标识") @Comment("树节点标识")
String tree; String tree;
/**
* 可用于扩展一些前端可能用到的路由参数
*/
@Comment("扩展元数据信息")
@Lob
@Column(columnDefinition = "JSON")
String meta;
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
public enum Type { public enum Type {
/** /**
* 目录/平台
*/
DIR,
/**
* 菜单 * 菜单
*/ */
MENU, MENU,
...@@ -91,4 +109,23 @@ public class Permission implements Serializable { ...@@ -91,4 +109,23 @@ public class Permission implements Serializable {
*/ */
BUTTON, BUTTON,
} }
/**
* 获取权限的元数据信息,通常是根据前端所需来输出,可自定义调整
* @return JSON 格式 Meta 元数据
*/
public JSONObject getMetaJson() {
JSONObject meta = new JSONObject();
meta.put("title", this.name);
meta.put("icon", this.icon);
meta.put("orderNo", this.serial);
meta.put("hideMenu", this.hidden);
try {
String raw = this.meta.replace("\\", "").replaceAll("^\"(.*)\"$", "$1");
meta.putAll(JSON.parseObject(raw));
} catch (Exception ignored) {}
return meta;
}
} }
...@@ -68,8 +68,8 @@ public class User implements Serializable { ...@@ -68,8 +68,8 @@ public class User implements Serializable {
@Comment("密码") @Comment("密码")
String password; String password;
@Comment("职称") @Comment("简介")
String title; String introduction;
@Comment("头像") @Comment("头像")
String avatar; String avatar;
......
...@@ -43,14 +43,14 @@ public class RegisterParam implements Serializable { ...@@ -43,14 +43,14 @@ public class RegisterParam implements Serializable {
@Pattern(regexp = "^1[0-9]{10}$", message = "手机号码格式不正确") @Pattern(regexp = "^1[0-9]{10}$", message = "手机号码格式不正确")
String mobile; String mobile;
@ApiModelProperty(value = "头像", example = "http://img.ifzm.cn/cat.jpg") @ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty(value = "邮箱", example = "developer@yiring.com") @ApiModelProperty(value = "邮箱", example = "developer@yiring.com")
String email; String email;
@ApiModelProperty(value = "职称", example = "平台管理员") @ApiModelProperty(value = "简介", example = "平台管理员")
String title; String introduction;
@ApiModelProperty(value = "是否启用", example = "true") @ApiModelProperty(value = "是否启用", example = "true")
Boolean enable; Boolean enable;
......
...@@ -36,21 +36,24 @@ public class PermissionParam implements Serializable { ...@@ -36,21 +36,24 @@ public class PermissionParam implements Serializable {
@ApiModelProperty(value = "序号", example = "1") @ApiModelProperty(value = "序号", example = "1")
Integer serial; Integer serial;
@ApiModelProperty(value = "标识", example = "home", required = true) @ApiModelProperty(value = "标识", example = "Dashboard", required = true)
@NotEmpty(message = "权限标识不能为空") @NotEmpty(message = "权限标识不能为空")
String uid; String uid;
@ApiModelProperty(value = "名称", example = "主页", required = true) @ApiModelProperty(value = "名称", example = "Dashboard", required = true)
@NotEmpty(message = "权限名称不能为空") @NotEmpty(message = "权限名称不能为空")
String name; String name;
@ApiModelProperty(value = "路径", example = "/") @ApiModelProperty(value = "路径", example = "/dashboard")
String path; String path;
@ApiModelProperty(value = "组件", example = "/home") @ApiModelProperty(value = "重定向", example = "/dashboard/workbench")
String redirect;
@ApiModelProperty(value = "组件", example = "LAYOUT")
String component; String component;
@ApiModelProperty(value = "图标", example = "menu") @ApiModelProperty(value = "图标", example = "ion:grid-outline")
String icon; String icon;
@ApiModelProperty(value = "是否隐藏", example = "false") @ApiModelProperty(value = "是否隐藏", example = "false")
...@@ -62,4 +65,10 @@ public class PermissionParam implements Serializable { ...@@ -62,4 +65,10 @@ public class PermissionParam implements Serializable {
@ApiModelProperty(value = "父级ID", example = "0") @ApiModelProperty(value = "父级ID", example = "0")
@Builder.Default @Builder.Default
Long pid = 0L; Long pid = 0L;
@ApiModelProperty(
value = "元数据",
example = "{\"title\": \"routes.dashboard.dashboard\", \"hideChildrenInMenu\": true}"
)
String meta;
} }
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.util; package com.yiring.auth.util;
import com.sun.istack.Nullable;
import com.yiring.auth.domain.permission.Permission; import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
import com.yiring.auth.vo.permission.MenuVo;
import com.yiring.auth.vo.permission.PermissionVo; import com.yiring.auth.vo.permission.PermissionVo;
import com.yiring.auth.vo.role.RoleVo; import com.yiring.auth.vo.role.RoleVo;
import com.yiring.common.util.Commons;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.NonNull; import lombok.NonNull;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
...@@ -39,6 +43,41 @@ public class Permissions { ...@@ -39,6 +43,41 @@ public class Permissions {
} }
/** /**
* 将权限集合转换成菜单树
* @param permissions 权限集合
* @return 菜单树
*/
public static List<MenuVo> toMenuTreeVo(@Nullable List<Permission> permissions) {
List<MenuVo> list = Commons.transform(
permissions,
MenuVo.class,
(source, target) -> target.setMeta(source.getMetaJson()),
Permission.Fields.meta
);
// 返回的树
ArrayList<MenuVo> roots = new ArrayList<>();
// 将数据添加到 Map
Map<Long, MenuVo> map = list.stream().collect(Collectors.toMap(MenuVo::getId, Function.identity()));
list.forEach(entity -> {
MenuVo menu = map.get(entity.getPid());
if (null == menu) {
roots.add(map.get(entity.getId()));
} else {
if (menu.getChildren() == null) {
menu.setChildren(new ArrayList<>());
}
menu.getChildren().add(entity);
}
});
return roots;
}
/**
* 将权限集合转换成 Vo 集合 * 将权限集合转换成 Vo 集合
* @param permissions 权限集合 * @param permissions 权限集合
* @return vos * @return vos
...@@ -49,6 +88,7 @@ public class Permissions { ...@@ -49,6 +88,7 @@ public class Permissions {
.map(permission -> { .map(permission -> {
PermissionVo vo = new PermissionVo(); PermissionVo vo = new PermissionVo();
BeanUtils.copyProperties(permission, vo); BeanUtils.copyProperties(permission, vo);
vo.setMeta(permission.getMetaJson());
return vo; return vo;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
...@@ -82,6 +122,7 @@ public class Permissions { ...@@ -82,6 +122,7 @@ public class Permissions {
.map(permission -> { .map(permission -> {
PermissionVo vo = new PermissionVo(); PermissionVo vo = new PermissionVo();
BeanUtils.copyProperties(permission, vo); BeanUtils.copyProperties(permission, vo);
vo.setMeta(permission.getMetaJson());
vo.setChildren(toTree(permissions, permission.getId())); vo.setChildren(toTree(permissions, permission.getId()));
return vo; return vo;
}) })
......
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.auth.vo.auth; package com.yiring.auth.vo.auth;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable; import java.io.Serializable;
...@@ -25,6 +27,10 @@ public class LoginVo implements Serializable { ...@@ -25,6 +27,10 @@ public class LoginVo implements Serializable {
private static final long serialVersionUID = -8690942241103456896L; private static final long serialVersionUID = -8690942241103456896L;
@JsonSerialize(using = ToStringSerializer.class)
@ApiModelProperty(value = "主键", example = "1")
Long userId;
@ApiModelProperty(value = "token", example = "c68ca9c8c04b4a59afeafd2fb7c04741") @ApiModelProperty(value = "token", example = "c68ca9c8c04b4a59afeafd2fb7c04741")
String token; String token;
} }
/* (C) 2022 YiRing, Inc. */
package com.yiring.auth.vo.permission;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
import lombok.*;
import lombok.experimental.FieldDefaults;
/**
* 菜单输出类
* @author Jim
* @version 0.1
* 2022/3/25 17:09
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel("MenuVo")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MenuVo implements Serializable {
private static final long serialVersionUID = -9139328772148985141L;
@JsonIgnore
Long id;
@JsonIgnore
Long pid;
@ApiModelProperty(value = "名称", example = "Dashboard")
String name;
@ApiModelProperty(value = "路径", example = "/dashboard")
String path;
@ApiModelProperty(value = "重定向", example = "/dashboard/workbench")
String redirect;
@ApiModelProperty(value = "组件", example = "LAYOUT")
String component;
@ApiModelProperty(
value = "元数据",
example = "{\"title\": \"routes.dashboard.dashboard\", \"hideChildrenInMenu\": true}"
)
JSONObject meta;
@ApiModelProperty(value = "子权限")
List<MenuVo> children;
}
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.auth.vo.permission; package com.yiring.auth.vo.permission;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
...@@ -64,6 +65,9 @@ public class PermissionVo implements Serializable { ...@@ -64,6 +65,9 @@ public class PermissionVo implements Serializable {
@ApiModelProperty(value = "父级ID", example = "0") @ApiModelProperty(value = "父级ID", example = "0")
Long pid; Long pid;
@ApiModelProperty(value = "元数据", example = "{}")
JSONObject meta;
@ApiModelProperty(value = "子权限") @ApiModelProperty(value = "子权限")
List<PermissionVo> children; List<PermissionVo> children;
} }
...@@ -3,7 +3,6 @@ package com.yiring.auth.vo.user; ...@@ -3,7 +3,6 @@ package com.yiring.auth.vo.user;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.yiring.auth.vo.permission.PermissionVo;
import com.yiring.auth.vo.role.RoleVo; import com.yiring.auth.vo.role.RoleVo;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
...@@ -39,29 +38,16 @@ public class UserInfoVo implements Serializable { ...@@ -39,29 +38,16 @@ public class UserInfoVo implements Serializable {
@ApiModelProperty(value = "用户名", example = "admin") @ApiModelProperty(value = "用户名", example = "admin")
String username; String username;
@ApiModelProperty(value = "手机号", example = "13012345678") @ApiModelProperty(value = "介绍", example = "系统管理员")
String mobile; String desc;
@ApiModelProperty(value = "邮箱", example = "developer@yiring.com") @ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String email;
@ApiModelProperty(value = "职称", example = "系统管理员")
String title;
@ApiModelProperty(value = "头像", example = "http://img.ifzm.cn/cat.jpg")
String avatar; String avatar;
@ApiModelProperty("角色") @ApiModelProperty("角色")
@Builder.Default @Builder.Default
List<RoleVo> roles = new ArrayList<>(0); List<RoleVo> roles = new ArrayList<>(0);
@ApiModelProperty("权限") @ApiModelProperty(value = "用户主页", example = "/dashboard/workbench")
@Builder.Default
List<PermissionVo> permissions = new ArrayList<>(0);
/**
* 通常用于前端决定登录成功后跳转到哪个模块页面
*/
@ApiModelProperty(value = "主页地址", example = "/")
String homePath; String homePath;
} }
/* (C) 2022 YiRing, Inc. */
package com.yiring.auth.vo.user;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.yiring.auth.vo.role.RoleVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import lombok.*;
import lombok.experimental.FieldDefaults;
/**
* 用户信息
* @author ifzm
* 2022/03/03 10:35
**/
@ApiModel("UserInfo")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class UserMenuListVo implements Serializable {
private static final long serialVersionUID = -5319037883240327088L;
@JsonSerialize(using = ToStringSerializer.class)
@ApiModelProperty(value = "主键", example = "1")
Long userId;
@ApiModelProperty(value = "真实姓名", example = "超级用户")
String realName;
@ApiModelProperty(value = "用户名", example = "admin")
String username;
@ApiModelProperty(value = "介绍", example = "系统管理员")
String desc;
@ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar;
@ApiModelProperty("角色")
@Builder.Default
List<RoleVo> roles = new ArrayList<>(0);
@ApiModelProperty(value = "用户主页", example = "/dashboard/workbench")
String homePath;
}
...@@ -45,7 +45,7 @@ public class UserVo implements Serializable { ...@@ -45,7 +45,7 @@ public class UserVo implements Serializable {
@ApiModelProperty(value = "职称", example = "系统管理员") @ApiModelProperty(value = "职称", example = "系统管理员")
String title; String title;
@ApiModelProperty(value = "头像", example = "http://img.ifzm.cn/cat.jpg") @ApiModelProperty(value = "头像", example = "https://s1.ax1x.com/2022/03/30/qggJH0.jpg")
String avatar; String avatar;
@ApiModelProperty(value = "是否启用", example = "true") @ApiModelProperty(value = "是否启用", example = "true")
......
...@@ -70,7 +70,7 @@ public class AuthController { ...@@ -70,7 +70,7 @@ public class AuthController {
// 构建用户信息写入数据库 // 构建用户信息写入数据库
User user = User User user = User
.builder() .builder()
.title(param.getTitle()) .introduction(param.getIntroduction())
.avatar(param.getAvatar()) .avatar(param.getAvatar())
.mobile(param.getMobile()) .mobile(param.getMobile())
.realName(param.getRealName()) .realName(param.getRealName())
...@@ -120,7 +120,7 @@ public class AuthController { ...@@ -120,7 +120,7 @@ public class AuthController {
userRepository.saveAndFlush(user); userRepository.saveAndFlush(user);
// 构建用户所需信息 // 构建用户所需信息
LoginVo vo = LoginVo.builder().token(StpUtil.getTokenValue()).build(); LoginVo vo = LoginVo.builder().userId(user.getId()).token(StpUtil.getTokenValue()).build();
return Result.ok(vo); return Result.ok(vo);
} }
......
...@@ -2,21 +2,25 @@ ...@@ -2,21 +2,25 @@
package com.yiring.auth.web.user; package com.yiring.auth.web.user;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.yiring.auth.domain.permission.Permission;
import com.yiring.auth.domain.role.Role; import com.yiring.auth.domain.role.Role;
import com.yiring.auth.domain.role.RoleRepository; import com.yiring.auth.domain.role.RoleRepository;
import com.yiring.auth.domain.user.User; import com.yiring.auth.domain.user.User;
import com.yiring.auth.domain.user.UserRepository; import com.yiring.auth.domain.user.UserRepository;
import com.yiring.auth.param.IdsParam; import com.yiring.auth.param.IdsParam;
import com.yiring.auth.util.Permissions; import com.yiring.auth.util.Permissions;
import com.yiring.auth.vo.permission.MenuVo;
import com.yiring.auth.vo.user.UserInfoVo; import com.yiring.auth.vo.user.UserInfoVo;
import com.yiring.auth.vo.user.UserVo; import com.yiring.auth.vo.user.UserVo;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.core.Status; import com.yiring.common.core.Status;
import com.yiring.common.exception.FailStatusException;
import com.yiring.common.param.IdParam; import com.yiring.common.param.IdParam;
import com.yiring.common.param.PageParam; import com.yiring.common.param.PageParam;
import com.yiring.common.vo.PageVo; import com.yiring.common.vo.PageVo;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
...@@ -54,30 +58,43 @@ public class UserController { ...@@ -54,30 +58,43 @@ public class UserController {
RoleRepository roleRepository; RoleRepository roleRepository;
@ApiOperation(value = "获取登录用户信息") @ApiOperation(value = "获取登录用户信息")
@GetMapping("info") @GetMapping("getUserInfo")
public Result<UserInfoVo> info() { public Result<UserInfoVo> getUserInfo() {
Long id = StpUtil.getLoginIdAsLong(); User user = getLoginUser();
Optional<User> optional = userRepository.findById(id);
if (optional.isEmpty()) {
StpUtil.logout(id);
return Result.no(Status.UNAUTHORIZED);
}
User user = optional.get();
UserInfoVo userInfoVo = UserInfoVo UserInfoVo userInfoVo = UserInfoVo
.builder() .builder()
.userId(user.getId()) .userId(user.getId())
.username(user.getUsername())
.realName(user.getRealName()) .realName(user.getRealName())
.avatar(user.getAvatar()) .avatar(user.getAvatar())
.title(user.getTitle()) .desc(user.getIntroduction())
.roles(Permissions.toRoleVos(user.getRoles())) .roles(Permissions.toRoleVos(user.getRoles()))
.permissions(Permissions.toTree(Permissions.toPermissions(user.getRoles()), 0L))
// 默认跳转到用户看板
.homePath("/dashboard/workbench")
.build(); .build();
return Result.ok(userInfoVo); return Result.ok(userInfoVo);
} }
@ApiOperation(value = "获取用户菜单")
@GetMapping("getMenuList")
public Result<ArrayList<MenuVo>> getMenuList() {
User user = getLoginUser();
List<Permission> permissions = Permissions
.toPermissions(user.getRoles())
.stream()
.filter(permission -> !Permission.Type.BUTTON.equals(permission.getType()))
.collect(Collectors.toList());
List<MenuVo> vos = Permissions.toMenuTreeVo(permissions);
return Result.ok((ArrayList<MenuVo>) vos);
}
@ApiOperation(value = "获取用户权限")
@GetMapping("getPermCode")
public Result<ArrayList<String>> getPermCode() {
User user = getLoginUser();
List<Permission> permissions = Permissions.toPermissions(user.getRoles());
List<String> codes = permissions.stream().map(Permission::getUid).collect(Collectors.toList());
return Result.ok((ArrayList<String>) codes);
}
@ApiOperation(value = "分配角色") @ApiOperation(value = "分配角色")
@PostMapping("/manage/assign") @PostMapping("/manage/assign")
public Result<String> assign(@Valid IdParam idParam, @Valid IdsParam idsParam) { public Result<String> assign(@Valid IdParam idParam, @Valid IdsParam idsParam) {
...@@ -115,4 +132,19 @@ public class UserController { ...@@ -115,4 +132,19 @@ public class UserController {
PageVo<UserVo> vo = PageVo.build(data, page.getTotalElements()); PageVo<UserVo> vo = PageVo.build(data, page.getTotalElements());
return Result.ok(vo); return Result.ok(vo);
} }
/**
* 获取登录用户信息
* @return 用户信息
*/
private User getLoginUser() {
Long id = StpUtil.getLoginIdAsLong();
Optional<User> optional = userRepository.findById(id);
if (optional.isPresent()) {
return optional.get();
}
StpUtil.logout(id);
throw new FailStatusException(Status.UNAUTHORIZED);
}
} }
# Sa-Token配置 # Sa-Token配置
sa-token: sa-token:
# token名称 (同时也是cookie名称) # token名称 (同时也是cookie名称)
token-name: satoken token-name: Authorization
# token有效期,单位s 默认30天, -1代表永不过期 # token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000 timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
......
/*
Navicat Premium Data Transfer
Source Server : localhost_3306
Source Server Type : MySQL
Source Server Version : 80025
Source Host : 127.0.0.1:3306
Source Schema : basic_app
Target Server Type : MySQL
Target Server Version : 80025
File Encoding : 65001
Date: 30/03/2022 17:11:44
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` bigint NOT NULL,
`component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`enable` bit(1) NULL DEFAULT NULL,
`hidden` bit(1) NULL DEFAULT NULL,
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`meta` json NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`pid` bigint NULL DEFAULT NULL,
`serial` int NULL DEFAULT NULL,
`tree` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`uid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`redirect` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `UK_n5idwm5pmlgfwvau5vrwf65ai`(`uid`) USING BTREE,
UNIQUE INDEX `UKn5idwm5pmlgfwvau5vrwf65ai`(`uid`) USING BTREE,
INDEX `IDXfgtwjg4pmylcko0v6ate29j2x`(`type`) USING BTREE,
INDEX `IDXcj5qpen847f3vkcgy7lsmqyug`(`pid`) USING BTREE,
INDEX `IDXckpwmqmt2tsu8fyefxpgwypmr`(`tree`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES (1509081714683547648, 'LAYOUT', b'1', NULL, 'ion:grid-outline', '{\"title\": \"routes.dashboard.dashboard\", \"hideChildrenInMenu\": true}', 'Dashboard', '/dashboard', 0, 1, '0', 'MENU', 'Dashboard', '/dashboard/workbench');
INSERT INTO `sys_permission` VALUES (1509082383666647040, '/dashboard/workbench/index', b'1', b'1', NULL, '{\"title\": \"routes.dashboard.workbench\", \"hideBreadcrumb\": true, \"currentActiveMenu\": \"/dashboard\"}', 'Workbench', 'workbench', 1509081714683547648, NULL, '0.1509081714683547648', 'MENU', 'Workbench', NULL);
INSERT INTO `sys_permission` VALUES (1509082892188258304, 'LAYOUT', b'1', NULL, 'simple-icons:about-dot-me', '{\"title\": \"routes.dashboard.about\", \"hideChildrenInMenu\": true}', 'About', '/about', 0, 100000, '0', 'MENU', 'About', '/about/index');
INSERT INTO `sys_permission` VALUES (1509083350738931712, '/sys/about/index', b'1', NULL, 'simple-icons:about-dot-me', '{\"title\": \"routes.dashboard.about\", \"hideMenu\": true}', 'AboutPage', 'index', 1509082892188258304, NULL, '0.1509082892188258304', 'MENU', 'AboutPage', NULL);
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` bigint NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`uid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `UK_o928jdlxprqf5qyw6rvor4lse`(`uid`) USING BTREE,
UNIQUE INDEX `UKo928jdlxprqf5qyw6rvor4lse`(`uid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1509083443323998208, '管理员', 'admin');
-- ----------------------------
-- Table structure for sys_role_permissions
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permissions`;
CREATE TABLE `sys_role_permissions` (
`role_id` bigint NOT NULL,
`permissions_id` bigint NOT NULL,
PRIMARY KEY (`role_id`, `permissions_id`) USING BTREE,
INDEX `FKrg8k5oymm622ik067yjss31co`(`permissions_id`) USING BTREE,
CONSTRAINT `FKaa4k4qhr2qdj6br8p5nrb2lhb` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FKrg8k5oymm622ik067yjss31co` FOREIGN KEY (`permissions_id`) REFERENCES `sys_permission` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role_permissions
-- ----------------------------
INSERT INTO `sys_role_permissions` VALUES (1509083443323998208, 1509081714683547648);
INSERT INTO `sys_role_permissions` VALUES (1509083443323998208, 1509082383666647040);
INSERT INTO `sys_role_permissions` VALUES (1509083443323998208, 1509082892188258304);
INSERT INTO `sys_role_permissions` VALUES (1509083443323998208, 1509083350738931712);
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint NOT NULL,
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`create_time` datetime NULL DEFAULT NULL,
`deleted` bit(1) NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`enabled` bit(1) NULL DEFAULT NULL,
`introduction` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`last_login_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`last_login_time` datetime NULL DEFAULT NULL,
`mobile` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`real_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `UK_ahtq5ew3v0kt1n7hf1sgp7p8l`(`email`) USING BTREE,
UNIQUE INDEX `UK_cvv4commjv5h4bai0h4vuqvkd`(`mobile`) USING BTREE,
UNIQUE INDEX `UK_51bvuyvihefoh4kp5syh2jpi4`(`username`) USING BTREE,
UNIQUE INDEX `UKcvv4commjv5h4bai0h4vuqvkd`(`mobile`) USING BTREE,
UNIQUE INDEX `UK51bvuyvihefoh4kp5syh2jpi4`(`username`) USING BTREE,
UNIQUE INDEX `UKahtq5ew3v0kt1n7hf1sgp7p8l`(`email`) USING BTREE,
INDEX `IDXeyi36w6acltgl5j4ujw0wlsyt`(`enabled`) USING BTREE,
INDEX `IDXner7sjf2kjerehlptwipdlst9`(`deleted`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1509080860341571584, 'https://s1.ax1x.com/2022/03/30/qggJH0.jpg', '2022-03-30 16:11:29', b'0', NULL, b'1', '平台管理员', '0:0:0:0:0:0:0:1', '2022-03-30 16:55:40', '13012345678', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', '管理员', 'admin');
-- ----------------------------
-- Table structure for sys_user_roles
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_roles`;
CREATE TABLE `sys_user_roles` (
`user_id` bigint NOT NULL,
`role_id` bigint NOT NULL,
PRIMARY KEY (`role_id`, `user_id`) USING BTREE,
INDEX `FKp2804vh0ea810pitigxq5n6pn`(`user_id`) USING BTREE,
CONSTRAINT `FKp2804vh0ea810pitigxq5n6pn` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FKqwiuml6b7mjmk48u5b9hmk853` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user_roles
-- ----------------------------
INSERT INTO `sys_user_roles` VALUES (1509080860341571584, 1509083443323998208);
SET FOREIGN_KEY_CHECKS = 1;
...@@ -8,9 +8,11 @@ dependencies { ...@@ -8,9 +8,11 @@ dependencies {
// swagger annotations // swagger annotations
implementation "io.swagger:swagger-annotations:${swaggerAnnotationsVersion}" implementation "io.swagger:swagger-annotations:${swaggerAnnotationsVersion}"
implementation "org.hibernate.validator:hibernate-validator:${hibernateValidatorVersion}" implementation "org.hibernate.validator:hibernate-validator:${hibernateValidatorVersion}"
// hutool-extra // hutool-extra
implementation "cn.hutool:hutool-extra:${hutoolVersion}" implementation "cn.hutool:hutool-extra:${hutoolVersion}"
// hutool-json
implementation "cn.hutool:hutool-json:${hutoolVersion}" // fastjson
implementation "com.alibaba:fastjson:${fastJsonVersion}"
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
package com.yiring.common.aspect; package com.yiring.common.aspect;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.yiring.common.constant.DateFormatter; import com.yiring.common.constant.DateFormatter;
import com.yiring.common.core.Result; import com.yiring.common.core.Result;
import com.yiring.common.util.Commons; import com.yiring.common.util.Commons;
...@@ -52,8 +52,8 @@ public class RequestAspect { ...@@ -52,8 +52,8 @@ public class RequestAspect {
// Print Request Log (Optional Replace: MDC) // Print Request Log (Optional Replace: MDC)
String extra = ""; String extra = "";
if (Boolean.TRUE.equals(debug)) { if (Boolean.TRUE.equals(debug)) {
extra += String.format("\nHeaders: %s", new JSONObject(ServletUtil.getHeaderMap(request)).toStringPretty()); extra += String.format("\nHeaders: %s", JSONObject.toJSONString(ServletUtil.getHeaderMap(request), true));
extra += String.format("\nParams: %s", new JSONObject(ServletUtil.getParamMap(request)).toStringPretty()); extra += String.format("\nParams: %s", JSONObject.toJSONString(ServletUtil.getParamMap(request), true));
if (result instanceof Result) { if (result instanceof Result) {
extra += extra +=
String.format( String.format(
......
/* (C) 2022 YiRing, Inc. */
package com.yiring.common.exception;
import com.yiring.common.core.Status;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.FieldDefaults;
/**
* Status 异常(用于快速失败,会进行全局异常拦截)
*
* @author Jim
* @version 0.1
* 2022/3/28 11:36
*/
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class FailStatusException extends RuntimeException {
private static final long serialVersionUID = -4226669531686389671L;
/**
* 状态
*/
Status status;
}
/* (C) 2021 YiRing, Inc. */ /* (C) 2021 YiRing, Inc. */
package com.yiring.common.util; package com.yiring.common.util;
import java.util.Collection; import java.lang.reflect.Constructor;
import java.util.Map; import java.util.*;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
/** /**
* 公共工具类 * 公共工具类
...@@ -15,6 +17,7 @@ import lombok.experimental.UtilityClass; ...@@ -15,6 +17,7 @@ import lombok.experimental.UtilityClass;
*/ */
@SuppressWarnings({ "unused" }) @SuppressWarnings({ "unused" })
@Slf4j
@UtilityClass @UtilityClass
public class Commons { public class Commons {
...@@ -79,4 +82,63 @@ public class Commons { ...@@ -79,4 +82,63 @@ public class Commons {
public boolean isNullOrEmpty(Map<?, ?> map) { public boolean isNullOrEmpty(Map<?, ?> map) {
return map == null || map.isEmpty(); return map == null || map.isEmpty();
} }
/**
* 将集合通过 BeanUtils 反射转换成指定类型集合
* @param list 原始数据集合
* @param type 目标类型
* @param ignoreProperties 忽略属性
* @param <T> 目标类型集合
* @param <S> 原类型集合
* @return 目标集合
*/
public <T, S> List<T> transform(@NonNull List<S> list, Class<T> type, String... ignoreProperties) {
return transform(list, type, null, ignoreProperties);
}
/**
* 将集合通过 BeanUtils 反射转换成指定类型集合
* @param list 原始数据集合
* @param type 目标类型
* @param fn 自定义处理函数
* @param ignoreProperties 忽略属性
* @param <T> 目标类型集合
* @param <S> 原类型集合
* @return 目标集合
*/
public <T, S> List<T> transform(
@NonNull List<S> list,
Class<T> type,
CallbackFunction<S, T> fn,
String... ignoreProperties
) {
try {
Constructor<T> declaredConstructor = type.getDeclaredConstructor();
List<T> targets = new ArrayList<>();
for (S source : list) {
// 实例化
T target = declaredConstructor.newInstance();
// 使用 BeanUtils 进行数据拷贝
BeanUtils.copyProperties(source, target, ignoreProperties);
// 通过自定义实现补充转换
if (fn != null) {
fn.apply(source, target);
}
targets.add(target);
}
return targets;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@FunctionalInterface
public interface CallbackFunction<S, T> {
void apply(S s, T t);
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论