feat: role 接口开发

This commit is contained in:
zhangshuai
2024-08-28 09:14:17 +08:00
parent 16fc3a4bc2
commit 09622b5ed3
12 changed files with 394 additions and 25 deletions

View File

@@ -0,0 +1,101 @@
package net.geedge.asw.common.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import java.util.HashMap;
import java.util.Map;
/**
* 查询参数
*/
public class Query {
private Class clz;
public Class<? extends Object> getClz() {
return clz;
}
public void setClz(Class clz) {
this.clz = clz;
}
public Query(Class clz) {
this.clz = clz;
}
public Page getPage(Map<String, Object> params) {
return this.getPage(params, null, false);
}
public Page getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
//分页参数
long curPage = 1;
long limit = Constants.PAGESIZE;
if(params.get(Constants.PAGE) != null){
curPage = Long.parseLong((String)params.get(Constants.PAGE));
}
if(params.get(Constants.LIMIT) != null){
limit = Long.parseLong((String)params.get(Constants.LIMIT));
if(limit == -1){
limit = Long.MAX_VALUE;
curPage = 0;
}
}
//分页对象
Page page = new Page(curPage, limit);
//分页参数
params.put(Constants.PAGE, page);
//排序字段 orderBy=id
//防止SQL注入因为sidx、order是通过拼接SQL实现排序的会有SQL注入风险
String orderField = SQLFilter.sqlInject((String)params.get(Constants.ORDER));
if (StrUtil.isNotEmpty(orderField)) {
boolean matcheFlag = orderField.trim().matches("-?[a-zA-Z_.-]+");
if (!matcheFlag) {
throw new ASWException(RCode.ERROR);
}
// 获取表名
Class<?> clz = this.getClz();
String tableName = "";
if (clz != null) {
TableName table = this.getClz().getAnnotation(TableName.class);
tableName = table.value();
}
// 通过表名获取排序字段映射
Map<String, String> columnAliasMap = Constants.TABLE_NAME_ORDER_FIELD_MAPPING.get(tableName);
columnAliasMap = T.MapUtil.isEmpty(columnAliasMap) ? new HashMap<>():columnAliasMap;
if (orderField.startsWith("-")) {
orderField = orderField.substring(1, orderField.length());
orderField = columnAliasMap.get(orderField) != null ? columnAliasMap.get(orderField) : orderField;
return page.addOrder(OrderItem.desc(orderField));
} else {
orderField = columnAliasMap.get(orderField) != null ? columnAliasMap.get(orderField) : orderField;
return page.addOrder(OrderItem.asc(orderField));
}
}
// 默认排序
if (StrUtil.isNotEmpty(defaultOrderField)) {
if (isAsc) {
return page.addOrder(OrderItem.asc(defaultOrderField));
} else {
return page.addOrder(OrderItem.desc(defaultOrderField));
}
}
return page;
}
}

View File

@@ -0,0 +1,49 @@
/**
*
*/
package net.geedge.asw.common.config;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import java.util.regex.Pattern;
/**
* SQL过滤
*
* @author Mark sunlightcs@gmail.com
*/
public class SQLFilter {
private static String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";
private static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);
/**
* SQL注入过滤
* @param str 待验证的字符串
*/
public static String sqlInject(String str) {
if (T.StrUtil.isBlank(str)) {
return null;
}
//转换成小写
String str1 = str.toLowerCase();
String s = "";
if (str1.startsWith("-")) {
s = str1.substring(1);
} else {
s = str1;
}
if (sqlPattern.matcher(s).matches()) {
throw new ASWException(RCode.ERROR);
}
return str;
}
}

View File

