发布 v2.5.0

This commit is contained in:
疯狂的狮子li
2021-07-12 11:36:37 +08:00
parent 7e3bbb4aab
commit 007a6d0c88
82 changed files with 2952 additions and 785 deletions

View File

@@ -2,7 +2,7 @@ package com.ruoyi.common.constant;
/**
* 用户常量信息
*
*
* @author ruoyi
*/
public class UserConstants
@@ -57,6 +57,9 @@ public class UserConstants
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验返回结果码 */
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";

View File

@@ -148,6 +148,10 @@ public class SysUser implements Serializable
@TableField(exist = false)
private Long[] postIds;
/** 角色ID */
@TableField(exist = false)
private Long roleId;
public SysUser(Long userId)
{
this.userId = userId;

View File

@@ -15,6 +15,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* mybatis-redis 二级缓存
*
* 使用方法 配置文件开启 mybatis-plus 二级缓存
* 在 XxxMapper.java 类上添加注解 @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
*
* @author Lion Li
*/
@Slf4j

View File

@@ -1,13 +1,17 @@
package com.ruoyi.common.core.mybatisplus.methods;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
/**
* 单sql批量插入
*
@@ -20,9 +24,28 @@ public class InsertAll extends AbstractMethod {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
KeyGenerator keyGenerator = new NoKeyGenerator();
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StrUtil.isNotBlank(tableInfo.getKeyProperty())) {
if (tableInfo.getIdType() == IdType.AUTO) {
/** 自增主键 */
keyGenerator = new Jdbc3KeyGenerator();
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null);
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, keyGenerator, keyProperty, keyColumn);
}
private String prepareFieldSql(TableInfo tableInfo) {

View File

@@ -205,9 +205,9 @@ public class RedisCache {
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
RListMultimap rListMultimap = redissonClient.getListMultimap(key);
return rListMultimap.getAll(hKeys);
public <K,V> Map<K,V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
RMap<K,V> rMap = redissonClient.getMap(key);
return rMap.getAll(hKeys);
}
/**

View File

@@ -88,7 +88,7 @@ public class DictUtils
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StrUtil.containsAny(separator, dictValue) && CollUtil.isNotEmpty(datas))
if (StrUtil.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
{
for (SysDictData dict : datas)
{
@@ -128,7 +128,7 @@ public class DictUtils
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StrUtil.containsAny(separator, dictLabel) && CollUtil.isNotEmpty(datas))
if (StrUtil.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
{
for (SysDictData dict : datas)
{

View File

@@ -0,0 +1,28 @@
package com.ruoyi.common.utils;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.Constants;
/**
* 字符串工具类
*
* @author ruoyi
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/** 空字符串 */
private static final String NULLSTR = "";
/** 下划线 */
private static final char SEPARATOR = '_';
/**
* 是否为http(s)://开头
*
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link) {
return StrUtil.startWithAny(link, Constants.HTTP, Constants.HTTPS);
}
}

View File

@@ -32,7 +32,7 @@ import java.util.stream.Collectors;
/**
* Excel相关处理
*
*
* @author ruoyi
*/
public class ExcelUtil<T>
@@ -88,12 +88,12 @@ public class ExcelUtil<T>
* 统计列表
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
* 数字格式
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/**
* 实体对象
*/
@@ -119,7 +119,7 @@ public class ExcelUtil<T>
/**
* 对excel表单默认第一个索引名转换成list
*
*
* @param is 输入流
* @return 转换后集合
*/
@@ -130,7 +130,7 @@ public class ExcelUtil<T>
/**
* 对excel表单指定表格索引名转换成list
*
*
* @param sheetName 表格索引名
* @param is 输入流
* @return 转换后集合
@@ -298,7 +298,7 @@ public class ExcelUtil<T>
/**
* 对list数据源将其里面的数据导入到excel表单
*
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
@@ -311,7 +311,7 @@ public class ExcelUtil<T>
/**
* 对list数据源将其里面的数据导入到excel表单
*
*
* @param sheetName 工作表的名称
* @return 结果
*/
@@ -323,7 +323,7 @@ public class ExcelUtil<T>
/**
* 对list数据源将其里面的数据导入到excel表单
*
*
* @return 结果
*/
public AjaxResult exportExcel()
@@ -391,7 +391,7 @@ public class ExcelUtil<T>
/**
* 填充excel数据
*
*
* @param index 序号
* @param row 单元格行
*/
@@ -418,7 +418,7 @@ public class ExcelUtil<T>
/**
* 创建表格样式
*
*
* @param wb 工作薄对象
* @return 样式列表
*/
@@ -456,7 +456,7 @@ public class ExcelUtil<T>
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
styles.put("header", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
@@ -500,7 +500,7 @@ public class ExcelUtil<T>
/**
* 设置单元格信息
*
*
* @param value 单元格值
* @param attr 注解相关
* @param cell 单元格信息
@@ -531,7 +531,7 @@ public class ExcelUtil<T>
}
}
}
/**
* 获取画布
*/
@@ -646,7 +646,7 @@ public class ExcelUtil<T>
/**
* 设置 POI XSSFSheet 单元格提示
*
*
* @param sheet 表单
* @param promptTitle 提示标题
* @param promptContent 提示内容
@@ -669,7 +669,7 @@ public class ExcelUtil<T>
/**
* 设置某些列的值只能输入预制的数据,显示下拉框.
*
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param firstRow 开始行
@@ -703,7 +703,7 @@ public class ExcelUtil<T>
/**
* 解析导出值 0=男,1=女,2=未知
*
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
@@ -716,7 +716,7 @@ public class ExcelUtil<T>
for (String item : convertSource)
{
String[] itemArray = item.split("=");
if (StrUtil.containsAny(separator, propertyValue))
if (StrUtil.containsAny(propertyValue, separator))
{
for (String value : propertyValue.split(separator))
{
@@ -740,7 +740,7 @@ public class ExcelUtil<T>
/**
* 反向解析值 男=0,女=1,未知=2
*
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
@@ -753,7 +753,7 @@ public class ExcelUtil<T>
for (String item : convertSource)
{
String[] itemArray = item.split("=");
if (StrUtil.containsAny(separator, propertyValue))
if (StrUtil.containsAny(propertyValue, separator))
{
for (String value : propertyValue.split(separator))
{
@@ -774,10 +774,10 @@ public class ExcelUtil<T>
}
return StrUtil.strip(propertyString.toString(), null,separator);
}
/**
* 解析字典值
*
*
* @param dictValue 字典值
* @param dictType 字典类型
* @param separator 分隔符
@@ -790,7 +790,7 @@ public class ExcelUtil<T>
/**
* 反向解析值字典值
*
*
* @param dictLabel 字典标签
* @param dictType 字典类型
* @param separator 分隔符
@@ -800,7 +800,7 @@ public class ExcelUtil<T>
{
return DictUtils.getDictValue(dictType, dictLabel, separator);
}
/**
* 合计统计信息
*/
@@ -837,7 +837,7 @@ public class ExcelUtil<T>
cell = row.createCell(0);
cell.setCellStyle(styles.get("total"));
cell.setCellValue("合计");
for (Integer key : keys)
{
cell = row.createCell(key);
@@ -859,7 +859,7 @@ public class ExcelUtil<T>
/**
* 获取下载路径
*
*
* @param filename 文件名称
*/
public String getAbsoluteFile(String filename)
@@ -875,7 +875,7 @@ public class ExcelUtil<T>
/**
* 获取bean中的属性值
*
*
* @param vo 实体对象
* @param field 字段
* @param excel 注解
@@ -906,7 +906,7 @@ public class ExcelUtil<T>
/**
* 以类的属性的get方法方法形式获取值
*
*
* @param o
* @param name
* @return value
@@ -955,7 +955,7 @@ public class ExcelUtil<T>
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
* 根据注解获取最大行高
*/
@@ -991,7 +991,7 @@ public class ExcelUtil<T>
/**
* 创建工作表
*
*
* @param sheetNo sheet数量
* @param index 序号
*/
@@ -1012,7 +1012,7 @@ public class ExcelUtil<T>
/**
* 获取单元格值
*
*
* @param row 获取的行
* @param column 获取单元格列号
* @return 单元格值
@@ -1069,4 +1069,4 @@ public class ExcelUtil<T>
}
return val;
}
}
}

View File

@@ -0,0 +1,70 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* spring-cache 演示案例
*
* @author Lion Li
*/
// 类级别 缓存统一配置
//@CacheConfig(cacheNames = "redissonCacheMap")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/demo/cache")
public class RedisCacheController {
/**
* 测试 @Cacheable
*
* 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
* 下一次调用该方法前,会去检查是否缓存中已经有值
* 如果有就直接返回,不调用方法
* 如果没有,就调用方法,然后把结果缓存起来
* 这个注解「一般用在查询方法上」
*
* cacheNames 为配置文件内 groupId
*/
@Cacheable(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
@GetMapping("/test1")
public AjaxResult<String> test1(String key, String value){
return AjaxResult.success("操作成功", value);
}
/**
* 测试 @CachePut
*
* 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
* 它「通常用在新增方法上」
*
* cacheNames 为 配置文件内 groupId
*/
@CachePut(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
@GetMapping("/test2")
public AjaxResult<String> test2(String key, String value){
return AjaxResult.success("操作成功", value);
}
/**
* 测试 @CacheEvict
*
* 使用了CacheEvict注解的方法,会清空指定缓存
* 「一般用在更新或者删除的方法上」
*
* cacheNames 为 配置文件内 groupId
*/
@CacheEvict(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
@GetMapping("/test3")
public AjaxResult<String> test3(String key, String value){
return AjaxResult.success("操作成功", value);
}
}

View File

@@ -7,7 +7,10 @@ import org.springframework.stereotype.Component;
/**
* feign测试fallback
* 自定义封装结构体熔断
* 需重写解码器 根据自定义实体 自行解析熔断
*
* @see {com.ruoyi.framework.config.FeignConfig#errorDecoder()}
* @author Lion Li
*/
@Slf4j

View File

@@ -1,63 +0,0 @@
package com.ruoyi.framework.config;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.spring5.ISpringTemplateEngine;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templateresolver.ITemplateResolver;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
* springboot-admin server配置类
*
* @author Lion Li
*/
@Configuration
@EnableAdminServer
public class AdminServerConfig {
@Lazy
@Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
/**
* 解决 admin 与 项目 页面的交叉引用 将 admin 的路由放到最后
* @param properties
* @param templateResolvers
* @param dialects
* @return
*/
@Bean
@ConditionalOnMissingBean(ISpringTemplateEngine.class)
SpringTemplateEngine templateEngine(ThymeleafProperties properties,
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
dialects.orderedStream().forEach(engine::addDialect);
Set<ITemplateResolver> templateResolvers1 = engine.getTemplateResolvers();
templateResolvers1 = templateResolvers1.stream()
.sorted(Comparator.comparing(ITemplateResolver::getOrder))
.collect(Collectors.toCollection(LinkedHashSet::new));
engine.setTemplateResolvers(templateResolvers1);
return engine;
}
}

View File

@@ -54,4 +54,40 @@ public class FeignConfig {
return new Retryer.Default();
}
// /**
// * 自定义异常解码器
// * 用于自定义返回体异常熔断
// */
// @Bean
// public ErrorDecoder errorDecoder() {
// return new CustomErrorDecoder();
// }
//
//
// /**
// * 自定义返回体解码器
// */
// @Slf4j
// public static class CustomErrorDecoder implements ErrorDecoder {
//
// @Override
// public Exception decode(String methodKey, Response response) {
// Exception exception = null;
// try {
// // 获取原始的返回内容
// String json = JsonUtils.toJsonString(response.body().asReader(StandardCharsets.UTF_8));
// exception = new RuntimeException(json);
// // 将返回内容反序列化为Result这里应根据自身项目作修改
// AjaxResult result = JsonUtils.parseObject(json, AjaxResult.class);
// // 业务异常抛出简单的 RuntimeException保留原来错误信息
// if (result.getCode() != 200) {
// exception = new RuntimeException(result.getMsg());
// }
// } catch (IOException e) {
// log.error(e.getMessage(), e);
// }
// return exception;
// }
// }
}

View File

@@ -19,6 +19,7 @@ import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -78,8 +79,13 @@ public class RedisConfig extends CachingConfigurerSupport {
*/
@Bean
public CacheManager cacheManager(RedissonClient redissonClient) {
List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
Map<String, CacheConfig> config = new HashMap<>();
config.put("redissonCacheMap", new CacheConfig(30*60*1000, 10*60*1000));
for (RedissonProperties.CacheGroup group : cacheGroup) {
CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
cacheConfig.setMaxSize(group.getMaxSize());
config.put(group.getGroupId(), cacheConfig);
}
return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
}

View File

@@ -3,7 +3,6 @@ package com.ruoyi.framework.config;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
@@ -57,9 +56,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
private CorsFilter corsFilter;
@Autowired
private AdminServerProperties adminServerProperties;
/**
* 解决 无法直接注入 AuthenticationManager
*
@@ -104,12 +100,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
.antMatchers("/login", "/captchaImage").anonymous()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js"
"/**/*.js",
"/profile/**"
).permitAll()
.antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous()
.antMatchers("/common/download/resource**").anonymous()
.antMatchers("/doc.html").anonymous()
@@ -117,9 +114,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/druid/**").anonymous()
// Spring Boot Admin Server 的安全配置
.antMatchers(adminServerProperties.getContextPath()).anonymous()
.antMatchers(adminServerProperties.getContextPath() + "/**").anonymous()
// Spring Boot Actuator 的安全配置
.antMatchers("/actuator").anonymous()
.antMatchers("/actuator/**").anonymous()

View File

@@ -2,11 +2,12 @@ package com.ruoyi.framework.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.client.codec.Codec;
import org.redisson.config.TransportMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Redisson 配置属性
*
@@ -37,6 +38,11 @@ public class RedissonProperties {
*/
private SingleServerConfig singleServerConfig;
/**
* 缓存组
*/
private List<CacheGroup> cacheGroup;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
@@ -98,4 +104,30 @@ public class RedissonProperties {
}
@Data
@NoArgsConstructor
public static class CacheGroup {
/**
* 组id
*/
private String groupId;
/**
* 组过期时间
*/
private long ttl;
/**
* 组最大空闲时间
*/
private long maxIdleTime;
/**
* 组最大长度
*/
private int maxSize;
}
}

View File

@@ -1,6 +1,8 @@
package com.ruoyi.framework.mybatisplus;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
@@ -8,6 +10,7 @@ import java.util.Date;
/**
* MP注入处理器
*
* @author Lion Li
* @date 2021/4/25
*/
@@ -15,30 +18,38 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//根据属性名字设置要填充的值
if (metaObject.hasGetter("createTime")) {
if (metaObject.getValue("createTime") == null) {
this.setFieldValByName("createTime", new Date(), metaObject);
try {
//根据属性名字设置要填充的值
if (metaObject.hasGetter("createTime")) {
if (metaObject.getValue("createTime") == null) {
this.setFieldValByName("createTime", new Date(), metaObject);
}
}
}
if (metaObject.hasGetter("createBy")) {
if (metaObject.getValue("createBy") == null) {
this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
if (metaObject.hasGetter("createBy")) {
if (metaObject.getValue("createBy") == null) {
this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
}
}
} catch (Exception e) {
throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
@Override
public void updateFill(MetaObject metaObject) {
if (metaObject.hasGetter("updateBy")) {
if (metaObject.getValue("updateBy") == null) {
this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
try {
if (metaObject.hasGetter("updateBy")) {
if (metaObject.getValue("updateBy") == null) {
this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
}
}
}
if (metaObject.hasGetter("updateTime")) {
if (metaObject.getValue("updateTime") == null) {
this.setFieldValByName("updateTime", new Date(), metaObject);
if (metaObject.hasGetter("updateTime")) {
if (metaObject.getValue("updateTime") == null) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
} catch (Exception e) {
throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}

View File

@@ -182,9 +182,9 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
for (GenTableColumn column : genTableColumns) {
GenUtils.initColumnField(column, table);
genTableColumnMapper.insert(column);
}
}
genTableColumnMapper.insertAll(genTableColumns);
}
}
} catch (Exception e) {
throw new CustomException("导入失败:" + e.getMessage());
@@ -258,7 +258,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
for (String template : templates) {
if (!StrUtil.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) {
if (!StrUtil.containsAny("sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm", template)) {
// 渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
@@ -290,9 +290,9 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
dbTableColumns.forEach(column -> {
if (!tableColumnNames.contains(column.getColumnName())) {
GenUtils.initColumnField(column, table);
genTableColumnMapper.insert(column);
}
});
}
});
genTableColumnMapper.insertAll(tableColumns);
List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
if (CollUtil.isNotEmpty(delColumns)) {

View File

@@ -1,6 +1,8 @@
package com.ruoyi.system.domain.vo;
import lombok.*;
import com.ruoyi.common.utils.StringUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
@@ -28,6 +30,11 @@ public class MetaVo {
*/
private boolean noCache;
/**
* 内链地址http(s)://开头)
*/
private String link;
public MetaVo(String title, String icon) {
this.title = title;
this.icon = icon;
@@ -39,4 +46,19 @@ public class MetaVo {
this.noCache = noCache;
}
public MetaVo(String title, String icon, String link) {
this.title = title;
this.icon = icon;
this.link = link;
}
public MetaVo(String title, String icon, boolean noCache, String link) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
if (StringUtils.ishttp(link)) {
this.link = link;
}
}
}

View File

@@ -24,6 +24,22 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser> {
*/
public List<SysUser> selectUserList(SysUser sysUser);
/**
* 根据条件分页查询未已配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
public Page<SysUser> selectAllocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
/**
* 根据条件分页查询未分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
public Page<SysUser> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param("user") SysUser user);
/**
* 通过用户名查询用户
*

View File

@@ -3,6 +3,8 @@ package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.system.domain.SysUserRole;
import java.util.List;
import java.util.Set;
@@ -26,7 +28,15 @@ public interface ISysRoleService extends IServicePlus<SysRole> {
public List<SysRole> selectRoleList(SysRole role);
/**
* 根据用户ID查询角色
* 根据用户ID查询角色列表
*
* @param userId 用户ID
* @return 角色列表
*/
public List<SysRole> selectRolesByUserId(Long userId);
/**
* 根据用户ID查询角色权限
*
* @param userId 用户ID
* @return 权限列表
@@ -134,4 +144,30 @@ public interface ISysRoleService extends IServicePlus<SysRole> {
* @return 结果
*/
public int deleteRoleByIds(Long[] roleIds);
/**
* 取消授权用户角色
*
* @param userRole 用户和角色关联信息
* @return 结果
*/
public int deleteAuthUser(SysUserRole userRole);
/**
* 批量取消授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要取消授权的用户数据ID
* @return 结果
*/
public int deleteAuthUsers(Long roleId, Long[] userIds);
/**
* 批量选择授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @return 结果
*/
public int insertAuthUsers(Long roleId, Long[] userIds);
}

View File

@@ -24,6 +24,22 @@ public interface ISysUserService extends IServicePlus<SysUser> {
*/
public List<SysUser> selectUserList(SysUser user);
/**
* 根据条件分页查询已分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
public TableDataInfo<SysUser> selectAllocatedList(SysUser user);
/**
* 根据条件分页查询未分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
public TableDataInfo<SysUser> selectUnallocatedList(SysUser user);
/**
* 通过用户名查询用户
*
@@ -103,6 +119,14 @@ public interface ISysUserService extends IServicePlus<SysUser> {
*/
public int updateUser(SysUser user);
/**
* 用户授权角色
*
* @param userId 用户ID
* @param roleIds 角色组
*/
public void insertUserAuth(Long userId, Long[] roleIds);
/**
* 修改用户状态
*
@@ -123,7 +147,7 @@ public interface ISysUserService extends IServicePlus<SysUser> {
* 修改用户头像
*
* @param userName 用户名
* @param avatar 头像地址
* @param avatar 头像地址
* @return 结果
*/
public boolean updateUserAvatar(String userName, String avatar);
@@ -164,9 +188,9 @@ public interface ISysUserService extends IServicePlus<SysUser> {
/**
* 导入用户数据
*
* @param userList 用户数据列表
* @param userList 用户数据列表
* @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
* @param operName 操作用户
* @param operName 操作用户
* @return 结果
*/
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);

View File

@@ -3,6 +3,7 @@ package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
@@ -10,6 +11,7 @@ import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysRoleMenu;
import com.ruoyi.system.domain.vo.MetaVo;
import com.ruoyi.system.domain.vo.RouterVo;
@@ -135,7 +137,7 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
router.setName(getRouteName(menu));
router.setPath(getRouterPath(menu));
router.setComponent(getComponent(menu));
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache())));
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
List<SysMenu> cMenus = menu.getChildren();
if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
router.setAlwaysShow(true);
@@ -148,7 +150,19 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
children.setPath(menu.getPath());
children.setComponent(menu.getComponent());
children.setName(StrUtil.upperFirst(menu.getPath()));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache())));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
childrenList.add(children);
router.setChildren(childrenList);
} else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
router.setMeta(null);
router.setPath("/inner");
List<RouterVo> childrenList = new ArrayList<RouterVo>();
RouterVo children = new RouterVo();
String routerPath = StringUtils.replaceEach(menu.getPath(), new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
children.setPath(routerPath);
children.setComponent(UserConstants.INNER_LINK);
children.setName(StringUtils.capitalize(routerPath));
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
childrenList.add(children);
router.setChildren(childrenList);
}
@@ -305,6 +319,10 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
*/
public String getRouterPath(SysMenu menu) {
String routerPath = menu.getPath();
// 内链打开外网方式
if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
routerPath = StringUtils.replaceEach(routerPath, new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
}
// 非外链并且是一级目录(类型为目录)
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
&& UserConstants.NO_FRAME.equals(menu.getIsFrame())) {
@@ -327,7 +345,9 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
String component = UserConstants.LAYOUT;
if (StrUtil.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
component = menu.getComponent();
} else if (StrUtil.isEmpty(menu.getComponent()) && isParentView(menu)) {
} else if (StrUtil.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
component = UserConstants.INNER_LINK;
} else if (StrUtil.isEmpty(menu.getComponent()) && isParentView(menu)) {
component = UserConstants.PARENT_VIEW;
}
return component;
@@ -344,6 +364,16 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
}
/**
* 是否为内链组件
*
* @param menu 菜单信息
* @return 结果
*/
public boolean isInnerLink(SysMenu menu) {
return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
}
/**
* 是否为parent_view组件
*
@@ -357,7 +387,7 @@ public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu>
/**
* 根据父节点的ID获取所有子节点
*
* @param list 分类表
* @param list 分类表
* @param parentId 传入的父节点ID
* @return String
*/

View File

@@ -59,6 +59,27 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole>
return baseMapper.selectRoleList(role);
}
/**
* 根据用户ID查询角色
*
* @param userId 用户ID
* @return 角色列表
*/
@Override
public List<SysRole> selectRolesByUserId(Long userId) {
List<SysRole> userRoles = baseMapper.selectRolePermissionByUserId(userId);
List<SysRole> roles = selectRoleAll();
for (SysRole role : roles) {
for (SysRole userRole : userRoles) {
if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) {
role.setFlag(true);
break;
}
}
}
return roles;
}
/**
* 根据用户ID查询权限
*
@@ -305,4 +326,51 @@ public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole>
roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, ids));
return baseMapper.deleteBatchIds(ids);
}
/**
* 取消授权用户角色
*
* @param userRole 用户和角色关联信息
* @return 结果
*/
@Override
public int deleteAuthUser(SysUserRole userRole) {
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getRoleId, userRole.getRoleId())
.eq(SysUserRole::getUserId, userRole.getUserId()));
}
/**
* 批量取消授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要取消授权的用户数据ID
* @return 结果
*/
@Override
public int deleteAuthUsers(Long roleId, Long[] userIds) {
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getRoleId, roleId)
.in(SysUserRole::getUserId, Arrays.asList(userIds)));
}
/**
* 批量选择授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @return 结果
*/
@Override
public int insertAuthUsers(Long roleId, Long[] userIds) {
// 新增用户与角色管理
List<SysUserRole> list = new ArrayList<SysUserRole>();
for (Long userId : userIds) {
SysUserRole ur = new SysUserRole();
ur.setUserId(userId);
ur.setRoleId(roleId);
list.add(ur);
}
return userRoleMapper.insertAll(list);
}
}

View File

@@ -69,6 +69,30 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
return baseMapper.selectUserList(user);
}
/**
* 根据条件分页查询已分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u", isUser = true)
public TableDataInfo<SysUser> selectAllocatedList(SysUser user) {
return PageUtils.buildDataInfo(baseMapper.selectAllocatedList(PageUtils.buildPage(), user));
}
/**
* 根据条件分页查询未分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u", isUser = true)
public TableDataInfo<SysUser> selectUnallocatedList(SysUser user) {
return PageUtils.buildDataInfo(baseMapper.selectUnallocatedList(PageUtils.buildPage(), user));
}
/**
* 通过用户名查询用户
*
@@ -231,6 +255,21 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
return baseMapper.updateById(user);
}
/**
* 用户授权角色
*
* @param userId 用户ID
* @param roleIds 角色组
*/
@Override
@Transactional
public void insertUserAuth(Long userId, Long[] roleIds)
{
userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getUserId, userId));
insertUserRole(userId, roleIds);
}
/**
* 修改用户状态
*
@@ -257,7 +296,7 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
* 修改用户头像
*
* @param userName 用户名
* @param avatar 头像地址
* @param avatar 头像地址
* @return 结果
*/
@Override
@@ -338,6 +377,28 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
}
}
/**
* 新增用户角色信息
*
* @param userId 用户ID
* @param roleIds 角色组
*/
public void insertUserRole(Long userId, Long[] roleIds) {
if (Validator.isNotNull(roleIds)) {
// 新增用户与角色管理
List<SysUserRole> list = new ArrayList<SysUserRole>();
for (Long roleId : roleIds) {
SysUserRole ur = new SysUserRole();
ur.setUserId(userId);
ur.setRoleId(roleId);
list.add(ur);
}
if (list.size() > 0) {
userRoleMapper.insertAll(list);
}
}
}
/**
* 通过用户ID删除用户
*
@@ -377,9 +438,9 @@ public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser>
/**
* 导入用户数据
*
* @param userList 用户数据列表
* @param userList 用户数据列表
* @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
* @param operName 操作用户
* @param operName 操作用户
* @return 结果
*/
@Override