@@ -1,7 +1,9 @@
package net.geedge.asw.common.util;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Constants {
@@ -26,4 +28,31 @@ public class Constants {
*/
public static final List<String> VISIBILITY_LIST = T.ListUtil.of("public", "private");
/**
* 当前页码
*/
public static final String PAGE = "current";
/**
* 每页显示记录数
*/
public static final String LIMIT = "size";
/**
* 每页显示条数
*/
public static final long PAGESIZE = 20;
/**
* 排序方式
*/
public static final String ORDER = "orderBy";
/**
* 表名 和 排序字段对应关系 KEY: tablename
*/
public static final Map<String, Map<String, String>> TABLE_NAME_ORDER_FIELD_MAPPING = T.MapUtil.newHashMap();
static {
Map<String, String> applicationOrderFieldMap = new HashMap<>();
TABLE_NAME_ORDER_FIELD_MAPPING.put("application", applicationOrderFieldMap);
}
}

View File

@@ -26,6 +26,8 @@ public enum RCode {
SYS_ACCESS_LEVEL_CANNOT_EMPTY(100014, "accessLevel cannot be empty"),
SYS_WORKSPACE_ROLES_CANNOT_EMPTY(100015, "workspaceRoles cannot be empty"),
SYS_USER_BUILT_IN(100016, "Built-in user are not allowed to delete or update"),
SYS_ROLE_BUILT_IN(100017, "Built-in role are not allowed to delete or update"),
SYS_ROLE_NOT_DELETE(100018, "Used role cannot be deleted"),
// Application

View File

@@ -1,6 +1,8 @@
package net.geedge.asw.module.sys.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.ASWException;
@@ -9,61 +11,90 @@ import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysRoleEntity;
import net.geedge.asw.module.sys.service.ISysRoleService;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/role")
public class SysRoleController {
private static final Log log = Log.get();
@Autowired
private ISysRoleService roleService;
@Autowired
private IWorkspaceMemberService workspaceMemberService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
SysRoleEntity entity = roleService.getById(id);
SysRoleEntity entity = roleService.detail(id);
return R.ok().putData("record", entity);
}
@GetMapping
public R list(String ids, String name,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "20") Integer size,
@RequestParam(defaultValue = "name") String orderBy) {
QueryWrapper<SysRoleEntity> queryWrapper = new QueryWrapper<SysRoleEntity>();
queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(","));
Page<SysRoleEntity> page = Page.of(current, size);
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
page = roleService.page(page, queryWrapper);
public R list(@RequestParam Map params) {
Page page = roleService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody SysRoleEntity entity) {
T.VerifyUtil.is(entity).notNull().and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY);
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY);
SysRoleEntity one = roleService.getOne(new QueryWrapper<SysRoleEntity>().lambda().eq(SysRoleEntity::getName, entity.getName()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setCreateTimestamp(T.DateUtil.current());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateTimestamp(T.DateUtil.current());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
roleService.saveOrUpdateRole(entity);
return R.ok().putData("id", entity.getId());
}
@PutMapping
public R update(@RequestBody SysRoleEntity entity) {
T.VerifyUtil.is(entity).notNull().and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY);
entity.setCreateTimestamp(null);
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY);
SysRoleEntity one = roleService.getOne(new QueryWrapper<SysRoleEntity>().lambda().eq(SysRoleEntity::getName, entity.getName()));
SysRoleEntity role = roleService.getById(entity.getId());
if (role.getBuildIn() == 1) {
throw ASWException.builder().rcode(RCode.SYS_ROLE_BUILT_IN).build();
}
if (T.ObjectUtil.isNotNull(one) && !one.getId().equals(entity.getId())) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setUpdateTimestamp(T.DateUtil.current());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
roleService.saveOrUpdateRole(entity);
return R.ok().putData("id", entity.getId());
}
@DeleteMapping
public R delete(String[] ids) {
public R delete(String ids) {
T.VerifyUtil.is(ids).notEmpty();
roleService.removeBatchByIds(T.ListUtil.of(ids));
log.info("delete Role, ids: {}", T.ArrayUtil.toString(ids));
List<String> idList = T.ListUtil.of(ids.split(","));
List<SysRoleEntity> roleList = roleService.list(new LambdaQueryWrapper<SysRoleEntity>().eq(SysRoleEntity::getBuildIn, 1).in(SysRoleEntity::getId, idList));
if (T.CollectionUtil.isNotEmpty(roleList)) {
throw ASWException.builder().rcode(RCode.SYS_ROLE_BUILT_IN).build();
}
List<WorkspaceMemberEntity> list = workspaceMemberService.list(new LambdaQueryWrapper<WorkspaceMemberEntity>().in(WorkspaceMemberEntity::getRoleId, idList));
if (T.CollectionUtil.isNotEmpty(list)) {
throw ASWException.builder().rcode(RCode.SYS_ROLE_NOT_DELETE).build();
}
roleService.delete(idList);
log.info("Delete Role, ids: {}", T.ArrayUtil.toString(idList));
return R.ok();
}

View File

@@ -1,7 +1,9 @@
package net.geedge.asw.module.sys.dao;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@@ -15,6 +17,8 @@ public interface SysRoleDao extends BaseMapper<SysRoleEntity> {
@Select("select sm.* from sys_menu sm LEFT JOIN sys_role_menu srm on sm.id = srm.menu_id LEFT JOIN sys_user_role sur on srm.role_id = sur.role_id where sur.role_id = #{roleId} and sm.state = 1 order by sm.order")
@Select("select sm.* from sys_menu sm LEFT JOIN sys_role_menu srm on sm.id = srm.menu_id where srm.role_id = #{roleId} and sm.state = 1 order by sm.order")
public List<SysMenuEntity> findMenuByRoleId(String roleId);
List<SysRoleEntity> queryList(Page page, Map params);
}

View File

@@ -15,15 +15,35 @@ public class SysRoleEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String i18n;
private String remark;
private Integer buildIn;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
@TableField(exist = false)
private String[] menuIds;
private Long createTimestamp;
@TableField(exist = false)
private List<SysMenuEntity> menus;
@TableField(exist = false)
private List<String> buttons;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
}

View File

@@ -1,10 +1,20 @@
package net.geedge.asw.module.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.sys.entity.SysRoleEntity;
import java.util.List;
import java.util.Map;
public interface ISysRoleService extends IService<SysRoleEntity>{
public void saveOrUpdateRole(SysRoleEntity entity);
void saveOrUpdateRole(SysRoleEntity entity);
SysRoleEntity detail(String id);
Page queryList(Map params);
void delete(List<String> idList);
}

View File

@@ -1,8 +1,17 @@
package net.geedge.asw.module.sys.service.impl;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.module.sys.entity.SysMenuEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -23,11 +32,17 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleDao, SysRoleEntity> i
@Autowired
private ISysRoleMenuService roleMenuService;
@Autowired
private ISysUserService userService;
@Autowired
private IWorkspaceMemberService workspaceMemberService;
@Override
@Transactional
public void saveOrUpdateRole(SysRoleEntity entity) {
// 删除 sys_role_menu 关联数据
if (T.StrUtil.isBlank(entity.getId())) {
if (!T.StrUtil.isBlank(entity.getId())) {
roleMenuService.remove(new QueryWrapper<SysRoleMenuEntity>().eq("role_id", entity.getId()));
}
// 保存 role
@@ -40,4 +55,50 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleDao, SysRoleEntity> i
roleMenuService.saveBatch(rmList);
}
@Override
public SysRoleEntity detail(String id) {
SysRoleEntity role = this.getById(id);
// 组织 button 数据
List<SysMenuEntity> menuList = this.getBaseMapper().findMenuByRoleId(id);
//生成 menu tree结构
Map<String, List<SysMenuEntity>> groupMap = menuList.stream()
.filter(menu -> !T.StrUtil.equalsIgnoreCase(menu.getPid(), "0"))
.collect(Collectors.groupingBy(SysMenuEntity::getPid));
menuList.forEach(menu -> {
menu.setChildren(groupMap.get(menu.getId()));
});
List<SysMenuEntity> collect = menuList.stream()
.filter(menu -> T.StrUtil.equals(menu.getPid(), "0"))
.filter(menu -> T.StrUtil.equals(menu.getType(), "menu"))
.collect(Collectors.toList());
role.setMenus(collect);
role.setCreateUser(userService.getById(role.getCreateUserId()));
role.setUpdateUser(userService.getById(role.getUpdateUserId()));
return role;
}
@Override
public Page queryList(Map params) {
params.put("orderBy", T.StrUtil.emptyToDefault(T.MapUtil.getStr(params, "orderBy"), "name"));
Page page = new Query(SysRoleEntity.class).getPage(params);
List<SysRoleEntity> roleList = this.getBaseMapper().queryList(page, params);
page.setRecords(roleList);
return page;
}
@Override
@Transactional()
public void delete(List<String> idList) {
// remove role
this.removeBatchByIds(idList);
// remove role menu 关联关系
roleMenuService.remove(new LambdaQueryWrapper<SysRoleMenuEntity>().in(SysRoleMenuEntity::getRoleId, idList));
// remove workspace member
workspaceMemberService.remove(new LambdaQueryWrapper<WorkspaceMemberEntity>().in(WorkspaceMemberEntity::getRoleId, idList));
}
}

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="net.geedge.asw.module.sys.dao.SysRoleDao">
<resultMap id="roleResult" type="net.geedge.asw.module.sys.entity.SysRoleEntity">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="i18n" column="i18n"/>
<result property="remark" column="remark"/>
<result property="buildIn" column="build_in"/>
<result property="createUserId" column="create_user_id"/>
<result property="updateUserId" column="update_user_id"/>
<result property="createTimestamp" column="create_timestamp"/>
<result property="updateTimestamp" column="update_timestamp"/>
<association property="createUser" columnPrefix="cu_" javaType="net.geedge.asw.module.sys.entity.SysUserEntity">
<id property="id" column="id"/>
<result property="name" column="name"/>
</association>
<association property="updateUser" columnPrefix="uu_" javaType="net.geedge.asw.module.sys.entity.SysUserEntity">
<id property="id" column="id"/>
<result property="name" column="name"/>
</association>
</resultMap>
<select id="queryList" resultMap="roleResult">
SELECT
sr.*,
cu.id as cu_id,
cu.name as cu_name,
uu.id as uu_id,
uu.id as uu_name
FROM
sys_role sr
left join sys_user cu on sr.create_user_id = cu.id
left join sys_user uu on sr.update_user_id = uu.id
<where>
<if test="params.ids != null and params.ids != ''">
sr.id in
<foreach item="id" collection="params.ids.split(',')" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="params.q != null and params.q != ''">
AND ( locate(#{params.q}, sr.name) OR locate(#{params.q}, sr.remark) )
</if>
</where>
<if test="params.orderBy == null or params.orderBy == ''">
ORDER BY sr.name
</if>
</select>
</mapper>

View File

@@ -117,5 +117,9 @@ INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (191, '201015', 'APP_ATTACHMENT_NOT_EXIST', '应用附件不存在', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (193, '201016', 'APP_PROPERTIES_FORMAT_ERROR', 'application properties format error', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (195, '201016', 'APP_PROPERTIES_FORMAT_ERROR', '应用属性格式错误', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (197, '100017', 'SYS_ROLE_BUILT_IN', 'Built-in role are not allowed to delete or update', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (199, '100017', 'SYS_ROLE_BUILT_IN', '内置权限不能删除或修改', 'zh', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (201, '100018', 'SYS_ROLE_NOT_DELETE', 'Used role cannot be delete', 'en', '', 'admin', 1724030366000);
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (203, '100018', 'SYS_ROLE_NOT_DELETE', '已使用权限不能删除', 'zh', '', 'admin', 1724030366000);
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -34,24 +34,27 @@ DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`i18n` varchar(255) NOT NULL,
`remark` varchar(255) NOT NULL,
`i18n` varchar(255) NOT NULL DEFAULT '',
`remark` varchar(255) NOT NULL DEFAULT '',
`build_in` int(10) NOT NULL DEFAULT 0,
`create_timestamp` bigint(20) NOT NULL,
`update_timestamp` bigint(20) NOT NULL,
`create_user_id` varchar(64) NOT NULL,
`update_user_id` varchar(64) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 添加内置角色
INSERT INTO `sys_role` (`id`, `name`, `i18n`, `remark`, `build_in`, `create_timestamp`) VALUES ('admin', 'admin', 'admin', 'admin', 1, UNIX_TIMESTAMP(NOW())*1000);
INSERT INTO `sys_role` (`id`, `name`, `i18n`, `remark`, `build_in`, `create_timestamp`) VALUES ('readonly', 'readonly', 'readonly', 'readonly', 1, UNIX_TIMESTAMP(NOW())*1000);
INSERT INTO `sys_role` (`id`, `name`, `i18n`, `remark`, `build_in`, `create_timestamp`, `update_timestamp`, `create_user_id`, `update_user_id`) VALUES ('admin', 'admin', 'admin', 'The system built-in administrator role', 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000, 'admin', 'admin');
INSERT INTO `sys_role` (`id`, `name`, `i18n`, `remark`, `build_in`, `create_timestamp`, `update_timestamp`, `create_user_id`, `update_user_id`) VALUES ('readonly', 'readonly', 'readonly', 'The system built-in common user role', 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000, 'admin', 'admin');
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`id` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`i18n` varchar(255) NOT NULL,
`i18n` varchar(255) NOT NULL DEFAULT '',
`pid` varchar(64) NOT NULL DEFAULT '',
`type` varchar(64) NOT NULL,
`perms` varchar(255) NOT NULL DEFAULT '',