View File

@@ -0,0 +1,29 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.config.RuoYiConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 首页
*
* @author ruoyi
*/
@RestController
public class SysIndexController
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/**
* 访问首页,提示语
*/
@RequestMapping("/")
public String index()
{
return StrUtil.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
}
}

View File

@@ -1,8 +1,6 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -11,6 +9,7 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -98,8 +97,7 @@ public class SysMenuController extends BaseController
{
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
&& !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
@@ -119,8 +117,7 @@ public class SysMenuController extends BaseController
{
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
&& !StrUtil.startWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}

View File

@@ -6,6 +6,7 @@ import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
@@ -14,6 +15,7 @@ import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -25,7 +27,7 @@ import java.util.List;
/**
* 角色信息
*
*
* @author ruoyi
*/
@RestController
@@ -171,4 +173,57 @@ public class SysRoleController extends BaseController
{
return AjaxResult.success(roleService.selectRoleAll());
}
/**
* 查询已分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/allocatedList")
public TableDataInfo allocatedList(SysUser user)
{
return userService.selectAllocatedList(user);
}
/**
* 查询未分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo unallocatedList(SysUser user)
{
return userService.selectUnallocatedList(user);
}
/**
* 取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
{
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
* 批量取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
{
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量选择用户授权
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
{
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
}

View File

@@ -196,4 +196,31 @@ public class SysUserController extends BaseController
user.setUpdateBy(SecurityUtils.getUsername());
return toAjax(userService.updateUserStatus(user));
}
/**
* 根据用户编号获取授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId)
{
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
Map<String, Object> ajax = new HashMap<>();
ajax.put("user", user);
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return AjaxResult.success(ajax);
}
/**
* 用户授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.insertUserAuth(userId, roleIds);
return success();
}
}