commit a817943dde1b48f017775d8c97c837cc6c7af832 Author: 齊 <23> Date: Tue Apr 21 16:45:14 2026 +0800 新建 diff --git a/application-tenant/pom.xml b/application-tenant/pom.xml new file mode 100644 index 0000000..35816f6 --- /dev/null +++ b/application-tenant/pom.xml @@ -0,0 +1,17 @@ + + + + apelet + apelet_saas + 1.0.0 + + 4.0.0 + + application-tenant + pom + + tenant-admin + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/pom.xml b/application-tenant/tenant-admin/pom.xml new file mode 100644 index 0000000..2f65d87 --- /dev/null +++ b/application-tenant/tenant-admin/pom.xml @@ -0,0 +1,152 @@ + + + + application-tenant + apelet + 1.0.0 + + 4.0.0 + + tenant-admin + 1.0.0 + tenant-admin + jar + + + + com.anji-plus + spring-boot-starter-captcha + ${ajcaptcha.version} + + + apelet + common-qy + 1.0.0 + + + apelet + common-redis + 1.0.0 + + + apelet + common-ext + 1.0.0 + + + apelet + common-dict + 1.0.0 + + + apelet + common-datafilter + 1.0.0 + + + apelet + common-mobile + 1.0.0 + + + apelet + common-core + 1.0.0 + + + apelet + common-log + 1.0.0 + + + apelet + common-datasync + 1.0.0 + + + apelet + common-aliyun-oss + 1.0.0 + + + + apelet + common-dbutil + 1.0.0 + + + apelet + common-swagger + 1.0.0 + + + + apelet + common-generator + 1.0.0 + + + + apelet + common-eas + 1.0.0 + + + apelet + common-online + 1.0.0 + + + com.baomidou + mybatis-plus-generator + ${mybatisplus.version} + + + cn.afterturn + easypoi-base + 4.4.0 + + + cn.afterturn + easypoi-web + 4.4.0 + + + cn.afterturn + easypoi-annotation + 4.4.0 + + + org.jetbrains + annotations + 24.0.1 + + + com.xuxueli + xxl-job-core + 2.3.1 + compile + + + org.springframework.boot + spring-boot-starter-quartz + + + org.quartz-scheduler + quartz + 2.3.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java new file mode 100644 index 0000000..3a0d02d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java @@ -0,0 +1,26 @@ +package apelet.tenantadmin; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 租户运营管理服务启动类。 + * + * @author guifc + * @date 2023-08-04 + */ +@ComponentScan("apelet") +@EnableScheduling +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +@MapperScan("apelet.common.**.dao") +public class TenantAdminApplication { + + public static void main(String[] args) { + SpringApplication.run(TenantAdminApplication.class, args); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java new file mode 100644 index 0000000..a709dce --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java @@ -0,0 +1,59 @@ +package apelet.tenantadmin.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 租户运营管理服务的配置类 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "application") +public class ApplicationConfig { + + /** + * token的Http Request Header的key + */ + private String tokenHeaderKey; + /** + * token在过期之前,但是已经需要被刷新时,response返回的header信息的key。 + */ + private String refreshedTokenHeaderKey; + /** + * token 加密用的密钥,该值的长度最少10个字符(过短会报错)。 + */ + private String tokenSigningKey; + /** + * 令牌的过期时间,单位毫秒 + */ + private Long expiration; + /** + * 用户密码被重置之后的缺省密码 + */ + private String defaultUserPassword; + /** + * 上传文件的基础目录 + */ + private String uploadFileBaseDir; + /** + * 授信ip列表,没有填写表示全部信任。多个ip之间逗号分隔,如: http://10.10.10.1:8080,http://10.10.10.2:8080 + */ + private String credentialIpList; + /** + * 发送租户的同步数据到消息队列的主题。 + */ + private String tenantSyncTopic; + /** + * Session会话和用户权限在Redis中的过期时间(秒)。 + * 缺省值是 one day + */ + private int sessionExpiredSeconds = 86400; + /** + * 是否排他登录。 + */ + private Boolean excludeLogin = false; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java new file mode 100644 index 0000000..2c97e0b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java @@ -0,0 +1,50 @@ +package apelet.tenantadmin.config; + +import apelet.common.core.constant.ApplicationConstant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 表示数据源类型的常量对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class DataSourceType { + + /** + * 租户管理数据源。 + */ + public static final int TENANT_ADMIN = ApplicationConstant.TENANT_ADMIN_DATASOURCE_TYPE; + /** + * 租户业务通用数据源,如全局编码字典、在线表单、流程和报表等数据。 + */ + public static final int TENANT_COMMON = ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE; + public static final int OPERATION_LOG = ApplicationConstant.OPERATION_LOG_DATASOURCE_TYPE; + public static final int GLOBAL_DICT = ApplicationConstant.COMMON_GLOBAL_DICT_TYPE; + + private static final Map TYPE_MAP = new HashMap<>(2); + static { + TYPE_MAP.put("tenant-admin", TENANT_ADMIN); + TYPE_MAP.put("tenant-common", TENANT_COMMON); + TYPE_MAP.put("operation-log", OPERATION_LOG); + TYPE_MAP.put("global-dict", GLOBAL_DICT); + } + + /** + * 根据名称获取字典类型。 + * + * @param name 数据源在配置中的名称。 + * @return 返回可用于多数据源切换的数据源类型。 + */ + public static Integer getDataSourceTypeByName(String name) { + return TYPE_MAP.get(name); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private DataSourceType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java new file mode 100644 index 0000000..43dd5ba --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java @@ -0,0 +1,61 @@ +package apelet.tenantadmin.config; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import javax.servlet.Filter; +import java.nio.charset.StandardCharsets; + +/** + * 这里主要配置Web的各种过滤器和监听器等Servlet容器组件。 + * + * @author guifc + * @date 2023-08-04 + */ +@Configuration +public class FilterConfig { + + /** + * 配置Ajax跨域过滤器。 + */ + @Bean + public CorsFilter corsFilterRegistration(ApplicationConfig applicationConfig) { + UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); + CorsConfiguration corsConfiguration = new CorsConfiguration(); + if (StringUtils.isNotBlank(applicationConfig.getCredentialIpList())) { + if ("*".equals(applicationConfig.getCredentialIpList())) { + corsConfiguration.addAllowedOriginPattern("*"); + } else { + String[] credentialIpList = StringUtils.split(applicationConfig.getCredentialIpList(), ","); + if (credentialIpList.length > 0) { + for (String ip : credentialIpList) { + corsConfiguration.addAllowedOrigin(ip); + } + } + } + corsConfiguration.addAllowedHeader("*"); + corsConfiguration.addAllowedMethod("*"); + corsConfiguration.addExposedHeader(applicationConfig.getRefreshedTokenHeaderKey()); + corsConfiguration.setAllowCredentials(true); + configSource.registerCorsConfiguration("/**", corsConfiguration); + } + return new CorsFilter(configSource); + } + + @Bean + public FilterRegistrationBean characterEncodingFilterRegistration() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( + new org.springframework.web.filter.CharacterEncodingFilter()); + filterRegistrationBean.addUrlPatterns("/*"); + filterRegistrationBean.addInitParameter("encoding", StandardCharsets.UTF_8.name()); + // forceEncoding强制response也被编码,另外即使request中已经设置encoding,forceEncoding也会重新设置 + filterRegistrationBean.addInitParameter("forceEncoding", "true"); + filterRegistrationBean.setAsyncSupported(true); + return filterRegistrationBean; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java new file mode 100644 index 0000000..0b1a0f1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java @@ -0,0 +1,24 @@ +package apelet.tenantadmin.config; + +import apelet.tenantadmin.interceptor.AuthenticationInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 所有的项目拦截器都在这里集中配置 + * + * @author guifc + * @date 2023-08-04 + */ +@Configuration +public class InterceptorConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new AuthenticationInterceptor()) + .addPathPatterns("/tenantadmin/**") + .addPathPatterns("/botp/**") + .addPathPatterns("/api/data-move/**"); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java new file mode 100644 index 0000000..2a39305 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java @@ -0,0 +1,66 @@ +package apelet.tenantadmin.config; + +import apelet.common.core.config.BaseMultiDataSourceConfig; +import apelet.common.core.config.DynamicDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * 多数据源配置对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Configuration +@EnableTransactionManagement +@MapperScan(value = {"apelet.tenantadmin.upms.dao","apelet.tenantadmin.tenant.dao", "apelet.common.*.dao"}) +public class MultiDataSourceConfig extends BaseMultiDataSourceConfig { + + @Bean(initMethod = "init", destroyMethod = "close") + @ConfigurationProperties(prefix = "spring.datasource.druid.tenant-admin") + public DataSource tenantAdminDataSource() { + return super.applyCommonProps(DruidDataSourceBuilder.create().build()); + } + + /** + * 默认生成的用于保存租户业务通用数据的数据源,如全局编码字典、在线表单、工作流和报表打印等基础数据。 + * 我们还是非常推荐使用独立的数据源,这样便于今后的数据迁移。 + */ + @Bean(initMethod = "init", destroyMethod = "close") + @ConfigurationProperties(prefix = "spring.datasource.druid.tenant-common") + public DataSource tenantCommonDataSource() { + return super.applyCommonProps(DruidDataSourceBuilder.create().build()); + } + + /** + * 默认生成的用于保存操作日志的数据源,可根据需求修改。 + * 我们还是非常推荐使用独立的数据源,这样便于今后的数据迁移。 + */ + @Bean(initMethod = "init", destroyMethod = "close") + @ConfigurationProperties(prefix = "spring.datasource.druid.operation-log") + public DataSource operationLogDataSource() { + return super.applyCommonProps(DruidDataSourceBuilder.create().build()); + } + + @Bean + @Primary + public DynamicDataSource dataSource() { + Map targetDataSources = new HashMap<>(3); + targetDataSources.put(DataSourceType.TENANT_ADMIN, tenantAdminDataSource()); + targetDataSources.put(DataSourceType.TENANT_COMMON, tenantCommonDataSource()); + targetDataSources.put(DataSourceType.OPERATION_LOG, operationLogDataSource()); + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + dynamicDataSource.setTargetDataSources(targetDataSources); + dynamicDataSource.setDefaultTargetDataSource(tenantAdminDataSource()); + return dynamicDataSource; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/interceptor/AuthenticationInterceptor.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/interceptor/AuthenticationInterceptor.java new file mode 100644 index 0000000..b40e457 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/interceptor/AuthenticationInterceptor.java @@ -0,0 +1,167 @@ +package apelet.tenantadmin.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.common.core.cache.CacheConfig; +import apelet.common.core.annotation.NoAuthInterface; +import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.object.TokenData; +import apelet.common.core.util.ApplicationContextHolder; +import apelet.common.core.util.JwtUtil; +import apelet.common.core.util.RedisKeyUtil; +import io.jsonwebtoken.Claims; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RBucket; +import org.redisson.api.RSet; +import org.redisson.api.RedissonClient; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.util.Assert; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 登录用户Token验证、生成和权限验证的拦截器。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +public class AuthenticationInterceptor implements HandlerInterceptor { + + private final ApplicationConfig appConfig = + ApplicationContextHolder.getBean("applicationConfig"); + + private final RedissonClient redissonClient = ApplicationContextHolder.getBean(RedissonClient.class); + + private final CacheManager cacheManager = ApplicationContextHolder.getBean("caffeineCacheManager"); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String url = request.getRequestURI(); + String token = this.getTokenFromRequest(request); + boolean noLoginUrl = this.isNoAuthInterface(handler); + // 如果接口方法标记NoAuthInterface注解,可以直接跳过Token鉴权验证,这里主要为了测试接口方便 + if (noLoginUrl && StrUtil.isBlank(token)) { + return true; + } + Claims c = JwtUtil.parseToken(token, appConfig.getTokenSigningKey()); + if (JwtUtil.isNullOrExpired(c)) { + // 如果免登陆接口携带的是过期的Token,这个时候直接返回给Controller即可。 + // 这样可以规避不必要的重新登录,而对于Controller,可以将本次请求视为未登录用户的请求。 + if (noLoginUrl) { + return true; + } + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + this.outputResponseMessage(response, + ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, "用户会话已过期或尚未登录,请重新登录!")); + return false; + } + String sessionId = (String) c.get("sessionId"); + String sessionIdKey = RedisKeyUtil.makeSessionIdKey(sessionId); + RBucket sessionData = redissonClient.getBucket(sessionIdKey); + TokenData tokenData = null; + if (sessionData.isExists()) { + tokenData = JSON.parseObject(sessionData.get(), TokenData.class); + } + if (tokenData == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + this.outputResponseMessage(response, + ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, "用户会话已失效,请重新登录!")); + return false; + } + tokenData.setToken(token); + TokenData.addToRequest(tokenData); + // 如果url是免登陆、白名单中,则不需要进行鉴权操作 + if (!noLoginUrl && Boolean.FALSE.equals(tokenData.getIsAdmin()) && !this.hasPermission(sessionId, url)) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + this.outputResponseMessage(response, ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION)); + return false; + } + if (JwtUtil.needToRefresh(c)) { + Map newClains = new HashMap<>(c.size()); + newClains.putAll(c); + String refreshedToken = JwtUtil.generateToken(newClains, appConfig.getExpiration(), appConfig.getTokenSigningKey()); + response.addHeader(appConfig.getRefreshedTokenHeaderKey(), refreshedToken); + } + return true; + } + + @SuppressWarnings("unchecked") + private boolean hasPermission(String sessionId, String url) { + // 为了提升效率,先检索Caffeine的一级缓存,如果不存在,再检索Redis的二级缓存,并将结果存入一级缓存。 + Set localPermSet; + String permKey = RedisKeyUtil.makeSessionPermIdKey(sessionId); + Cache cache = cacheManager.getCache(CacheConfig.CacheEnum.USER_PERMISSION_CACHE.name()); + Assert.notNull(cache, "Cache USER_PERMISSION_CACHE can't be NULL."); + Cache.ValueWrapper wrapper = cache.get(permKey); + if (wrapper == null) { + RSet permSet = redissonClient.getSet(permKey); + localPermSet = permSet.readAll(); + cache.put(permKey, localPermSet); + } else { + localPermSet = (Set) wrapper.get(); + } + return CollUtil.contains(localPermSet, url); + } + + private boolean isNoAuthInterface(Object handler) { + if (handler instanceof HandlerMethod) { + HandlerMethod hm = (HandlerMethod) handler; + return hm.getBeanType().getAnnotation(NoAuthInterface.class) != null + || hm.getMethodAnnotation(NoAuthInterface.class) != null; + } + return false; + } + + private String getTokenFromRequest(HttpServletRequest request) { + String token = request.getHeader(appConfig.getTokenHeaderKey()); + if (StrUtil.isBlank(token)) { + token = request.getParameter(appConfig.getTokenHeaderKey()); + } + if (StrUtil.isBlank(token)) { + token = request.getHeader(ApplicationConstant.HTTP_HEADER_INTERNAL_TOKEN); + } + return token; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, + ModelAndView modelAndView) throws Exception { + // 这里需要空注解,否则sonar会不happy。 + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + throws Exception { + // 这里需要空注解,否则sonar会不happy。 + } + + private void outputResponseMessage(HttpServletResponse response, ResponseResult respObj) { + PrintWriter out; + try { + out = response.getWriter(); + } catch (IOException e) { + log.error("Failed to call OutputResponseMessage.", e); + return; + } + response.setContentType("application/json; charset=utf-8"); + out.print(JSON.toJSONString(respObj)); + out.flush(); + out.close(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/Enum/FunctionTypeEnum.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/Enum/FunctionTypeEnum.java new file mode 100644 index 0000000..aeaa9e5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/Enum/FunctionTypeEnum.java @@ -0,0 +1,37 @@ +package apelet.tenantadmin.tenant.Enum; + +/** + * 参数类型 + */ +public enum FunctionTypeEnum { + + VARCHAR(1, "字符串"), + INT(2, "数值"), + DATE(3, "日期"), + LONG(4, "整型"), + OBJECT(5, "对象"), + VARCHAR_ARRAY(6, "字符串数组"), + INT_ARRAY(7, "数值数组"), + DATE_ARRAY(8, "日期数组"), + LONG_ARRAY(9, "整型数组"), + OBJECT_ARRAY(10, "对象数组"); + + + + + private final Integer code; + private final String description; + + FunctionTypeEnum(Integer code, String description) { + this.code = code; + this.description = description; + } + + public Integer getCode() { + return code; + } + + public String getDescription() { + return description; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DeployPluginController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DeployPluginController.java new file mode 100644 index 0000000..bf886f5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DeployPluginController.java @@ -0,0 +1,252 @@ +package apelet.tenantadmin.tenant.controller; + +import apelet.common.aliyun.oss.util.AliyunOssUpDownloader; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.upload.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.online.config.OnlineProperties; +import apelet.common.redis.cache.SessionCacheHelper; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.model.DeployPlugin; +import apelet.tenantadmin.tenant.plugin.PluginService; +import apelet.tenantadmin.tenant.service.IDeployPluginService; +import apelet.tenantadmin.upms.model.SysUser; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.BooleanUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.github.pagehelper.page.PageMethod; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.servlet.http.HttpServletResponse; +import javax.validation.groups.Default; +import java.io.*; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + *

+ * 热部署插件表 前端控制器 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +@RestController +@RequestMapping("/tenantadmin/tenant/deployPlugin") +public class DeployPluginController { + + @Autowired + private IDeployPluginService deployPluginService; + + @Autowired + PluginService pluginService; + + @Autowired + private ApplicationConfig appConfig; + @Autowired + private SessionCacheHelper cacheHelper; + @Autowired + private UpDownloaderFactory upDownloaderFactory; + @Autowired + LocalUpDownloader localUpDownloader; + @Autowired + private OnlineProperties onlineProperties; + // @Autowired + // AliyunOssUpDownloader aliyunOssUpDownloader; + + + /** + * 列表接口 + * + * @param DeployPlugin + * @param orderParam + * @param pageParam + * @return + */ + @PostMapping(value = "/list") + public ResponseResult list(@MyRequestBody DeployPlugin DeployPlugin, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + List deployPluginList = deployPluginService.getDeployPluginList( + DeployPlugin, MyOrderParam.buildOrderBy(orderParam, DeployPlugin.class) + ); + return ResponseResult.success(MyPageUtil.makeResponseData(deployPluginList, DeployPlugin.INSTANCE)); + } + + + /** + * 新增 + * + * @param deployPlugin + * @return + */ + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@RequestBody DeployPlugin deployPlugin) { + deployPlugin.setCreateTime(new Date()); + String errorMessage = MyCommonUtil.getModelValidationError(deployPlugin); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + deployPlugin.setSubmitTime(new Date()); + deployPluginService.save(deployPlugin); + return ResponseResult.success(deployPlugin); + } + + /** + * 修改 + * + * @param deployPlugin + * @return + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@RequestBody DeployPlugin deployPlugin) { + String errorMessage = MyCommonUtil.getModelValidationError(deployPlugin, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!deployPluginService.updateById(deployPlugin)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + + /** + * 删除 + * + * @param id + * @return + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@RequestBody Long id) { + if (MyCommonUtil.existBlankArgument(id)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!deployPluginService.removeById(id)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + + @PostMapping("/reloadPlugin") + public ResponseResult reloadPlugin(@MyRequestBody List ids) { + List pluginNameList = deployPluginService.reloadPlugin(ids); + return ResponseResult.success(pluginNameList); + } + + + @PostMapping("/executePlugin") + public ResponseResult executePlugin(@RequestBody String s) { + JSONObject jsonObject = JSON.parseObject(s); + String pluginName = jsonObject.getString("pluginName"); + Map params = (Map) jsonObject.get("pluginData"); + Object value = deployPluginService.executePlugin(pluginName, params); + return ResponseResult.success(value); + } + + + /** + * 文件上传操作。 + * + * @param fieldName 上传文件名。 + * @param asImage 是否作为图片上传。如果是图片,今后下载的时候无需权限验证。否则就是附件上传,下载时需要权限验证。 + * @param uploadFile 上传文件对象。 + */ + @OperationLog(type = SysOperationLogType.UPLOAD, saveResponse = false) + @PostMapping("/upload") + public void upload( + @RequestParam String fieldName, + @RequestParam Boolean asImage, + @RequestParam("file") MultipartFile uploadFile) throws Exception { + InputStream inputStream = uploadFile.getInputStream(); + // 生成密钥 + SecretKey secretKey = generateKey(); + + // 加密数据 + byte[] encryptedData = encrypt(inputStream, secretKey); + + // 将加密后的数据转换为InputStream + InputStream encryptedInputStream = new ByteArrayInputStream(encryptedData); + + // 解密数据 + InputStream decryptedInputStream = decrypt(encryptedInputStream, secretKey); + + UploadStoreTypeEnum uploadStoreType = UploadStoreTypeEnum.values()[onlineProperties.getDistributeStoreType()]; + BaseUpDownloader upDownloader = upDownloaderFactory.get(uploadStoreType); + + UploadResponseInfo responseInfo = upDownloader.doUpload( + "", + appConfig.getUploadFileBaseDir(), + DeployPlugin.class.getSimpleName(), + "", + false, + uploadFile); + if (Boolean.TRUE.equals(responseInfo.getUploadFailed())) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, + ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage())); + return; + } + cacheHelper.putSessionUploadFile(responseInfo.getFilename()); + ResponseResult.output(ResponseResult.success(responseInfo)); + } + + + private static SecretKey generateKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(128); + return keyGenerator.generateKey(); + } + + private static byte[] encrypt(InputStream inputStream, SecretKey secretKey) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(cipher.update(buffer, 0, bytesRead)); + } + outputStream.write(cipher.doFinal()); + return outputStream.toByteArray(); + } + + private static InputStream decrypt(InputStream encryptedInputStream, SecretKey secretKey) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = encryptedInputStream.read(buffer)) != -1) { + outputStream.write(cipher.update(buffer, 0, bytesRead)); + } + outputStream.write(cipher.doFinal()); + return new ByteArrayInputStream(outputStream.toByteArray()); + } + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysMessageController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysMessageController.java new file mode 100644 index 0000000..cc3cb24 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysMessageController.java @@ -0,0 +1,58 @@ +package apelet.tenantadmin.tenant.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.object.*; + +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.online.service.SysMessageService; +import com.github.pagehelper.page.PageMethod; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + + +@RestController +@RequestMapping("/tenantadmin/tenant/sysMessage") +public class SysMessageController { + @Autowired + private SysMessageService sysMessageService; + /** + * 消息列表接口 + * + * @param + * @param pageParam + * @return + */ + @PostMapping(value = "/list") + public ResponseResult list( + @MyRequestBody MyPageParam pageParam, + @MyRequestBody(required =false) String readStatus) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + Map pammap= new HashMap(); + if(readStatus!=null){ + pammap.put("readStatus",readStatus); + } + TokenData tokenData = TokenData.takeFromRequest(); + pammap.put("receiveUserId",tokenData.getUserId()); + MyPageData> list = sysMessageService.getUserMessagelist(pammap,pageParam); + return ResponseResult.success(list); + } + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody String entryId){ + Map map = new HashMap(); + map.put("entryId",entryId); + sysMessageService.updateMessageStatus(map); + return ResponseResult.success(); + } + + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantController.java new file mode 100644 index 0000000..6fa51f8 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantController.java @@ -0,0 +1,195 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import apelet.common.core.util.MyModelUtil; +import apelet.tenantadmin.tenant.dto.SysTenantDto; +import apelet.tenantadmin.tenant.model.SysTenant; +import apelet.tenantadmin.tenant.service.SysTenantService; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.vo.SysTenantVo; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Map; + +/** + * 租户管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenant") +public class SysTenantController { + + @Autowired + private SysTenantService sysTenantService; + + /** + * 新增租户操作。 + * + * @param sysTenantDto 新增租户对象。 + * @return 应答结果对象,包含新增租户的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantDto.tenantId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysTenantDto sysTenantDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenant sysTenant = MyModelUtil.copyTo(sysTenantDto, SysTenant.class); + sysTenantService.saveNew(sysTenant); + return ResponseResult.success(sysTenant.getTenantId()); + } + + /** + * 更新租户操作。 + * + * @param sysTenantDto 更新租户对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysTenantDto sysTenantDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenant originalSysTenant = sysTenantService.getById(sysTenantDto.getTenantId()); + if (originalSysTenant == null) { + errorMessage = "数据验证失败,当前租户并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenant sysTenant = MyModelUtil.copyTo(sysTenantDto, SysTenant.class); + if (!sysTenantService.update(sysTenant, originalSysTenant)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户操作。 + * + * @param tenantId 指定租户主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long tenantId) { + if (MyCommonUtil.existBlankArgument(tenantId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + SysTenant tenant = sysTenantService.getById(tenantId); + if (tenant == null) { + errorMessage = "数据验证失败,当前租户并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!sysTenantService.remove(tenant)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 查看租户列表。 + * + * @param sysTenantDtoFilter 租户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含租户列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysTenantDto sysTenantDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenant sysTenantFilter = MyModelUtil.copyTo(sysTenantDtoFilter, SysTenant.class); + List tenantList = sysTenantService.getSysTenantList( + sysTenantFilter, MyOrderParam.buildOrderBy(orderParam, SysTenant.class)); + return ResponseResult.success(MyPageUtil.makeResponseData(tenantList, SysTenant.INSTANCE)); + } + + /** + * 查看租户详情。 + * + * @param tenantId 指定租户主键Id。 + * @return 应答结果对象,包含租户详情对象。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long tenantId) { + if (MyCommonUtil.existBlankArgument(tenantId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysTenant sysTenant = sysTenantService.getByIdWithRelation(tenantId, MyRelationParam.normal()); + if (sysTenant == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantVo sysTenantVo = SysTenant.INSTANCE.fromModel(sysTenant); + return ResponseResult.success(sysTenantVo); + } + + /** + * 查询租户的权限资源列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param url url过滤条件。 + * @return 应答对象,包含从租户到权限资源的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysTenantPermWithDetail") + public ResponseResult>> listSysTenantPermWithDetail(Long tenantId, String url) { + if (MyCommonUtil.isBlankOrNull(tenantId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysTenantService.getSysTenantPermListWithDetail(tenantId, url)); + } + + /** + * 查询租户的权限字列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param permCode 权限字称过滤条件。 + * @return 应答对象,包含从租户到权限字的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysTenantPermCodeWithDetail") + public ResponseResult>> listSysTenantPermCodeWithDetail(Long tenantId, String permCode) { + if (MyCommonUtil.isBlankOrNull(tenantId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysTenantService.getSysTenantPermCodeListWithDetail(tenantId, permCode)); + } + + /** + * 查询租户的菜单列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 应答对象,包含从租户到菜单的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysTenantMenuWithDetail") + public ResponseResult>> listSysTenantMenuWithDetail(Long tenantId, String menuName) { + if (MyCommonUtil.isBlankOrNull(tenantId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysTenantService.getSysTenantMenuListWithDetail(tenantId, menuName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantDatasourceController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantDatasourceController.java new file mode 100644 index 0000000..5d0656c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantDatasourceController.java @@ -0,0 +1,204 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import cn.hutool.core.util.StrUtil; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.annotation.NoAuthInterface; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.exception.MyRuntimeException; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.dbutil.util.DataSourceUtil; +import apelet.common.dbutil.constant.DblinkType; +import apelet.tenantadmin.tenant.dto.SysTenantDatasourceDto; +import apelet.tenantadmin.tenant.dto.SysTenantPermDto; +import apelet.tenantadmin.tenant.model.SysTenant; +import apelet.tenantadmin.tenant.model.SysTenantDatasource; +import apelet.tenantadmin.tenant.service.SysTenantDatasourceService; +import apelet.tenantadmin.tenant.service.SysTenantService; +import apelet.tenantadmin.tenant.vo.SysTenantDatasourceVo; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Map; + +/** + * 租户数据源管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户数据源管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantDatasource") +public class SysTenantDatasourceController { + + @Autowired + private SysTenantDatasourceService sysTenantDatasourceService; + @Autowired + private SysTenantService sysTenantService; + @Autowired + private RedissonClient redissonClient; + + /** + * 测试链接。 + * + * @param sysTenantDatasourceDto 数据源参数对象。 + * @return 测试结果应答。 + */ + @PostMapping("/testConnection") + public ResponseResult testConnection(@MyRequestBody SysTenantDatasourceDto sysTenantDatasourceDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantDatasourceDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + try { + DataSourceUtil.testConnection( + DblinkType.MYSQL, + sysTenantDatasourceDto.getDatabaseHost(), + sysTenantDatasourceDto.getPort(), + sysTenantDatasourceDto.getSchemaName(), + sysTenantDatasourceDto.getDatabaseName(), + sysTenantDatasourceDto.getUsername(), + sysTenantDatasourceDto.getPassword()); + } catch (MyRuntimeException e) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, e.getMessage()); + } + return ResponseResult.success(); + } + + /** + * 新增租户数据源操作。 + * + * @param sysTenantDatasourceDto 新增租户数据源对象。 + * @return 应答结果对象,包含新租户数据源的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantDatasourceDto.datasourceId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysTenantDatasourceDto sysTenantDatasourceDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantDatasourceDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantDatasource sysTenantDatasource = MyModelUtil.copyTo(sysTenantDatasourceDto, SysTenantDatasource.class); + sysTenantDatasourceService.saveNew(sysTenantDatasource); + return ResponseResult.success(sysTenantDatasource.getDatasourceId()); + } + + /** + * 更新租户数据源操作。 + * + * @param sysTenantDatasourceDto 更新租户数据源对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysTenantDatasourceDto sysTenantDatasourceDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantDatasourceDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantDatasource originalSysTenantDatasource = + sysTenantDatasourceService.getById(sysTenantDatasourceDto.getDatasourceId()); + if (originalSysTenantDatasource == null) { + errorMessage = "数据验证失败,当前租户数据源并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenantDatasource sysTenantDatasource = MyModelUtil.copyTo(sysTenantDatasourceDto, SysTenantDatasource.class); + if (!sysTenantDatasource.getDatasourceType().equals(originalSysTenantDatasource.getDatasourceType())) { + errorMessage = "数据验证失败,多数据源类型值不能修改!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + String password = sysTenantDatasource.getPassword(); + if (StrUtil.isNotBlank(password) && StrUtil.isAllCharMatch(password, c -> '*' == c)) { + sysTenantDatasource.setPassword(originalSysTenantDatasource.getPassword()); + } + if (!sysTenantDatasourceService.update(sysTenantDatasource, originalSysTenantDatasource)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户数据源操作。 + * + * @param datasourceId 指定的租户数据源主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody(required = true) Long datasourceId) { + String errorMessage; + SysTenantDatasource originalSysTenantDatasource = sysTenantDatasourceService.getById(datasourceId); + if (originalSysTenantDatasource == null) { + errorMessage = "数据验证失败,当前租户数据源并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenant filter = new SysTenant(); + filter.setDatasourceType(originalSysTenantDatasource.getDatasourceType()); + if (sysTenantService.existByFilter(filter)) { + errorMessage = "数据验证失败,存在使用该租户数据源的租户,不能直接删除该租户数据源!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!sysTenantDatasourceService.removeById(datasourceId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 查看租户数据源列表。 + * + * @param sysTenantDatasourceDtoFilter 过滤对象。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含租户权限资源列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysTenantPermDto sysTenantDatasourceDtoFilter, @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenantDatasource filter = MyModelUtil.copyTo(sysTenantDatasourceDtoFilter, SysTenantDatasource.class); + List resultList = sysTenantDatasourceService.getSysTenantDatasourceList(filter); + for (SysTenantDatasource datasource : resultList) { + datasource.setPassword(StrUtil.repeat('*', datasource.getPassword().length())); + } + + List resultVoList = MyModelUtil.copyCollectionTo(resultList, SysTenantDatasourceVo.class); + long totalCount = 0L; + if (resultList instanceof Page) { + totalCount = ((Page) resultList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(resultVoList, totalCount)); + } + + /** + * 获取租户数据源的字典列表。 + * + * @return 租户数据源的字典列表。 + */ + @NoAuthInterface + @GetMapping("/listDict") + public ResponseResult>> listDict() { + List resultList = sysTenantDatasourceService.getAllList(); + return ResponseResult.success(MyCommonUtil.toDictDataList( + resultList, SysTenantDatasource::getDatasourceType, SysTenantDatasource::getDatasourceName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantMenuController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantMenuController.java new file mode 100644 index 0000000..d831f64 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantMenuController.java @@ -0,0 +1,167 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import apelet.common.core.util.MyModelUtil; +import apelet.tenantadmin.tenant.dto.SysTenantMenuDto; +import apelet.tenantadmin.tenant.model.SysTenantMenu; +import apelet.tenantadmin.tenant.service.SysTenantMenuService; +import apelet.tenantadmin.tenant.service.SysTenantPermCodeService; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.vo.SysTenantMenuVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Set; + +/** + * 租户菜单管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户菜单管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantMenu") +public class SysTenantMenuController { + + @Autowired + private SysTenantMenuService sysTenantMenuService; + @Autowired + private SysTenantPermCodeService sysTenantPermCodeService; + + /** + * 添加新租户菜单操作。 + * + * @param sysTenantMenuDto 新租户菜单对象。 + * @param permCodeIdListString 与当前菜单Id绑定的权限Id列表,多个权限之间逗号分隔。 + * @return 应答结果对象,包含新增菜单的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantMenuDto.menuId"}) + @SuppressWarnings("unchecked") + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysTenantMenuDto sysTenantMenuDto, + @MyRequestBody String permCodeIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantMenuDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantMenu sysTenantMenu = MyModelUtil.copyTo(sysTenantMenuDto, SysTenantMenu.class); + CallResult verifyResult = + sysTenantMenuService.verifyRelatedData(sysTenantMenu, null, permCodeIdListString); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + Set permCodeIdSet = null; + if (verifyResult.getData() != null) { + permCodeIdSet = (Set) verifyResult.getData().get("permCodeIdSet"); + } + sysTenantMenuService.saveNew(sysTenantMenu, permCodeIdSet); + return ResponseResult.success(sysTenantMenu.getMenuId()); + } + + /** + * 更新租户菜单数据的操作。 + * + * @param sysTenantMenuDto 更新租户菜单对象。 + * @param permCodeIdListString 与当前租户菜单Id绑定的权限Id列表,多个权限之间逗号分隔。 + * @return 应答结果对象。 + */ + @SuppressWarnings("unchecked") + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysTenantMenuDto sysTenantMenuDto, + @MyRequestBody String permCodeIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantMenuDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantMenu originalSysTenantMenu = sysTenantMenuService.getById(sysTenantMenuDto.getMenuId()); + if (originalSysTenantMenu == null) { + errorMessage = "数据验证失败,当前租户菜单并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenantMenu sysTenantMenu = MyModelUtil.copyTo(sysTenantMenuDto, SysTenantMenu.class); + CallResult verifyResult = sysTenantMenuService.verifyRelatedData(sysTenantMenu, originalSysTenantMenu, permCodeIdListString); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + Set permCodeIdSet = null; + if (verifyResult.getData() != null) { + permCodeIdSet = (Set) verifyResult.getData().get("permCodeIdSet"); + } + if (!sysTenantMenuService.update(sysTenantMenu, originalSysTenantMenu, permCodeIdSet)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户菜单的操作。 + * + * @param menuId 指定租户菜单主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long menuId) { + if (MyCommonUtil.existBlankArgument(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + if (sysTenantMenuService.hasChildren(menuId)) { + errorMessage = "数据验证失败,当前租户菜单存在下级菜单!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysTenantMenuService.remove(menuId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 获取全部菜单列表。 + * + * @return 应答结果对象,包含全部菜单数据列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List menuList = + sysTenantMenuService.getAllListByOrder("menuType", "showOrder"); + return ResponseResult.success(SysTenantMenu.INSTANCE.fromModelList(menuList)); + } + + /** + * 查看指定租户菜单数据详情。 + * + * @param menuId 指定租户菜单主键Id。 + * @return 应答结果对象,包含菜单详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long menuId) { + if (MyCommonUtil.existBlankArgument(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysTenantMenu sysMenu = sysTenantMenuService.getByIdWithRelation(menuId, MyRelationParam.full()); + if (sysMenu == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantMenuVo sysMenuVo = SysTenantMenu.INSTANCE.fromModel(sysMenu); + return ResponseResult.success(sysMenuVo); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermCodeController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermCodeController.java new file mode 100644 index 0000000..e5f9392 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermCodeController.java @@ -0,0 +1,171 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import apelet.common.core.util.MyModelUtil; +import apelet.tenantadmin.tenant.dto.SysTenantPermCodeDto; +import apelet.tenantadmin.tenant.model.SysTenantPermCode; +import apelet.tenantadmin.tenant.service.SysTenantPermCodeService; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.vo.SysTenantPermCodeVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Set; + +/** + * 租户权限字管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户权限字管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantPermCode") +public class SysTenantPermCodeController { + + @Autowired + private SysTenantPermCodeService sysTenantPermCodeService; + + /** + * 新增租户权限字操作。 + * + * @param sysTenantPermCodeDto 新增租户权限字对象。 + * @param permIdListString 与当前租户权限字Id绑定租户的权限资源Id列表,多个权限资源之间逗号分隔。 + * @return 应答结果对象,包含新增权限字的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantPermCodeDto.permCodeId"}) + @SuppressWarnings("unchecked") + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysTenantPermCodeDto sysTenantPermCodeDto, + @MyRequestBody String permIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantPermCodeDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPermCode sysTenantPermCode = MyModelUtil.copyTo(sysTenantPermCodeDto, SysTenantPermCode.class); + CallResult verifyResult = + sysTenantPermCodeService.verifyRelatedData(sysTenantPermCode, null, permIdListString); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + Set permIdSet = null; + if (verifyResult.getData() != null) { + permIdSet = (Set) verifyResult.getData().get("permIdSet"); + } + sysTenantPermCodeService.saveNew(sysTenantPermCode, permIdSet); + return ResponseResult.success(sysTenantPermCode.getPermCodeId()); + } + + /** + * 更新租户权限字操作。 + * + * @param sysTenantPermCodeDto 更新租户权限字对象。 + * @param permIdListString 与当前租户权限字Id绑定的租户权限资源Id列表,多个权限资源之间逗号分隔。 + * @return 应答结果对象。 + */ + @SuppressWarnings("unchecked") + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysTenantPermCodeDto sysTenantPermCodeDto, + @MyRequestBody String permIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantPermCodeDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPermCode originalSysTenantPermCode = sysTenantPermCodeService.getById(sysTenantPermCodeDto.getPermCodeId()); + if (originalSysTenantPermCode == null) { + errorMessage = "数据验证失败,当前权限字并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenantPermCode sysTenantPermCode = MyModelUtil.copyTo(sysTenantPermCodeDto, SysTenantPermCode.class); + CallResult verifyResult = + sysTenantPermCodeService.verifyRelatedData(sysTenantPermCode, originalSysTenantPermCode, permIdListString); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + Set permIdSet = null; + if (verifyResult.getData() != null) { + permIdSet = (Set) verifyResult.getData().get("permIdSet"); + } + try { + if (!sysTenantPermCodeService.update(sysTenantPermCode, originalSysTenantPermCode, permIdSet)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + } catch (DuplicateKeyException e) { + errorMessage = "数据操作失败,权限字编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户权限字操作。 + * + * @param permCodeId 指定的租户权限字主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long permCodeId) { + String errorMessage; + if (MyCommonUtil.existBlankArgument(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (sysTenantPermCodeService.hasChildren(permCodeId)) { + errorMessage = "数据验证失败,当前租户权限字存在下级权限字!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysTenantPermCodeService.remove(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 查看权限字列表。 + * + * @return 应答结果对象,包含权限字列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List permCodeList = + sysTenantPermCodeService.getAllListByOrder("permCodeType", "showOrder"); + return ResponseResult.success(SysTenantPermCode.INSTANCE.fromModelList(permCodeList)); + } + + /** + * 查看权限字对象详情。 + * + * @param permCodeId 指定权限字主键Id。 + * @return 应答结果对象,包含权限字对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long permCodeId) { + if (MyCommonUtil.existBlankArgument(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysTenantPermCode sysPermCode = sysTenantPermCodeService.getByIdWithRelation(permCodeId, MyRelationParam.full()); + if (sysPermCode == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantPermCodeVo sysPermCodeVo = SysTenantPermCode.INSTANCE.fromModel(sysPermCode); + return ResponseResult.success(sysPermCodeVo); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermController.java new file mode 100644 index 0000000..286dcbb --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermController.java @@ -0,0 +1,156 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import apelet.common.core.util.MyModelUtil; +import apelet.tenantadmin.tenant.dto.SysTenantPermDto; +import apelet.tenantadmin.tenant.model.SysTenantPerm; +import apelet.tenantadmin.tenant.service.SysTenantPermService; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.vo.SysTenantPermVo; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; + +/** + * 租户权限资源管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户权限资源管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantPerm") +public class SysTenantPermController { + + @Autowired + private SysTenantPermService sysTenantPermService; + + /** + * 新增租户权限资源操作。 + * + * @param sysTenantPermDto 新增租户权限资源对象。 + * @return 应答结果对象,包含新租户增权限资源的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantPermDto.permId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysTenantPermDto sysTenantPermDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantPermDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPerm sysTenantPerm = MyModelUtil.copyTo(sysTenantPermDto, SysTenantPerm.class); + CallResult verifyResult = sysTenantPermService.verifyRelatedData(sysTenantPerm, null); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + sysTenantPermService.saveNew(sysTenantPerm); + return ResponseResult.success(sysTenantPerm.getPermId()); + } + + /** + * 更新租户权限资源操作。 + * + * @param sysTenantPermDto 更新租户权限资源对象。 + * @return 应答结果对象,包含更新租户权限资源的主键Id。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysTenantPermDto sysTenantPermDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantPermDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPerm originalSysTenantPerm = sysTenantPermService.getById(sysTenantPermDto.getPermId()); + if (originalSysTenantPerm == null) { + errorMessage = "数据验证失败,当前租户权限资源并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenantPerm sysTenantPerm = MyModelUtil.copyTo(sysTenantPermDto, SysTenantPerm.class); + CallResult verifyResult = sysTenantPermService.verifyRelatedData(sysTenantPerm, originalSysTenantPerm); + if (!verifyResult.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, verifyResult.getErrorMessage()); + } + if (!sysTenantPermService.update(sysTenantPerm, originalSysTenantPerm)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户权限资源操作。 + * + * @param permId 指定的租户权限资源主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long permId) { + if (MyCommonUtil.existBlankArgument(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysTenantPermService.remove(permId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 查看租户权限资源对象详情。 + * + * @param permId 指定租户权限资源主键Id。 + * @return 应答结果对象,包含租户权限资源对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long permId) { + if (MyCommonUtil.existBlankArgument(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysTenantPerm perm = sysTenantPermService.getById(permId); + if (perm == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantPermVo permVo = MyModelUtil.copyTo(perm, SysTenantPermVo.class); + return ResponseResult.success(permVo); + } + + /** + * 查看租户权限资源列表。 + * + * @param sysPermDtoFilter 过滤对象。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含租户权限资源列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysTenantPermDto sysPermDtoFilter, @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenantPerm sysPermFilter = MyModelUtil.copyTo(sysPermDtoFilter, SysTenantPerm.class); + List resultList = sysTenantPermService.getPermListWithRelation(sysPermFilter); + List resultVoList = MyModelUtil.copyCollectionTo(resultList, SysTenantPermVo.class); + long totalCount = 0L; + if (resultList instanceof Page) { + totalCount = ((Page) resultList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(resultVoList, totalCount)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermModuleController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermModuleController.java new file mode 100644 index 0000000..7e0a615 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantPermModuleController.java @@ -0,0 +1,174 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import apelet.common.core.util.MyModelUtil; +import apelet.tenantadmin.tenant.dto.SysTenantPermModuleDto; +import apelet.tenantadmin.tenant.model.SysTenantPerm; +import apelet.tenantadmin.tenant.model.SysTenantPermModule; +import apelet.tenantadmin.tenant.service.SysTenantPermModuleService; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.vo.SysTenantPermModuleVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.groups.Default; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * 权限资源模块管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "权限资源模块管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantPermModule") +public class SysTenantPermModuleController { + + @Autowired + private SysTenantPermModuleService sysTenantPermModuleService; + + /** + * 新增租户权限资源模块操作。 + * + * @param sysTenantPermModuleDto 新增租户权限资源模块对象。 + * @return 应答结果对象,包含新增租户权限资源模块的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantPermModuleDto.moduleId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysTenantPermModuleDto sysTenantPermModuleDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantPermModuleDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPermModule sysTenantPermModule = + MyModelUtil.copyTo(sysTenantPermModuleDto, SysTenantPermModule.class); + if (sysTenantPermModule.getParentId() != null + && sysTenantPermModuleService.getById(sysTenantPermModule.getParentId()) == null) { + errorMessage = "数据验证失败,关联的上级租户权限模块并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + sysTenantPermModuleService.saveNew(sysTenantPermModule); + return ResponseResult.success(sysTenantPermModule.getModuleId()); + } + + /** + * 更新租户权限资源模块操作。 + * + * @param sysTenantPermModuleDto 更新租户权限资源模块对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysTenantPermModuleDto sysTenantPermModuleDto) { + String errorMessage = + MyCommonUtil.getModelValidationError(sysTenantPermModuleDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantPermModule originalSysTenantPermModule = + sysTenantPermModuleService.getById(sysTenantPermModuleDto.getModuleId()); + if (originalSysTenantPermModule == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantPermModule sysTenantPermModule = MyModelUtil.copyTo(sysTenantPermModuleDto, SysTenantPermModule.class); + if (sysTenantPermModule.getParentId() != null + && !sysTenantPermModule.getParentId().equals(originalSysTenantPermModule.getParentId()) + && !sysTenantPermModuleService.existId(sysTenantPermModule.getParentId())) { + errorMessage = "数据验证失败,关联的上级权限模块并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + if (!sysTenantPermModuleService.update(sysTenantPermModule, originalSysTenantPermModule)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户权限资源模块操作。 + * + * @param moduleId 指定的租户权限资源模块主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long moduleId) { + if (MyCommonUtil.existBlankArgument(moduleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + if (sysTenantPermModuleService.hasChildren(moduleId) + || sysTenantPermModuleService.hasModulePerms(moduleId)) { + errorMessage = "数据验证失败,当前租户权限模块存在子模块或权限资源,请先删除关联数据!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysTenantPermModuleService.remove(moduleId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 查看全部租户权限资源模块列表。 + * + * @return 应答结果对象,包含租户权限资源模块列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List permModuleList = + sysTenantPermModuleService.getAllListByOrder("showOrder"); + return ResponseResult.success(MyModelUtil.copyCollectionTo(permModuleList, SysTenantPermModuleVo.class)); + } + + /** + * 列出全部租户权限资源模块及其下级关联的权限资源列表。 + * + * @return 应答结果对象,包含树状列表,结构为租户权限资源模块和租户权限资源之间的树状关系。 + */ + @PostMapping("/listAll") + public ResponseResult>> listAll() { + List sysPermModuleList = sysTenantPermModuleService.getPermModuleAndPermList(); + List> resultList = new LinkedList<>(); + for (SysTenantPermModule sysPermModule : sysPermModuleList) { + Map permModuleMap = new HashMap<>(5); + permModuleMap.put("id", sysPermModule.getModuleId()); + permModuleMap.put("name", sysPermModule.getModuleName()); + permModuleMap.put("type", sysPermModule.getModuleType()); + permModuleMap.put("isPerm", false); + if (MyCommonUtil.isNotBlankOrNull(sysPermModule.getParentId())) { + permModuleMap.put("parentId", sysPermModule.getParentId()); + } + resultList.add(permModuleMap); + if (CollectionUtils.isNotEmpty(sysPermModule.getSysPermList())) { + for (SysTenantPerm sysPerm : sysPermModule.getSysPermList()) { + Map permMap = new HashMap<>(4); + permMap.put("id", sysPerm.getPermId()); + permMap.put("name", sysPerm.getPermName()); + permMap.put("isPerm", true); + permMap.put("url", sysPerm.getUrl()); + permMap.put("parentId", sysPermModule.getModuleId()); + resultList.add(permMap); + } + } + } + return ResponseResult.success(resultList); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantRoleController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantRoleController.java new file mode 100644 index 0000000..4762b7b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/SysTenantRoleController.java @@ -0,0 +1,621 @@ +package apelet.tenantadmin.tenant.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.ObjectUtil; +import apelet.tenantadmin.tenant.dto.SysTenantDto; +import apelet.tenantadmin.tenant.dto.SysTenantRoleDto; +import apelet.tenantadmin.tenant.dto.SysTenantRoleMenuDto; +import apelet.tenantadmin.tenant.dto.SysTenantRoleTenantDto; +import apelet.tenantadmin.tenant.model.*; +import apelet.tenantadmin.tenant.service.SysTenantMenuService; +import apelet.tenantadmin.tenant.service.SysTenantRoleService; +import apelet.tenantadmin.tenant.service.SysTenantService; +import apelet.tenantadmin.tenant.vo.SysTenantMenuVo; +import apelet.tenantadmin.tenant.vo.SysTenantRoleVo; +import apelet.tenantadmin.tenant.vo.SysTenantVo; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.report.dto.ReportDatasetDto; +import apelet.common.report.model.ReportDataset; +import apelet.common.report.service.ReportDatasetService; +import apelet.common.report.vo.ReportDatasetVo; +import apelet.common.online.dto.OnlinePageDto; +import apelet.common.online.model.OnlinePage; +import apelet.common.online.service.OnlinePageService; +import apelet.common.online.vo.OnlinePageVo; +import apelet.common.tenant.annotation.SwitchTenantCommonDatasource; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 租户角色管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户角色管理接口控制器类。") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/sysTenantRole") +public class SysTenantRoleController { + + @Autowired + private SysTenantRoleService sysTenantRoleService; + @Autowired + private SysTenantMenuService sysTenantMenuService; + @Autowired + private SysTenantService sysTenantService; + @Autowired + private ReportDatasetService reportDatasetService; + @Autowired + private OnlinePageService onlinePageService; + + /** + * 新增租户角色操作。 + * + * @param sysTenantRoleDto 新增租户角色对象。 + * @return 应答结果对象,包含新增角色的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysTenantRoleDto.tenantRoleId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysTenantRoleDto sysTenantRoleDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantRoleDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantRole sysTenantRole = MyModelUtil.copyTo(sysTenantRoleDto, SysTenantRole.class); + sysTenantRoleService.saveNew(sysTenantRole); + return ResponseResult.success(sysTenantRole.getTenantRoleId()); + } + + /** + * 更新租户角色操作。 + * + * @param sysTenantRoleDto 更新租户角色对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysTenantRoleDto sysTenantRoleDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysTenantRoleDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysTenantRole originalSysTenantRole = sysTenantRoleService.getById(sysTenantRoleDto.getTenantRoleId()); + if (originalSysTenantRole == null) { + errorMessage = "数据验证失败,当前租户角色并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysTenantRole sysTenantRole = MyModelUtil.copyTo(sysTenantRoleDto, SysTenantRole.class); + if (!sysTenantRoleService.update(sysTenantRole, originalSysTenantRole)) { + errorMessage = "更新失败,租户角色数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定租户角色操作。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long tenantRoleId) { + if (MyCommonUtil.existBlankArgument(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 如果租户角色中,同时包含租户及其关联数据时,不能直接删除租户角色数据。 + // 需要提示用户先把菜单等数据逐条删除,毕竟菜单数据可能相对较少,这个需要视实际数据 + // 情况而定,总之租户角色如果只是包含菜单和租户其中一个关联时,是可以直接删除, + // 且不用同步远程租户数据库的。 + if (sysTenantRoleService.hasTenantByRoleId(tenantRoleId) + && sysTenantRoleService.hasTenantRelatedDataByTenantRoleId(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, + "数据操作失败,当前租户角色同时包含租户及其关联数据,不能直接删除!"); + } + if (!sysTenantRoleService.remove(tenantRoleId)) { + String errorMessage = "数据操作失败,租户角色不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看租户角色列表。 + * + * @param sysTenantRoleDtoFilter 租户角色过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含租户角色列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysTenantRole sysTenantRoleDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenantRole sysTenantRoleFilter = MyModelUtil.copyTo(sysTenantRoleDtoFilter, SysTenantRole.class); + List roleList = sysTenantRoleService.getSysTenantRoleList( + sysTenantRoleFilter, MyOrderParam.buildOrderBy(orderParam, SysTenantRole.class)); + return ResponseResult.success(MyPageUtil.makeResponseData(roleList, SysTenantRole.INSTANCE)); + } + + /** + * 查看租户角色详情。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @return 应答结果对象,包含租户角色详情对象。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long tenantRoleId) { + if (MyCommonUtil.existBlankArgument(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysTenantRole sysRole = sysTenantRoleService.getById(tenantRoleId); + if (sysRole == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysTenantRoleVo sysRoleVo = SysTenantRole.INSTANCE.fromModel(sysRole); + return ResponseResult.success(sysRoleVo); + } + + /** + * 列出指定租户角色没有包含的租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantDtoFilter 租户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,指定租户角色没有包含的租户列表。 + */ + @PostMapping("/listNotInTenantRoleTenant") + public ResponseResult> listNotInTenantRoleTenant( + @MyRequestBody Long tenantRoleId, + @MyRequestBody SysTenantDto sysTenantDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenant sysTenantFilter = MyModelUtil.copyTo(sysTenantDtoFilter, SysTenant.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysTenant.class); + List resultList = + sysTenantService.getNotInSysTenantListByRoleId(tenantRoleId, sysTenantFilter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(resultList, SysTenant.INSTANCE)); + } + + /** + * 列出指定租户角色的租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantDtoFilter 租户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,指定租户角色包含租户菜单列表。 + */ + @PostMapping("/listTenantRoleTenant") + public ResponseResult> listTenantRoleTenant( + @MyRequestBody Long tenantRoleId, + @MyRequestBody SysTenantDto sysTenantDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysTenant sysTenantFilter = MyModelUtil.copyTo(sysTenantDtoFilter, SysTenant.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysTenant.class); + List resultList = + sysTenantService.getSysTenantListByRoleId(tenantRoleId, sysTenantFilter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(resultList, SysTenant.INSTANCE)); + } + + /** + * 为指定租户角色添加租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantRoleTenantDtoList 租户角色与租户的关联列表。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addTenantRoleTenant") + public ResponseResult addTenantRoleTenant( + @MyRequestBody Long tenantRoleId, + @MyRequestBody List sysTenantRoleTenantDtoList) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, sysTenantRoleTenantDtoList)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + Set tenantIdSet = sysTenantRoleTenantDtoList.stream() + .map(SysTenantRoleTenantDto::getTenantId).collect(Collectors.toSet()); + if (!sysTenantRoleService.existId(tenantRoleId) + || !sysTenantService.existUniqueKeyList("tenantId", tenantIdSet)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + List sysTenantRoleTenantList = + MyModelUtil.copyCollectionTo(sysTenantRoleTenantDtoList, SysTenantRoleTenant.class); + sysTenantRoleService.addTenantRoleTenantList(tenantRoleId, sysTenantRoleTenantList); + return ResponseResult.success(); + } + + /** + * 为指定租户角色移除指定租户。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @param tenantId 指定租户主键Id。 + * @param force 是否强制移除。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteTenantRoleTenant") + public ResponseResult deleteTenantRoleTenant( + @MyRequestBody Long tenantRoleId, @MyRequestBody Long tenantId, @MyRequestBody Boolean force) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, tenantId, force)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 判断该租户角色是否包含租户关联数据,如果不包含,就只是删除本地关联数据即可。 + if (!sysTenantRoleService.hasTenantRelatedDataByTenantRoleId(tenantRoleId)) { + sysTenantRoleService.removeTenantRoleTenant(tenantRoleId, tenantId); + return ResponseResult.success(); + } + // 租户角色包含菜单,如果不是强制删除,就给出错误提示即可。 + if (BooleanUtil.isFalse(force)) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, + "数据操作失败,当前租户角色包含租户及其关联数据,是否强制删除!"); + } + sysTenantRoleService.removeTenantRoleTenantAndSync(tenantRoleId, tenantId); + return ResponseResult.success(); + } + + /** + * 列出指定租户角色没有包含的租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色没有包含的菜单列表。 + */ + @PostMapping("/listNotInTenantRoleMenu") + public ResponseResult> listNotInTenantRoleMenu( + @MyRequestBody Long tenantRoleId, @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysTenantMenu.class); + List resultList = + sysTenantMenuService.getNotInSysTenantMenuListByRoleId(tenantRoleId, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(resultList, SysTenantMenu.INSTANCE)); + } + + /** + * 列出指定租户角色的租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色包含租户菜单列表。 + */ + @PostMapping("/listTenantRoleMenu") + public ResponseResult> listTenantRoleMenu( + @MyRequestBody Long tenantRoleId, @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysTenantMenu.class); + List resultList = sysTenantMenuService.getSysTenantMenuListByRoleId(tenantRoleId, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(resultList, SysTenantMenu.INSTANCE)); + } + + private ResponseResult doTenantRoleVerify(Long tenantRoleId) { + if (MyCommonUtil.existBlankArgument(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysTenantRoleService.existId(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + return ResponseResult.success(); + } + + /** + * 为指定租户角色添加租户菜单列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantRoleMenuDtoList 租户角色菜单关联列表。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addTenantRoleMenu") + public ResponseResult addTenantRoleMenu( + @MyRequestBody Long tenantRoleId, + @MyRequestBody List sysTenantRoleMenuDtoList) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, sysTenantRoleMenuDtoList)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + Set menuIdSet = + sysTenantRoleMenuDtoList.stream().map(SysTenantRoleMenuDto::getMenuId).collect(Collectors.toSet()); + if (!sysTenantRoleService.existId(tenantRoleId) + || !sysTenantMenuService.existUniqueKeyList("menuId", menuIdSet)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + List sysTenantRoleMenuList = + MyModelUtil.copyCollectionTo(sysTenantRoleMenuDtoList, SysTenantRoleMenu.class); + sysTenantRoleService.addTenantRoleMenuList(tenantRoleId, sysTenantRoleMenuList); + return ResponseResult.success(); + } + + /** + * 为指定租户角色移除指定租户菜单。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @param menuId 指定租户菜单主键Id。 + * @param force 是否强制移除。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteTenantRoleMenu") + public ResponseResult deleteTenantRoleMenu( + @MyRequestBody Long tenantRoleId, @MyRequestBody Long menuId, @MyRequestBody Boolean force) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, menuId, force)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 判断该租户角色是否包含租户,如果不包含,就只是删除本地关联数据即可。 + if (!sysTenantRoleService.hasTenantByRoleId(tenantRoleId)) { + sysTenantRoleService.removeTenantRoleMenu(tenantRoleId, menuId); + return ResponseResult.success(); + } + // 租户角色包含租户,如果不是强制删除,就给出错误提示即可。 + if (BooleanUtil.isFalse(force)) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, + "数据操作失败,当前租户角色包含租户数据,是否强制删除!"); + } + sysTenantRoleService.removeTenantRoleMenuAndSync(tenantRoleId, menuId); + return ResponseResult.success(); + } + + /** + * 列出指定租户角色没有包含的报表数据集列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param reportDatasetDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色没有包含的报表数据集列表。 + */ + @SwitchTenantCommonDatasource + @PostMapping("/listNotInTenantRoleReportDataset") + public ResponseResult> listNotInTenantRoleReportDataset( + @MyRequestBody Long tenantRoleId, + @MyRequestBody ReportDatasetDto reportDatasetDtoFilter, + @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + List datasetIds = sysTenantRoleService.getSysTenantRoleReportDatasetIdList(tenantRoleId); + String orderBy = MyOrderParam.buildOrderBy(orderParam, ReportDataset.class); + // SwitchTenantReportDatasource注解会将数据源切换到ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE + // 所指向的数据库,既租户业务的通用数据库。 + List reportDatasetList = + reportDatasetService.getNotInList("datasetId", new HashSet<>(datasetIds), orderBy); + if (reportDatasetDtoFilter != null && reportDatasetDtoFilter.getGroupId() != null) { + reportDatasetList = reportDatasetList.stream() + .filter(d -> d.getGroupId().equals(reportDatasetDtoFilter.getGroupId())) + .collect(Collectors.toList()); + } + reportDatasetList = reportDatasetList.stream() + .filter(d -> ObjectUtil.equal(d.getAppCode(), TokenData.takeFromRequest().getAppCode())) + .collect(Collectors.toList()); + reportDatasetService.buildRelationForDataList(reportDatasetList, MyRelationParam.dictOnly()); + return ResponseResult.success(MyPageUtil.makeResponseData(reportDatasetList, ReportDataset.INSTANCE)); + } + + /** + * 列出指定租户角色的报表数据集列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色包含报表数据集列表。 + */ + @SwitchTenantCommonDatasource + @PostMapping("/listTenantRoleReportDataset") + public ResponseResult> listTenantRoleReportDataset( + @MyRequestBody Long tenantRoleId, @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + List datasetIds = sysTenantRoleService.getSysTenantRoleReportDatasetIdList(tenantRoleId); + String orderBy = MyOrderParam.buildOrderBy(orderParam, ReportDataset.class); + // SwitchTenantReportDatasource注解会将数据源切换到ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE + // 所指向的数据库,既租户业务的通用数据库。 + List reportDatasetList = + reportDatasetService.getInList("datasetId", new HashSet<>(datasetIds), orderBy); + reportDatasetService.buildRelationForDataList(reportDatasetList, MyRelationParam.dictOnly()); + return ResponseResult.success(MyPageUtil.makeResponseData(reportDatasetList, ReportDataset.INSTANCE)); + } + + /** + * 为指定租户角色添加报表数据集列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param datasetIds 报表数据集Id集合。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addTenantRoleReportDataset") + public ResponseResult addTenantRoleReportDataset( + @MyRequestBody Long tenantRoleId, @MyRequestBody Set datasetIds) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, datasetIds)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysTenantRoleService.existId(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + sysTenantRoleService.addTenantRoleReportDatasetList(tenantRoleId, datasetIds); + return ResponseResult.success(); + } + + /** + * 为指定租户角色移除指定报表数据集。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @param datasetId 指定报表数据集主键Id。 + * @param force 是否强制移除。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteTenantRoleReportDataset") + public ResponseResult deleteTenantRoleReportDataset( + @MyRequestBody Long tenantRoleId, @MyRequestBody Long datasetId, @MyRequestBody Boolean force) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, datasetId, force)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 判断该租户角色是否包含租户,如果不包含,就只是删除本地关联数据即可。 + if (!sysTenantRoleService.hasTenantByRoleId(tenantRoleId)) { + sysTenantRoleService.removeTenantRoleReportDataset(tenantRoleId, datasetId); + return ResponseResult.success(); + } + // 租户角色包含租户,如果不是强制删除,就给出错误提示即可。 + if (BooleanUtil.isFalse(force)) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, + "数据操作失败,当前租户角色包含租户数据,是否强制删除!"); + } + sysTenantRoleService.removeTenantRoleReportDatasetAndSync(tenantRoleId, datasetId); + return ResponseResult.success(); + } + + /** + * 列出指定租户角色没有包含的在线表单页面列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param onlinePageDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色没有包含的在线表单页面列表。 + */ + @SwitchTenantCommonDatasource + @PostMapping("/listNotInTenantRoleOnlinePage") + public ResponseResult> listNotInTenantRoleOnlinePage( + @MyRequestBody Long tenantRoleId, + @MyRequestBody OnlinePageDto onlinePageDtoFilter, + @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + List pageIds = sysTenantRoleService.getSysTenantRoleOnlinePageIdList(tenantRoleId); + String orderBy = MyOrderParam.buildOrderBy(orderParam, OnlinePage.class); + // SwitchTenantReportDatasource注解会将数据源切换到ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE + // 所指向的数据库,既租户业务的通用数据库。 + List onlinePageList = onlinePageService.getNotInListWithNonTenant(pageIds, orderBy); + if (onlinePageDtoFilter != null && onlinePageDtoFilter.getPageType() != null) { + onlinePageList = onlinePageList.stream() + .filter(d -> d.getPageType().equals(onlinePageDtoFilter.getPageType())).collect(Collectors.toList()); + } + onlinePageList = onlinePageList.stream() + .filter(d -> ObjectUtil.equal(d.getAppCode(), TokenData.takeFromRequest().getAppCode())) + .collect(Collectors.toList()); + onlinePageService.buildRelationForDataList(onlinePageList, MyRelationParam.dictOnly()); + return ResponseResult.success(MyPageUtil.makeResponseData(onlinePageList, OnlinePage.INSTANCE)); + } + + /** + * 列出指定租户角色的在线表单页面列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param orderParam 排序参数。 + * @return 应答结果对象,指定租户角色包含在线表单页面列表。 + */ + @SwitchTenantCommonDatasource + @PostMapping("/listTenantRoleOnlinePage") + public ResponseResult> listTenantRoleOnlinePage( + @MyRequestBody Long tenantRoleId, @MyRequestBody MyOrderParam orderParam) { + ResponseResult verifyResult = this.doTenantRoleVerify(tenantRoleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + List pageIds = sysTenantRoleService.getSysTenantRoleOnlinePageIdList(tenantRoleId); + String orderBy = MyOrderParam.buildOrderBy(orderParam, OnlinePage.class); + // SwitchTenantReportDatasource注解会将数据源切换到ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE + // 所指向的数据库,既租户业务的通用数据库。 + List onlinePageList = onlinePageService.getInListWithNonTenant(pageIds, orderBy); + onlinePageService.buildRelationForDataList(onlinePageList, MyRelationParam.dictOnly()); + return ResponseResult.success(MyPageUtil.makeResponseData(onlinePageList, OnlinePage.INSTANCE)); + } + + /** + * 为指定租户角色添加在线表单页面列表。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param pageIds 在线表单页面Id集合。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addTenantRoleOnlinePage") + public ResponseResult addTenantRoleOnlinePage( + @MyRequestBody Long tenantRoleId, @MyRequestBody Set pageIds) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, pageIds)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysTenantRoleService.existId(tenantRoleId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + sysTenantRoleService.addTenantRoleOnlinePageList(tenantRoleId, pageIds); + return ResponseResult.success(); + } + + /** + * 为指定租户角色移除指定在线表单页面。 + * + * @param tenantRoleId 指定租户角色主键Id。 + * @param pageId 指定在线表单页面主键Id。 + * @param force 是否强制移除。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteTenantRoleOnlinePage") + public ResponseResult deleteTenantRoleOnlinePage( + @MyRequestBody Long tenantRoleId, @MyRequestBody Long pageId, @MyRequestBody Boolean force) { + if (MyCommonUtil.existBlankArgument(tenantRoleId, pageId, force)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 判断该租户角色是否包含租户,如果不包含,就只是删除本地关联数据即可。 + if (!sysTenantRoleService.hasTenantByRoleId(tenantRoleId)) { + sysTenantRoleService.removeTenantRoleOnlinePage(tenantRoleId, pageId); + return ResponseResult.success(); + } + // 租户角色包含租户,如果不是强制删除,就给出错误提示即可。 + if (BooleanUtil.isFalse(force)) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, + "数据操作失败,当前租户角色包含租户数据,是否强制删除!"); + } + sysTenantRoleService.removeTenantRoleOnlinePageAndSync(tenantRoleId, pageId); + return ResponseResult.success(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/TenantGlobalDictController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/TenantGlobalDictController.java new file mode 100644 index 0000000..d0f424c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/TenantGlobalDictController.java @@ -0,0 +1,506 @@ +package apelet.tenantadmin.tenant.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.BooleanUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.MyOrderParam; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.dict.dto.TenantGlobalDictDto; +import apelet.common.dict.dto.TenantGlobalDictItemDto; +import apelet.common.dict.model.TenantGlobalDict; +import apelet.common.dict.model.TenantGlobalDictItem; +import apelet.common.dict.service.TenantGlobalDictItemService; +import apelet.common.dict.service.TenantGlobalDictService; +import apelet.common.dict.vo.TenantGlobalDictVo; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.tenant.model.SysTenant; +import apelet.tenantadmin.tenant.service.SysTenantService; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 租户全局通用字典操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "租户全局字典管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/tenant/tenantGlobalDict") +public class TenantGlobalDictController { + + @Autowired + private TenantGlobalDictService tenantGlobalDictService; + @Autowired + private TenantGlobalDictItemService tenantGlobalDictItemService; + @Autowired + private SysTenantService sysTenantService; + @Autowired + private IdGeneratorWrapper idGenerator; + + private static final String ID = "id"; + private static final String ITEM_ID = "itemId"; + + /** + * 新增租户全局字典接口。 + * + * @param tenantGlobalDictDto 新增字典对象。 + * @return 保存后的字典对象。 + */ + @ApiOperationSupport(ignoreParameters = {"tenantGlobalDictDto.dictId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody TenantGlobalDictDto tenantGlobalDictDto) { + String errorMessage = MyCommonUtil.getModelValidationError(tenantGlobalDictDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 这里必须手动校验字典编码是否存在,因为我们缺省的实现是逻辑删除,所以字典编码字段没有设置为唯一索引。 + if (tenantGlobalDictService.existDictCode(tenantGlobalDictDto.getDictCode())) { + errorMessage = "数据验证失败,租户字典编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + TenantGlobalDict tenantGlobalDict = MyModelUtil.copyTo(tenantGlobalDictDto, TenantGlobalDict.class); + Set tenantIdSet = null; + if (BooleanUtil.isFalse(tenantGlobalDict.getTenantCommon())) { + tenantIdSet = sysTenantService.getAllList().stream().map(SysTenant::getTenantId).collect(Collectors.toSet()); + } + tenantGlobalDictService.saveNew(tenantGlobalDict, tenantIdSet); + return ResponseResult.success(tenantGlobalDict.getDictId()); + } + + /** + * 更新租户全局字典操作。 + * + * @param tenantGlobalDictDto 更新全局字典对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody TenantGlobalDictDto tenantGlobalDictDto) { + String errorMessage = MyCommonUtil.getModelValidationError( + tenantGlobalDictDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + TenantGlobalDict originalTenantGlobalDict = + tenantGlobalDictService.getById(tenantGlobalDictDto.getDictId()); + if (originalTenantGlobalDict == null) { + errorMessage = "数据验证失败,当前租户全局字典并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + TenantGlobalDict tenantGlobalDict = MyModelUtil.copyTo(tenantGlobalDictDto, TenantGlobalDict.class); + if (ObjectUtil.notEqual(tenantGlobalDict.getDictCode(), originalTenantGlobalDict.getDictCode())) { + errorMessage = "数据验证失败,租户字典编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (ObjectUtil.notEqual(tenantGlobalDict.getTenantCommon(), originalTenantGlobalDict.getTenantCommon())) { + errorMessage = "数据验证失败,该字典已存在字典数据,不能修改是否为租户共用字典标记!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!tenantGlobalDictService.update(tenantGlobalDict, originalTenantGlobalDict)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定的租户全局字典。 + * + * @param dictId 指定全局字典主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody(required = true) Long dictId) { + if (!tenantGlobalDictService.remove(dictId)) { + String errorMessage = "数据操作失败,租户全局字典Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看租户全局字典列表。 + * + * @param tenantGlobalDictDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含角色列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody TenantGlobalDictDto tenantGlobalDictDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + TenantGlobalDict filter = MyModelUtil.copyTo(tenantGlobalDictDtoFilter, TenantGlobalDict.class); + List tenantGlobalDictList = + tenantGlobalDictService.getGlobalDictList( + filter, MyOrderParam.buildOrderBy(orderParam, TenantGlobalDict.class)); + List tenantGlobalDictVoList = + MyModelUtil.copyCollectionTo(tenantGlobalDictList, TenantGlobalDictVo.class); + long totalCount = 0L; + if (tenantGlobalDictList instanceof Page) { + totalCount = ((Page) tenantGlobalDictList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(tenantGlobalDictVoList, totalCount)); + } + + /** + * 获取字典详情。 + * + * @param dictId 字典主键。 + * @return 获取字典详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long dictId) { + TenantGlobalDict globalDict = tenantGlobalDictService.getById(dictId); + if (globalDict == null) { + String errorMessage = "数据操作失败,租户全局字典Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(MyModelUtil.copyTo(globalDict, TenantGlobalDictVo.class)); + } + + /** + * 新增租户全局字典项目接口。 + * + * @param tenantGlobalDictItemDto 新增字典项目对象。 + * @return 保存后的字典对象。 + */ + @ApiOperationSupport(ignoreParameters = {"tenantGlobalDictItemDto.id"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/addItem") + public ResponseResult addItem(@MyRequestBody TenantGlobalDictItemDto tenantGlobalDictItemDto) { + String errorMessage = MyCommonUtil.getModelValidationError(tenantGlobalDictItemDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + ResponseResult verifyResult = + this.doVerifyTenantGlobalDict(tenantGlobalDictItemDto.getDictCode()); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + TenantGlobalDict dict = verifyResult.getData(); + TenantGlobalDictItem dictItem = MyModelUtil.copyTo(tenantGlobalDictItemDto, TenantGlobalDictItem.class); + Long id = null; + if (BooleanUtil.isTrue(dict.getTenantCommon())) { + if (tenantGlobalDictItemService.existDictCodeAndItemId(dict, tenantGlobalDictItemDto.getItemId())) { + errorMessage = "数据验证失败,该租户字典编码的项目Id已存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + tenantGlobalDictItemService.saveNew(dict, dictItem); + id = dictItem.getId(); + } else { + if (this.existItemForTenantDict(dict, dictItem)) { + errorMessage = "数据验证失败,该租户字典编码的项目Id已存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 对于非租户公共字典,这里添加的字典数据,都是该编码字典的初始化数据,只会在新建租户的时候, + // 将这里的数据同步到租户的编码字典中,后面所有的修改,都不会再同步到租户的字典数据了,也避免 + // 了数据冲突。 + this.addInitialData(dict, dictItem); + MyModelUtil.fillCommonsForUpdate(dict, dict); + tenantGlobalDictService.updateById(dict); + } + return ResponseResult.success(id); + } + + /** + * 更新租户全局字典项目。 + * + * @param tenantGlobalDictItemDto 更新全局字典项目对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/updateItem") + public ResponseResult updateItem(@MyRequestBody TenantGlobalDictItemDto tenantGlobalDictItemDto) { + String errorMessage = MyCommonUtil.getModelValidationError( + tenantGlobalDictItemDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + ResponseResult verifyResult = + this.doVerifyTenantGlobalDict(tenantGlobalDictItemDto.getDictCode()); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + TenantGlobalDictItem dictItem = MyModelUtil.copyTo(tenantGlobalDictItemDto, TenantGlobalDictItem.class); + TenantGlobalDict dict = verifyResult.getData(); + if (BooleanUtil.isTrue(dict.getTenantCommon())) { + TenantGlobalDictItem originalDictItem = + tenantGlobalDictItemService.getById(tenantGlobalDictItemDto.getId()); + if (originalDictItem == null) { + errorMessage = "数据验证失败,当前租户全局字典项目并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (ObjectUtil.notEqual(dictItem.getDictCode(), originalDictItem.getDictCode())) { + errorMessage = "数据验证失败,租户字典项目的字典编码不能修改!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (ObjectUtil.notEqual(dictItem.getItemId(), originalDictItem.getItemId()) + && tenantGlobalDictItemService.existDictCodeAndItemId(dict, dictItem.getItemId())) { + errorMessage = "数据验证失败,该租户字典编码的项目Id已存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!tenantGlobalDictItemService.update(dict, dictItem, originalDictItem)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + } else { + if (this.existItemForTenantDict(dict, dictItem)) { + errorMessage = "数据验证失败,该租户字典编码的项目Id已存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 对于非租户公共字典,这里添加的字典数据,都是该编码字典的初始化数据,只会在新建租户的时候, + // 将这里的数据同步到租户的编码字典中,后面所有的修改,都不会再同步到租户的字典数据了,也避免 + // 了数据冲突。 + this.updateInitialData(dict, dictItem); + MyModelUtil.fillCommonsForUpdate(dict, dict); + tenantGlobalDictService.updateById(dict); + } + return ResponseResult.success(); + } + + /** + * 更新租户全局字典项目的状态。 + * + * @param id 更新全局字典项目主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/updateItemStatus") + public ResponseResult updateItemStatus( + @MyRequestBody(required = true) Long id, @MyRequestBody(required = true) Integer status) { + String errorMessage; + TenantGlobalDictItem dictItem = tenantGlobalDictItemService.getById(id); + if (dictItem == null) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + ResponseResult verifyResult = this.doVerifyTenantGlobalDict(dictItem.getDictCode()); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + TenantGlobalDict dict = verifyResult.getData(); + if (BooleanUtil.isFalse(dict.getTenantCommon())) { + errorMessage = "数据验证失败,非公用租户编码字典,不能调用该接口!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (ObjectUtil.notEqual(dictItem.getStatus(), status)) { + tenantGlobalDictItemService.updateStatus(dict, dictItem, status); + } + return ResponseResult.success(); + } + + /** + * 删除指定编码的租户全局字典项目。 + * + * @param dictCode 字典编码。 + * @param id 字典项目主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/deleteItem") + public ResponseResult deleteItem( + @MyRequestBody(required = true) String dictCode, + @MyRequestBody(required = true) Long id) { + ResponseResult verifyResult = this.doVerifyTenantGlobalDict(dictCode); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + String errorMessage; + TenantGlobalDict dict = verifyResult.getData(); + if (BooleanUtil.isTrue(dict.getTenantCommon())) { + TenantGlobalDictItem dictItem = tenantGlobalDictItemService.getById(id); + if (dictItem == null) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!tenantGlobalDictItemService.remove(dict, dictItem)) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + } else { + // 对于非租户公共字典,这里添加的字典数据,都是该编码字典的初始化数据,只会在新建租户的时候, + // 将这里的数据同步到租户的编码字典中,后面所有的修改,都不会再同步到租户的字典数据了,也避免 + // 了数据冲突。 + this.removeInitialData(dict, id); + MyModelUtil.fillCommonsForUpdate(dict, dict); + tenantGlobalDictService.updateById(dict); + } + return ResponseResult.success(); + } + + /** + * 白名单接口,登录用户均可访问。以字典形式返回全部租户字典数据集合。 + * fullResultList中的字典列表全部取自于数据库,而cachedResultList全部取自于缓存,前端负责比对。 + * + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listAll") + public ResponseResult listAll(@RequestParam String dictCode) { + ResponseResult verifyResult = this.doVerifyTenantGlobalDict(dictCode); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + String errorMessage; + TenantGlobalDict dict = verifyResult.getData(); + if (BooleanUtil.isFalse(dict.getTenantCommon())) { + errorMessage = "数据验证失败,非公用租户编码字典,不能调用该接口!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + List fullResultList = tenantGlobalDictItemService.getGlobalDictItemList(dict); + List cachedList = + tenantGlobalDictService.getGlobalDictItemListFromCache(dict, null); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("fullResultList", this.toDictDataList(fullResultList)); + jsonObject.put("cachedResultList", this.toDictDataList(cachedList)); + return ResponseResult.success(jsonObject); + } + + /** + * 将当前租户通用字典表的数据重新加载到缓存中。 + * 由于缓存的数据更新,在add/update/delete等接口均有同步处理。因此该接口仅当同步过程中出现问题时, + * 可手工调用,或者每天晚上定时同步一次。 + */ + @OperationLog(type = SysOperationLogType.RELOAD_CACHE) + @GetMapping("/reloadCachedData") + public ResponseResult reloadCachedData(@RequestParam String dictCode) { + ResponseResult verifyResult = this.doVerifyTenantGlobalDict(dictCode); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + TenantGlobalDict dict = verifyResult.getData(); + if (BooleanUtil.isTrue(dict.getTenantCommon())) { + tenantGlobalDictService.reloadCachedData(dict); + } else { + tenantGlobalDictService.reloadAllTenantCachedData(dict); + } + return ResponseResult.success(true); + } + + private List> toDictDataList(List resultList) { + return resultList.stream().map(item -> { + Map dataMap = new HashMap<>(2); + dataMap.put(ApplicationConstant.DICT_ID, item.getId()); + dataMap.put(ApplicationConstant.DICT_NAME, item.getItemName()); + dataMap.put(ITEM_ID, item.getItemId()); + dataMap.put("status", item.getStatus()); + dataMap.put("showOrder", item.getShowOrder()); + return dataMap; + }).collect(Collectors.toList()); + } + + private ResponseResult doVerifyTenantGlobalDict(String dictCode) { + String errorMessage; + TenantGlobalDict tenantGlobalDict = + tenantGlobalDictService.getTenantGlobalDictByDictCode(dictCode); + if (tenantGlobalDict == null) { + errorMessage = "数据验证失败,租户字典编码不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + return ResponseResult.success(tenantGlobalDict); + } + + private boolean existItemForTenantDict(TenantGlobalDict dict, TenantGlobalDictItem dictItem) { + if (StrUtil.isBlank(dict.getInitialData())) { + return false; + } + // 解析并迭代数组数据,要求每个JSON对象中必须包含id和name。 + JSONArray initialDataArray = JSONArray.parseArray(dict.getInitialData()); + for (int i = 0; i < initialDataArray.size(); i++) { + JSONObject data = initialDataArray.getJSONObject(i); + if (StrUtil.equals(data.getString(ITEM_ID), dictItem.getItemId()) + && ObjectUtil.notEqual(data.getLong(ID), dictItem.getId())) { + return true; + } + } + return false; + } + + private void addInitialData(TenantGlobalDict dict, TenantGlobalDictItem dictItem) { + JSONArray dataArray; + if (StrUtil.isBlank(dict.getInitialData())) { + dataArray = new JSONArray(); + } else { + dataArray = JSON.parseArray(dict.getInitialData()); + } + dataArray.add(this.convertToDictItemData(dictItem)); + dict.setInitialData(dataArray.toJSONString()); + } + + private void updateInitialData(TenantGlobalDict dict, TenantGlobalDictItem dictItem) { + if (StrUtil.isBlank(dict.getInitialData())) { + return; + } + JSONArray resultArray = new JSONArray(); + JSONArray dataArray = JSON.parseArray(dict.getInitialData()); + for (int i = 0; i < dataArray.size(); i++) { + JSONObject data = dataArray.getJSONObject(i); + Long id = data.getLong(ID); + if (ObjectUtil.equals(id, dictItem.getId())) { + resultArray.add(this.convertToDictItemData(dictItem)); + } else { + resultArray.add(data); + } + } + dict.setInitialData(resultArray.toJSONString()); + } + + private void removeInitialData(TenantGlobalDict dict, Long id) { + if (StrUtil.isBlank(dict.getInitialData())) { + return; + } + JSONArray resultArray = new JSONArray(); + JSONArray dataArray = JSON.parseArray(dict.getInitialData()); + for (int i = 0; i < dataArray.size(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (ObjectUtil.notEqual(data.getLong(ID), id)) { + resultArray.add(data); + } + } + dict.setInitialData(resultArray.toJSONString()); + } + + private JSONObject convertToDictItemData(TenantGlobalDictItem dictItem) { + JSONObject newData = new JSONObject(); + if (dictItem.getId() == null) { + newData.put(ID, idGenerator.nextLongId()); + } else { + newData.put(ID, dictItem.getId()); + } + newData.put(ITEM_ID, dictItem.getItemId()); + newData.put("itemName", dictItem.getItemName()); + newData.put("showOrder", dictItem.getShowOrder()); + return newData; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/DeployPluginMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/DeployPluginMapper.java new file mode 100644 index 0000000..fa2255d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/DeployPluginMapper.java @@ -0,0 +1,20 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.DeployPlugin; + +import java.util.List; + +/** + *

+ * 热部署插件表 Mapper 接口 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +public interface DeployPluginMapper extends BaseDaoMapper { + + List getDeployPluginList(DeployPlugin deployPlugin, String orderBy); + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantDatasourceMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantDatasourceMapper.java new file mode 100644 index 0000000..df58cd7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantDatasourceMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantDatasource; + +/** + * 租户数据源数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantDatasourceMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantExtMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantExtMapper.java new file mode 100644 index 0000000..379063e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantExtMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantExt; + +/** + * 租户扩展数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantExtMapper extends BaseDaoMapper { +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMapper.java new file mode 100644 index 0000000..84ad39f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMapper.java @@ -0,0 +1,83 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenant; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 租户数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantMapper extends BaseDaoMapper { + + /** + * 根据租户过滤对象,获取匹配的租户对象列表。 + * + * @param sysTenantFilter 租户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 查询的租户列表。 + */ + List getSysTenantList( + @Param("sysTenantFilter") SysTenant sysTenantFilter, @Param("orderBy") String orderBy); + + /** + * 根据租户角色Id,获取关联的租户菜单从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的从表数据列表。 + */ + List getNotInSysTenantListByRoleId( + @Param("tenantRoleId") Long tenantRoleId, + @Param("sysTenantFilter") SysTenant sysTenantFilter, + @Param("orderBy") String orderBy); + + /** + * 根据租户角色Id,获取关联的租户菜单从表中和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的从表数据列表。 + */ + List getSysTenantListByRoleId( + @Param("tenantRoleId") Long tenantRoleId, + @Param("sysTenantFilter") SysTenant sysTenantFilter, + @Param("orderBy") String orderBy); + + /** + * 查询租户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param url url过滤条件。 + * @return 包含从租户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysTenantPermListWithDetail( + @Param("tenantId") Long tenantId, @Param("url") String url); + + /** + * 查询租户的权限字列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从租户到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysTenantPermCodeListWithDetail( + @Param("tenantId") Long tenantId, @Param("permCode") String permCode); + + /** + * 查询租户的菜单列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从租户到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysTenantMenuListWithDetail( + @Param("tenantId") Long tenantId, @Param("menuName") String menuName); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuMapper.java new file mode 100644 index 0000000..3e744e8 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuMapper.java @@ -0,0 +1,36 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 租户菜单数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantMenuMapper extends BaseDaoMapper { + + /** + * 根据租户角色Id,获取关联的租户菜单从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的从表数据列表。 + */ + List getNotInSysTenantMenuListByRoleId( + @Param("tenantRoleId") Long tenantRoleId, @Param("orderBy") String orderBy); + + /** + * 根据租户角色Id,获取关联的租户菜单从表和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的从表数据列表。 + */ + List getSysTenantMenuListByRoleId( + @Param("tenantRoleId") Long tenantRoleId, @Param("orderBy") String orderBy); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuPermCodeMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuPermCodeMapper.java new file mode 100644 index 0000000..faf430b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantMenuPermCodeMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantMenuPermCode; + +import java.util.List; + +/** + * 菜单与权限字关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantMenuPermCodeMapper extends BaseDaoMapper { + + /** + * 批量插入租户菜单和权限字的关联关系。 + * + * @param sysMenuPermCodeList 租户菜单和权限字的关联列表。 + */ + void insertList(List sysMenuPermCodeList); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodeMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodeMapper.java new file mode 100644 index 0000000..f211bb1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodeMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantPermCode; + +/** + * 权限字数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermCodeMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodePermMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodePermMapper.java new file mode 100644 index 0000000..16a2c18 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermCodePermMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantPermCodePerm; + +import java.util.List; + +/** + * 权限字与权限资源关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermCodePermMapper extends BaseDaoMapper { + + /** + * 批量插入租户权限字和租户权限资源的关联关系。 + * + * @param sysPermCodePermList 租户权限字和租户权限资源的关联关系列表。 + */ + void insertList(List sysPermCodePermList); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermMapper.java new file mode 100644 index 0000000..122bd1b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantPerm; + +/** + * 权限资源数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermModuleMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermModuleMapper.java new file mode 100644 index 0000000..03d8dd7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantPermModuleMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantPermModule; + +import java.util.List; + +/** + * 权限资源模块数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermModuleMapper extends BaseDaoMapper { + + /** + * 获取整个权限模块和权限关联后的全部数据。 + * + * @return 关联的权限模块和权限资源列表。 + */ + List getPermModuleAndPermList(); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMapper.java new file mode 100644 index 0000000..8929a4e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMapper.java @@ -0,0 +1,26 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 租户角色数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleMapper extends BaseDaoMapper { + + /** + * 获取租户角色列表。 + * + * @param sysTenantRoleFilter 过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 租户角色列表。 + */ + List getSysTenantRoleList( + @Param("sysTenantRoleFilter") SysTenantRole sysTenantRoleFilter, @Param("orderBy") String orderBy); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMenuMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMenuMapper.java new file mode 100644 index 0000000..86f0d82 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleMenuMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantRoleMenu; + +import java.util.List; + +/** + * 租户角色菜单数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleMenuMapper extends BaseDaoMapper { + + /** + * 批量插入租户角色和租户菜单之间的关联关系列表。 + * + * @param sysTenantRoleMenuList 租户角色和租户菜单关联关系列表。 + */ + void insertList(List sysTenantRoleMenuList); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleOnlinePageMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleOnlinePageMapper.java new file mode 100644 index 0000000..533612e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleOnlinePageMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantRoleOnlinePage; + +import java.util.List; + +/** + * 租户角色在线表单页面访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleOnlinePageMapper extends BaseDaoMapper { + + /** + * 批量插入租户角色和在线表单页面之间的关联关系列表。 + * + * @param dataList 租户角色和在线表单页面关联关系列表。 + */ + void insertList(List dataList); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleReportDatasetMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleReportDatasetMapper.java new file mode 100644 index 0000000..e126b95 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleReportDatasetMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantRoleReportDataset; + +import java.util.List; + +/** + * 租户角色报表数据集访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleReportDatasetMapper extends BaseDaoMapper { + + /** + * 批量插入租户角色和报表数据集之间的关联关系列表。 + * + * @param dataList 租户角色和报表数据集关联关系列表。 + */ + void insertList(List dataList); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleTenantMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleTenantMapper.java new file mode 100644 index 0000000..6d060fa --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/SysTenantRoleTenantMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.tenant.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.tenant.model.SysTenantRoleTenant; + +import java.util.List; + +/** + * 租户角色租户数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleTenantMapper extends BaseDaoMapper { + + /** + * 批量新增租户角色和租户的关联关系。 + * + * @param sysTenantRoleTenantList 租户角色和租户的关联关系列表。 + */ + void insertList(List sysTenantRoleTenantList); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/DeployPluginMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/DeployPluginMapper.xml new file mode 100644 index 0000000..8c5456e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/DeployPluginMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantDatasourceMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantDatasourceMapper.xml new file mode 100644 index 0000000..f441300 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantDatasourceMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantExtMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantExtMapper.xml new file mode 100644 index 0000000..4376c69 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantExtMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMapper.xml new file mode 100644 index 0000000..0efff5e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMapper.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + AND xy_sys_tenant.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + AND xy_sys_tenant.tenant_code = #{sysTenantFilter.tenantCode} + + + + AND xy_sys_tenant.show_name LIKE #{safeShowName} + + + AND xy_sys_tenant.datasource_type = #{sysTenantFilter.datasourceType} + + + AND xy_sys_tenant.available = #{sysTenantFilter.available} + + + AND xy_sys_tenant.create_time >= #{sysTenantFilter.createTimeStart} + + + AND xy_sys_tenant.create_time <= #{sysTenantFilter.createTimeEnd} + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuMapper.xml new file mode 100644 index 0000000..87da03e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuMapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuPermCodeMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuPermCodeMapper.xml new file mode 100644 index 0000000..fddfbb8 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantMenuPermCodeMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_menu_perm_code(menu_id, perm_code_id) VALUES + + (#{item.menuId}, #{item.permCodeId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodeMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodeMapper.xml new file mode 100644 index 0000000..3368a36 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodeMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodePermMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodePermMapper.xml new file mode 100644 index 0000000..f1fbd34 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermCodePermMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_perm_code_perm(perm_code_id, perm_id) VALUES + + (#{item.permCodeId}, #{item.permId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermMapper.xml new file mode 100644 index 0000000..d9b2488 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermModuleMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermModuleMapper.xml new file mode 100644 index 0000000..d1e0274 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantPermModuleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMapper.xml new file mode 100644 index 0000000..b258ba9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + AND role_name LIKE #{safeRoleName} + + + AND xy_sys_tenant_role.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMenuMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMenuMapper.xml new file mode 100644 index 0000000..4999923 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleMenuMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_role_menu(tenant_role_id, menu_id) VALUES + + (#{item.tenantRoleId}, #{item.menuId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleOnlinePageMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleOnlinePageMapper.xml new file mode 100644 index 0000000..f84a896 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleOnlinePageMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_role_online_page(tenant_role_id, page_id) VALUES + + (#{item.tenantRoleId}, #{item.pageId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleReportDatasetMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleReportDatasetMapper.xml new file mode 100644 index 0000000..1ac1129 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleReportDatasetMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_role_report_ds(tenant_role_id, dataset_id) VALUES + + (#{item.tenantRoleId}, #{item.datasetId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleTenantMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleTenantMapper.xml new file mode 100644 index 0000000..e2c448c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dao/mapper/SysTenantRoleTenantMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + INSERT INTO xy_sys_tenant_role_tenant(tenant_role_id, tenant_id) VALUES + + (#{item.tenantRoleId}, #{item.tenantId}) + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDatasourceDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDatasourceDto.java new file mode 100644 index 0000000..7103baf --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDatasourceDto.java @@ -0,0 +1,81 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户数据源Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户数据源Dto对象") +@Data +public class SysTenantDatasourceDto { + + /** + * 租户数据源Id。 + */ + @Schema(description = "租户数据源Id") + @NotNull(message = "租户数据源Id不能为空!", groups = {UpdateGroup.class}) + private Long datasourceId; + + /** + * 租户数据源名称。 + */ + @Schema(description = "租户数据源名称") + @NotBlank(message = "租户数据源名称不能为空!") + private String datasourceName; + + /** + * 租户所在数据源的类型值。 + */ + @Schema(description = "租户所在数据源的类型值") + @NotNull(message = "租户所在数据源的类型值不能为空!") + private Integer datasourceType; + + /** + * 主机名。 + */ + @Schema(description = "主机名") + @NotBlank(message = "主机名不能为空!") + private String databaseHost; + + /** + * 端口号。 + */ + @Schema(description = "端口号") + @NotNull(message = "端口号不能为空!") + private Integer port; + + /** + * 用户名。 + */ + @Schema(description = "用户名") + @NotBlank(message = "用户名不能为空!") + private String username; + + /** + * 密码。 + */ + @Schema(description = "密码") + @NotBlank(message = "密码不能为空!") + private String password; + + /** + * 模式名。 + */ + @Schema(description = "模式名") + private String schemaName; + + /** + * 数据库名。 + */ + @Schema(description = "数据库名") + @NotBlank(message = "数据库名不能为空!") + private String databaseName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDto.java new file mode 100644 index 0000000..e710fb4 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantDto.java @@ -0,0 +1,66 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户Dto对象") +@Data +public class SysTenantDto { + + /** + * 租户Id。 + */ + @Schema(description = "租户Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户Id不能为空!", groups = {UpdateGroup.class}) + private Long tenantId; + + /** + * 租户的英文编码。 + */ + @Schema(description = "租户的英文编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户编码不能为空!") + private String tenantCode; + + /** + * 租户的中文显示名称。 + */ + @Schema(description = "租户的中文显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户编码不能为空!") + private String showName; + + /** + * 租户所在数据源的类型值。 + */ + @Schema(description = "租户所在数据源的类型值", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户所在数据源的类型值不能为空!") + private Integer datasourceType; + + /** + * 是否可用标记。 + */ + @Schema(description = "是否可用标记", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户是否可用标记不能为空!", groups = {UpdateGroup.class}) + private Boolean available; + + /** + * [创建时间] 范围过滤起始值(>=)。 + */ + @Schema(description = "[创建时间] 范围过滤起始值(>=)") + private String createTimeStart; + + /** + * [创建时间] 范围过滤结束值(<=)。 + */ + @Schema(description = "[创建时间] 范围过滤结束值(<=)") + private String createTimeEnd; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantMenuDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantMenuDto.java new file mode 100644 index 0000000..af3c6f4 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantMenuDto.java @@ -0,0 +1,86 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysMenuType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户菜单资源Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户菜单资源Dto对象") +@Data +public class SysTenantMenuDto { + + /** + * 租户菜单Id。 + */ + @Schema(description = "租户菜单Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户菜单Id不能为空!", groups = {UpdateGroup.class}) + private Long menuId; + + /** + * 菜单显示名称。 + */ + @Schema(description = "菜单显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "菜单显示名称不能为空!") + private String menuName; + + /** + * 菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @Schema(description = "菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "菜单类型不能为空!") + @ConstDictRef(constDictClass = SysMenuType.class, message = "数据验证失败,菜单类型为无效值!") + private Integer menuType; + + /** + * 父菜单Id,目录菜单的父菜单为null。 + */ + @Schema(description = "父菜单Id,目录菜单的父菜单为null") + private Long parentId; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @Schema(description = "前端表单路由名称,仅用于menu_type为1的菜单类型") + private String formRouterName; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @Schema(description = "统计页面主键Id") + private Long reportPageId; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @Schema(description = "在线表单主键Id") + private Long onlineFormId; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @Schema(description = "菜单显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "菜单显示顺序不能为空!") + private Integer showOrder; + + /** + * 菜单图标。 + */ + @Schema(description = "菜单图标") + private String icon; + + /** + * 附加信息。 + */ + @Schema(description = "附加信息") + private String extraData; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermCodeDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermCodeDto.java new file mode 100644 index 0000000..dd776f6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermCodeDto.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysPermCodeType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户权限字Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限字Dto对象") +@Data +public class SysTenantPermCodeDto { + + /** + * 租户权限字Id。 + */ + @Schema(description = "租户权限字Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限字Id不能为空!", groups = {UpdateGroup.class}) + private Long permCodeId; + + /** + * 上级权限字Id。 + */ + @Schema(description = "租户权限模块Id") + private Long parentId; + + /** + * 租户权限字标识(一般为有含义的英文字符串)。 + */ + @Schema(description = "租户权限字标识(一般为有含义的英文字符串)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户权限字编码不能为空!") + private String permCode; + + /** + * 租户权限类型(0: 表单 1: UI片段 2: 操作)。 + */ + @Schema(description = "租户权限类型(0: 表单 1: UI片段 2: 操作)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限字类型不能为空!") + @ConstDictRef(constDictClass = SysPermCodeType.class, message = "数据验证失败,权限类型为无效值!") + private Integer permCodeType; + + /** + * 租户权限字显示顺序(数值越小,越靠前)。 + */ + @Schema(description = "租户权限字显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限字显示顺序不能为空!") + private Integer showOrder; + + /** + * 租户权限字显示名称。 + */ + @Schema(description = "租户权限字显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户权限字显示名称不能为空!") + private String showName; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermDto.java new file mode 100644 index 0000000..9a12e3c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermDto.java @@ -0,0 +1,54 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户权限资源Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限资源Dto对象") +@Data +public class SysTenantPermDto { + + /** + * 租户权限资源Id。 + */ + @Schema(description = "租户权限模块Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限Id不能为空!", groups = {UpdateGroup.class}) + private Long permId; + + /** + * 租户权限所在的权限模块Id。 + */ + @Schema(description = "租户权限所在的权限模块Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限模块Id不能为空!") + private Long moduleId; + + /** + * 租户权限名称。 + */ + @Schema(description = "租户权限名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户权限名称不能为空!") + private String permName; + + /** + * 租户权限关联的URL。 + */ + @Schema(description = "租户权限关联的URL", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户权限关联的url不能为空!") + private String url; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @Schema(description = "权限在当前模块下的顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限显示顺序不能为空!") + private Integer showOrder; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermModuleDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermModuleDto.java new file mode 100644 index 0000000..cbb49be --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantPermModuleDto.java @@ -0,0 +1,56 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysPermModuleType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户权限模块Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限模块Dto对象") +@Data +public class SysTenantPermModuleDto { + + /** + * 租户权限模块Id。 + */ + @Schema(description = "租户权限模块Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户权限模块Id不能为空!", groups = {UpdateGroup.class}) + private Long moduleId; + + /** + * 上级权限模块Id。 + */ + @Schema(description = "上级权限模块Id") + private Long parentId; + + /** + * 租户权限模块名称。 + */ + @Schema(description = "租户权限模块名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户权限模块名称不能为空!") + private String moduleName; + + /** + * 租户权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @Schema(description = "租户权限模块类型(0: 普通模块 1: Controller模块)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "模块类型不能为空!") + @ConstDictRef(constDictClass = SysPermModuleType.class, message = "数据验证失败,租户权限模块类型为无效值!") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @Schema(description = "权限模块在当前层级下的顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限模块显示顺序不能为空!") + private Integer showOrder; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleDto.java new file mode 100644 index 0000000..3fcb69a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleDto.java @@ -0,0 +1,33 @@ +package apelet.tenantadmin.tenant.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 租户角色Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户角色Dto对象") +@Data +public class SysTenantRoleDto { + + /** + * 租户角色Id。 + */ + @Schema(description = "租户角色Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "租户角色Id不能为空!", groups = {UpdateGroup.class}) + private Long tenantRoleId; + + /** + * 租户角色名称。 + */ + @Schema(description = "租户角色名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户角色名称不能为空!") + private String roleName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleMenuDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleMenuDto.java new file mode 100644 index 0000000..0592416 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleMenuDto.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.tenant.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 租户角色和租户菜单多对多关联Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户角色和租户菜单多对多关联Dto对象") +@Data +public class SysTenantRoleMenuDto { + + /** + * 租户角色Id。 + */ + @Schema(description = "租户角色Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long tenantRoleId; + + /** + * 租户菜单Id。 + */ + @Schema(description = "租户菜单Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long menuId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleTenantDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleTenantDto.java new file mode 100644 index 0000000..8dd58fb --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/dto/SysTenantRoleTenantDto.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.tenant.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 租户角色与租户多对多关联Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户角色与租户多对多关联Dto对象") +@Data +public class SysTenantRoleTenantDto { + + /** + * 租户角色Id。 + */ + @Schema(description = "租户角色Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long tenantRoleId; + + /** + * 租户Id。 + */ + @Schema(description = "租户Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long tenantId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/DeployPlugin.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/DeployPlugin.java new file mode 100644 index 0000000..5124d04 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/DeployPlugin.java @@ -0,0 +1,101 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.tenant.vo.DeployPluginVo; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 自定义模板 + * 热部署插件表 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@TableName("deploy_plugin") +public class DeployPlugin extends BaseModel implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @TableField(value = "number") + private String number; + + @TableField(value = "name") + private String name; + + //运维工单号 + @TableField(value = "work_number") + private String workNumber; + + //任务类型 + @TableField(value = "task_type") + private String taskType; + + //数据中心 + @TableField(value = "data_center") + private String dataCenter; + + //上传文件地址 + @TableField(value = "files") + private String files; + + //附件下载地址 + @TableField(value = "files_list") + private String fileList; + + private String filename; + + //状态 + @TableField(value = "status") + private Integer status; + + //提交时间 + @TableField(value = "submit_time") + private Date submitTime; + + //处理时间 + @TableField(value = "handle_time") + private Date handleTime; + + //部署说明 + @TableField(value = "deploy_explain") + private String deployExplain; + + //提单人 + @TableField(value = "delivery_user") + private String deliveryUser; + + //处理人 + @TableField(value = "handle_user") + private String handleUser; + + //异常信息 + @TableField(value = "abnormal") + private String abnormal; + + + @Mapper + public interface DeployPluginModelMapper extends BaseModelMapper { + } + + public static final DeployPluginModelMapper INSTANCE = Mappers.getMapper(DeployPluginModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenant.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenant.java new file mode 100644 index 0000000..fdafa4d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenant.java @@ -0,0 +1,89 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.annotation.RelationDict; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.tenant.vo.SysTenantVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +/** + * 租户实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant") +public class SysTenant extends BaseModel { + + /** + * 租户Id。 + */ + @TableId(value = "tenant_id") + private Long tenantId; + + /** + * 租户的英文编码。 + */ + @TableField(value = "tenant_code") + private String tenantCode; + + /** + * 租户的中文显示名称。 + */ + @TableField(value = "show_name") + private String showName; + + /** + * 租户所在数据源的类型值。 + */ + @TableField(value = "datasource_type") + private Integer datasourceType; + + /** + * 是否可用标记。 + */ + private Boolean available; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + /** + * [创建时间] 范围过滤起始值(>=)。 + */ + @TableField(exist = false) + private String createTimeStart; + + /** + * [创建时间] 范围过滤结束值(<=)。 + */ + @TableField(exist = false) + private String createTimeEnd; + + /** + * 租户数据源字典关联数据。 + */ + @RelationDict( + masterIdField = "datasourceType", + slaveModelClass = SysTenantDatasource.class, + slaveIdField = "datasourceType", + slaveNameField = "datasourceName") + @TableField(exist = false) + private Map datasourceTypeDictMap; + + @Mapper + public interface SysTenantModelMapper extends BaseModelMapper { + } + public static final SysTenantModelMapper INSTANCE = Mappers.getMapper(SysTenantModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantDatasource.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantDatasource.java new file mode 100644 index 0000000..58505a9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantDatasource.java @@ -0,0 +1,74 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.base.model.BaseModel; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 租户数据源实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@TableName(value = "xy_sys_tenant_datasource") +public class SysTenantDatasource extends BaseModel { + + /** + * 租户数据源Id。 + */ + @TableId(value = "datasource_id") + private Long datasourceId; + + /** + * 租户数据源名。 + */ + @TableField(value = "datasource_name") + private String datasourceName; + + /** + * 租户所在数据源的类型值。 + */ + @TableField(value = "datasource_type") + private Integer datasourceType; + + /** + * 主机名。 + */ + @TableField(value = "database_host") + private String databaseHost; + + /** + * 端口号。 + */ + @TableField(value = "port") + private Integer port; + + /** + * 用户名。 + */ + @TableField(value = "username") + private String username; + + /** + * 密码。 + */ + @TableField(value = "password") + private String password; + + /** + * 模式名。 + */ + @TableField(value = "schema_name") + private String schemaName; + + /** + * 数据库名。 + */ + @TableField(value = "database_name") + private String databaseName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantExt.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantExt.java new file mode 100644 index 0000000..065c8d3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantExt.java @@ -0,0 +1,30 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户扩展信息的实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_ext") +public class SysTenantExt { + + /** + * 租户Id。 + */ + @TableId(value = "tenant_id") + private Long tenantId; + + /** + * 租户的在线表单页面Id。 + * 为了保证每个租户的在线表单页面Id的唯一性和永久一致性,这里会保存一份。 + */ + @TableField(value = "tenant_online_page_ids") + private String tenantOnlinePageIds; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenu.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenu.java new file mode 100644 index 0000000..bab6a58 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenu.java @@ -0,0 +1,127 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.tenant.vo.SysTenantMenuVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 租户菜单资源实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant_menu") +public class SysTenantMenu extends BaseModel { + + /** + * 租户菜单Id。 + */ + @TableId(value = "menu_id") + private Long menuId; + + /** + * 菜单显示名称。 + */ + @TableField(value = "menu_name") + private String menuName; + + /** + * 菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @TableField(value = "menu_type") + private Integer menuType; + + /** + * 父菜单Id,目录菜单的父菜单为null。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @TableField(value = "form_router_name") + private String formRouterName; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @TableField(value = "report_page_id") + private Long reportPageId; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @TableField(value = "online_form_id") + private Long onlineFormId; + + /** + * 在线表单菜单的权限控制类型,具体值可参考SysOnlineMenuPermType常量对象。 + */ + @TableField(value = "online_menu_perm_type") + private Integer onlineMenuPermType; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 菜单图标。 + */ + private String icon; + + /** + * 附加信息。 + */ + @TableField(value = "extra_data") + private String extraData; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationManyToMany( + relationMasterIdField = "menuId", + relationModelClass = SysTenantMenuPermCode.class) + @TableField(exist = false) + private List sysMenuPermCodeList; + + @Mapper + public interface SysTenantMenuModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysMenuVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysMenuPermCodeList", expression = "java(mapToBean(sysMenuVo.getSysMenuPermCodeList(), apelet.tenantadmin.tenant.model.SysTenantMenuPermCode.class))") + @Override + SysTenantMenu toModel(SysTenantMenuVo sysMenuVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysMenu 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysMenuPermCodeList", expression = "java(beanToMap(sysMenu.getSysMenuPermCodeList(), false))") + @Override + SysTenantMenuVo fromModel(SysTenantMenu sysMenu); + } + public static final SysTenantMenuModelMapper INSTANCE = Mappers.getMapper(SysTenantMenuModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenuPermCode.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenuPermCode.java new file mode 100644 index 0000000..aae8a11 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantMenuPermCode.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户菜单与租户权限字关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_menu_perm_code") +public class SysTenantMenuPermCode { + + /** + * 关联租户菜单Id。 + */ + @TableField(value = "menu_id") + private Long menuId; + + /** + * 关联租户权限字Id。 + */ + @TableField(value = "perm_code_id") + private Long permCodeId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPerm.java new file mode 100644 index 0000000..631802d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPerm.java @@ -0,0 +1,66 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.annotation.RelationDict; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.upms.model.SysPermModule; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 租户权限资源实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant_perm") +public class SysTenantPerm extends BaseModel { + + /** + * 租户权限资源Id。 + */ + @TableId(value = "perm_id") + private Long permId; + + /** + * 租户权限所在的权限模块Id。 + */ + @TableField(value = "module_id") + private Long moduleId; + + /** + * 租户权限名称。 + */ + @TableField(value = "perm_name") + private String permName; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 租户权限关联的URL。 + */ + private String url; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationDict( + masterIdField = "moduleId", + slaveModelClass = SysPermModule.class, + slaveIdField = "moduleId", + slaveNameField = "moduleName") + @TableField(exist = false) + private Map moduleIdDictMap; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCode.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCode.java new file mode 100644 index 0000000..c770fae --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCode.java @@ -0,0 +1,98 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.tenant.vo.SysTenantPermCodeVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 租户权限字实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant_perm_code") +public class SysTenantPermCode extends BaseModel { + + /** + * 租户权限字Id。 + */ + @TableId(value = "perm_code_id") + private Long permCodeId; + + /** + * 租户权限字标识(一般为有含义的英文字符串)。 + */ + @TableField(value = "perm_code") + private String permCode; + + /** + * 租户权限类型(0: 表单 1: UI片段 2: 操作)。 + */ + @TableField(value = "perm_code_type") + private Integer permCodeType; + + /** + * 上级权限字Id。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 租户权限字显示顺序(数值越小,越靠前)。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 租户权限字显示名称。 + */ + @TableField(value = "show_name") + private String showName; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationManyToMany( + relationMasterIdField = "permCodeId", + relationModelClass = SysTenantPermCodePerm.class) + @TableField(exist = false) + private List sysPermCodePermList; + + @Mapper + public interface SysTenantPermCodeModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysPermCodeVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysPermCodePermList", expression = "java(mapToBean(sysPermCodeVo.getSysPermCodePermList(), apelet.tenantadmin.tenant.model.SysTenantPermCodePerm.class))") + @Override + SysTenantPermCode toModel(SysTenantPermCodeVo sysPermCodeVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysPermCode 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysPermCodePermList", expression = "java(beanToMap(sysPermCode.getSysPermCodePermList(), false))") + @Override + SysTenantPermCodeVo fromModel(SysTenantPermCode sysPermCode); + } + public static final SysTenantPermCodeModelMapper INSTANCE = Mappers.getMapper(SysTenantPermCodeModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCodePerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCodePerm.java new file mode 100644 index 0000000..2670313 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermCodePerm.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 权限字与权限资源关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_perm_code_perm") +public class SysTenantPermCodePerm { + + /** + * 权限字Id。 + */ + @TableField(value = "perm_code_id") + private Long permCodeId; + + /** + * 权限Id。 + */ + @TableField(value = "perm_id") + private Long permId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermModule.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermModule.java new file mode 100644 index 0000000..ee976ca --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantPermModule.java @@ -0,0 +1,60 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.base.model.BaseModel; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 租户权限模块实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant_perm_module") +public class SysTenantPermModule extends BaseModel { + + /** + * 租户权限模块Id。 + */ + @TableId(value = "module_id") + private Long moduleId; + + /** + * 租户权限模块名称。 + */ + @TableField(value = "module_name") + private String moduleName; + + /** + * 上级权限模块Id。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 租户权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @TableField(value = "module_type") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @TableField(exist = false) + private List sysPermList; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRole.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRole.java new file mode 100644 index 0000000..a7faca7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRole.java @@ -0,0 +1,46 @@ +package apelet.tenantadmin.tenant.model; + +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.tenant.vo.SysTenantRoleVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 租户角色实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_tenant_role") +public class SysTenantRole extends BaseModel { + + /** + * 租户角色Id。 + */ + @TableId(value = "tenant_role_id") + private Long tenantRoleId; + + /** + * 租户角色名称。 + */ + @TableField(value = "role_name") + private String roleName; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @Mapper + public interface SysTenantRoleModelMapper extends BaseModelMapper { + } + public static final SysTenantRoleModelMapper INSTANCE = Mappers.getMapper(SysTenantRoleModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleMenu.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleMenu.java new file mode 100644 index 0000000..6da6c3a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleMenu.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户角色与菜单多对多关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_role_menu") +public class SysTenantRoleMenu { + + /** + * 租户角色Id。 + */ + @TableField(value = "tenant_role_id") + private Long tenantRoleId; + + /** + * 租户菜单Id。 + */ + @TableField(value = "menu_id") + private Long menuId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleOnlinePage.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleOnlinePage.java new file mode 100644 index 0000000..134f025 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleOnlinePage.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户角色与在线表单页面多对多关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_role_online_page") +public class SysTenantRoleOnlinePage { + + /** + * 租户角色Id。 + */ + @TableField(value = "tenant_role_id") + private Long tenantRoleId; + + /** + * 在线表单页面Id。 + */ + @TableField(value = "page_id") + private Long pageId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleReportDataset.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleReportDataset.java new file mode 100644 index 0000000..37ef5d0 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleReportDataset.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户角色与报表数据集多对多关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_role_report_ds") +public class SysTenantRoleReportDataset { + + /** + * 租户角色Id。 + */ + @TableField(value = "tenant_role_id") + private Long tenantRoleId; + + /** + * 报表数据集Id。 + */ + @TableField(value = "dataset_id") + private Long datasetId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleTenant.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleTenant.java new file mode 100644 index 0000000..b80fc9f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/model/SysTenantRoleTenant.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.tenant.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 租户角色与租户多对多关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_tenant_role_tenant") +public class SysTenantRoleTenant { + + /** + * 权限字Id。 + */ + @TableField(value = "tenant_role_id") + private Long tenantRoleId; + + /** + * 租户Id。 + */ + @TableField(value = "tenant_id") + private Long tenantId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/Plugin.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/Plugin.java new file mode 100644 index 0000000..b8fec4d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/Plugin.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.tenant.plugin; + +import java.util.Map; + +/** + * @author: HF + * @description: TODO + * @date: 2024/5/27 18:43 + */ +public interface Plugin { + + Map process(Map params); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/PluginService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/PluginService.java new file mode 100644 index 0000000..3383219 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/plugin/PluginService.java @@ -0,0 +1,200 @@ +package apelet.tenantadmin.tenant.plugin; + +import apelet.common.core.exception.MyRuntimeException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import java.io.*; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * 热部署插件服务 + */ +@Service +public class PluginService { + + private URLClassLoader urlClassLoader; + + private final Map packageNameMap = new HashMap<>(); + + public List reloadPlugin(String jarPath) { + List pluginList = new ArrayList<>(); + // 1. 创建一个File对象,指向外部jar包的路径 + File externalJar = new File(jarPath); + try { + JarFile jar = new JarFile(externalJar); + Enumeration entries = jar.entries(); + String s = "jar:file:" + externalJar.getAbsolutePath() + "!/"; + URL[] urls = {new URL(s)}; + urlClassLoader = URLClassLoader.newInstance(urls); + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + if (je.isDirectory() || !je.getName().endsWith(".class")) { + continue; + } + String className = je.getName().substring(0, je.getName().length() - 6); + className = className.replace('/', '.'); + Class c = urlClassLoader.loadClass(className); + if (implementsPlugin(c, Plugin.class)) { + List list = Arrays.asList(c.getName().split("\\.")); + packageNameMap.put(list.get(list.size() - 1), c.getName()); + pluginList.add(c.getName()); + } + // 在这里你可以使用反射来调用类的方法,例如: + // Method method = c.getMethod("methodName", parameterTypes); + // Object result = method.invoke(null, arguments); + } + jar.close(); + } catch (Exception e) { + e.printStackTrace(); + throw new MyRuntimeException(e.getMessage()); + } + return pluginList; + } + + + //根据指定plug名称,在缓存中查找插件class,并反射执行 + public Object process(String pluginName, Map params) { + try { + Class clazz = urlClassLoader.loadClass(packageNameMap.get(pluginName)); + Object o = clazz.newInstance(); + Method method = clazz.getMethod("process", Map.class); + return method.invoke(o, params); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + public boolean implementsPlugin(Class c, Class pluginClass) { + boolean implementsPlugin = false; + for (Class iface : c.getInterfaces()) { + if (iface.getName().equals(pluginClass.getName())) { + implementsPlugin = true; + break; + } + } + return implementsPlugin; + } + + public List reloadPlugin(String rootPath, String packageName) { + List result = new ArrayList<>(); + try { + URL[] urls = new URL[]{new URL("file:" + rootPath)}; + URLClassLoader urlClassLoader = new URLClassLoader(urls); + String plugDirPath = rootPath + StringUtils.replace(packageName, ".", "\\"); + File plugDir = new File(plugDirPath); + List classFiles = getClassFiles(plugDir); + for (File classFile : classFiles) { + String path = classFile.getPath(); + path = path.substring(0, path.length() - 6); + String s = "apelet" + path.split("apelet")[1]; + s = s.replace('\\', '.'); + Class pluginClass = urlClassLoader.loadClass(s); + if (implementsPlugin(pluginClass, Plugin.class)) { + result.add(pluginClass.getSimpleName()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + public static List getClassFiles(File directory) { + List classFiles = new ArrayList<>(); + if (directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + classFiles.addAll(getClassFiles(file)); + } else if (file.getName().endsWith(".class")) { + classFiles.add(file); + } + } + } + } + return classFiles; + } + + + /*public static void main(String[] args) { + // 生成密钥 + SecretKey secretKey = null; + try { + secretKey = generateKey(); + // 加密文件并保存到指定位置 + encryptFile("D:\\Temp\\xxl-job-admin.jar", "D:\\Temp\\xxl-job-admin-jiami.jar", secretKey); + + // 解密文件并保存到指定位置 + decryptFile("D:\\Temp\\xxl-job-admin-jiami.jar", "D:\\Temp\\xxl-job-admin-2.jar", secretKey); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } + }*/ + + public SecretKey generateKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(128); + return keyGenerator.generateKey(); + } + + // 加密文件 + public void encryptFile(String inputFile, String outputFile, SecretKey secretKey) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + try (InputStream inputStream = new FileInputStream(inputFile); + OutputStream outputStream = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] outputBuffer = cipher.update(buffer, 0, bytesRead); + if (outputBuffer != null) { + outputStream.write(outputBuffer); + } + } + byte[] outputBytes = cipher.doFinal(); + if (outputBytes != null) { + outputStream.write(outputBytes); + } + } + } + + public void decryptFile(String inputFile, String outputFile, SecretKey secretKey) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + + try (InputStream inputStream = new FileInputStream(inputFile); + OutputStream outputStream = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[64]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] outputBuffer = cipher.update(buffer, 0, bytesRead); + if (outputBuffer != null) { + outputStream.write(outputBuffer); + } + } + byte[] outputBytes = cipher.doFinal(); + if (outputBytes != null) { + outputStream.write(outputBytes); + } + } + } + + + + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasFlowQuartzJob.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasFlowQuartzJob.java new file mode 100644 index 0000000..e61cf8f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasFlowQuartzJob.java @@ -0,0 +1,33 @@ +package apelet.tenantadmin.tenant.quartz.job; + +import apelet.common.eas.service.IEasFlowService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Configuration +@EnableScheduling +@Slf4j +public class MySyncEasFlowQuartzJob implements Job{ + + @Autowired + private IEasFlowService easFlowService; + + @Override + public void execute(JobExecutionContext context) { + easFlowService.syncEasFlow(""); + } + +// @Scheduled(cron = "0 0/20 6-22 * * ?") + public void sync(){ + log.warn("=========================================="); + log.warn("===============开始同步eas流程==============="); + log.warn("=========================================="); + easFlowService.syncEasFlow(""); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasUserQuartzJob.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasUserQuartzJob.java new file mode 100644 index 0000000..a13fa75 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/quartz/job/MySyncEasUserQuartzJob.java @@ -0,0 +1,34 @@ +package apelet.tenantadmin.tenant.quartz.job; + +import apelet.tenantadmin.upms.service.SysUserService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DisallowConcurrentExecution; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@EnableScheduling +public class MySyncEasUserQuartzJob implements Job{ + + @Autowired + private SysUserService sysUserService; + + @Override + public void execute(JobExecutionContext context) { + sysUserService.syncEasUSer(); + } + +// @Scheduled(cron = "0 0/10 6-22 * * ?") + public void sync(){ + log.warn("=========================================="); + log.warn("===============开始同步eas用户=============="); + log.warn("=========================================="); + sysUserService.syncEasUSer(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/IDeployPluginService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/IDeployPluginService.java new file mode 100644 index 0000000..1dce96c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/IDeployPluginService.java @@ -0,0 +1,46 @@ +package apelet.tenantadmin.tenant.service; + + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.DeployPlugin; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 热部署插件表 服务类 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +public interface IDeployPluginService extends IBaseService { + + + /** + * 分页 获取插件 + * @param deployPlugin + * @param s + * @return + */ + List getDeployPluginList(DeployPlugin deployPlugin, String s); + + + /** + * 加载插件 + * + * @param id + * @return + */ + List reloadPlugin(List id); + + /** + * 执行插件 + * + * @param pluginName 插件全包名 ( apelet.tenantadmin.tenant.plugin.MyTestPlugin) + * @param map 插件方法参数 + * @return + */ + Object executePlugin(String pluginName, Map map); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantDatasourceService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantDatasourceService.java new file mode 100644 index 0000000..7c978af --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantDatasourceService.java @@ -0,0 +1,40 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.SysTenantDatasource; + +import java.util.List; + +/** + * 租户权限资源数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantDatasourceService extends IBaseService { + + /** + * 新增的租户数据源。 + * + * @param datasource 新增的租户数据源实体对象。 + * @return 新增的租户数据源对象。 + */ + SysTenantDatasource saveNew(SysTenantDatasource datasource); + + /** + * 更新租户数据源。 + * + * @param datasource 更新的租户数据源对象。 + * @param originalDatasource 原有的租户数据源对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenantDatasource datasource, SysTenantDatasource originalDatasource); + + /** + * 获取租户数据源列表。 + * + * @param filter 过滤对象。 + * @return 租户数据源列表。 + */ + List getSysTenantDatasourceList(SysTenantDatasource filter); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantMenuService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantMenuService.java new file mode 100644 index 0000000..a9b1837 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantMenuService.java @@ -0,0 +1,82 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.tenant.model.SysTenantMenu; + +import java.util.*; + +/** + * 租户菜单数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantMenuService extends IBaseService { + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysMenu 新增的租户菜单对象。 + * @param permCodeIdSet 租户权限字Id列表。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenantMenu saveNew(SysTenantMenu sysMenu, Set permCodeIdSet); + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysMenu 更新的租户菜单对象。 + * @param originalSysMenu 原有的租户菜单对象。 + * @param permCodeIdSet 租户权限字Id列表。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenantMenu sysMenu, SysTenantMenu originalSysMenu, Set permCodeIdSet); + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param menuId 租户菜单主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + boolean remove(Long menuId); + + /** + * 根据租户角色Id,获取关联的租户菜单从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的租户菜单数据列表。 + */ + List getNotInSysTenantMenuListByRoleId(Long tenantRoleId, String orderBy); + + /** + * 根据租户角色Id,获取关联的租户菜单从表中和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的租户菜单数据列表。 + */ + List getSysTenantMenuListByRoleId(Long tenantRoleId, String orderBy); + + /** + * 判断当前租户菜单是否存在子菜单。 + * + * @param menuId 租户菜单主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long menuId); + + /** + * 验证租户菜单对象关联的数据是否都合法。 + * + * @param sysMenu 当前操作的对象。 + * @param originalSysMenu 原有对象。 + * @param permCodeIdListString 逗号分隔的权限Id列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData(SysTenantMenu sysMenu, SysTenantMenu originalSysMenu, String permCodeIdListString); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermCodeService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermCodeService.java new file mode 100644 index 0000000..6fe06cb --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermCodeService.java @@ -0,0 +1,65 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.tenant.model.SysTenantPermCode; + +import java.util.*; + +/** + * 租户权限字数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermCodeService extends IBaseService { + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param permCode 新增的租户权限字实体对象。 + * @param permIdSet 租户权限资源Id列表。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenantPermCode saveNew(SysTenantPermCode permCode, Set permIdSet); + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysPermCode 更新的租户权限字对象。 + * @param originalSysPermCode 原有的租户权限字对象。 + * @param permIdSet 租户权限资源Id列表。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenantPermCode sysPermCode, SysTenantPermCode originalSysPermCode, Set permIdSet); + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param permCodeId 租户权限字主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + boolean remove(Long permCodeId); + + /** + * 判断当前租户权限字是否存在下级权限字对象。 + * + * @param permCodeId 租户权限字主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long permCodeId); + + /** + * 验证租户权限字对象关联的数据是否都合法。 + * + * @param sysPermCode 当前操作的对象。 + * @param originalSysPermCode 原有对象。 + * @param permIdListString 逗号分隔的权限资源Id列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData( + SysTenantPermCode sysPermCode, SysTenantPermCode originalSysPermCode, String permIdListString); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermModuleService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermModuleService.java new file mode 100644 index 0000000..6dc2163 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermModuleService.java @@ -0,0 +1,66 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.SysTenantPermModule; + +import java.util.List; + +/** + * 租户权限资源模块数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermModuleService extends IBaseService { + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysPermModule 新增的租户权限资源模块实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenantPermModule saveNew(SysTenantPermModule sysPermModule); + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysPermModule 更新的租户权限资源模块对象。 + * @param originalSysPermModule 原有的租户权限资源模块对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenantPermModule sysPermModule, SysTenantPermModule originalSysPermModule); + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param moduleId 待删除的租户权限资源模块主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + boolean remove(Long moduleId); + + /** + * 获取权限模块资源及其关联的权限资源列表。 + * + * @return 权限资源模块及其关联的权限资源列表。 + */ + List getPermModuleAndPermList(); + + /** + * 判断是否存在下级租户权限资源模块。 + * + * @param moduleId 租户权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long moduleId); + + /** + * 判断是否存在租户权限数据。 + * + * @param moduleId 租户权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasModulePerms(Long moduleId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermService.java new file mode 100644 index 0000000..0c7d63a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantPermService.java @@ -0,0 +1,51 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.SysTenantPerm; + +import java.util.List; + +/** + * 租户权限资源数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantPermService extends IBaseService { + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param perm 新增的租户权限资源实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenantPerm saveNew(SysTenantPerm perm); + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param perm 更新的租户权限资源对象。 + * @param originalPerm 原有的租户权限资源对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenantPerm perm, SysTenantPerm originalPerm); + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param permId 待删除的租户权限资源主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + boolean remove(Long permId); + + /** + * 获取租户权限数据列表。 + * + * @param sysPermFilter 过滤对象。 + * @return 权限列表。 + */ + List getPermListWithRelation(SysTenantPerm sysPermFilter); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantRoleService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantRoleService.java new file mode 100644 index 0000000..bb3a30b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantRoleService.java @@ -0,0 +1,179 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.*; + +import java.util.*; + +/** + * 租户角色数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantRoleService extends IBaseService { + + /** + * 填充租户角色实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param tenantRole 新增的租户角色对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenantRole saveNew(SysTenantRole tenantRole); + + /** + * 更新租户角色对象。 + * + * @param tenantRole 更新的租户角色对象。 + * @param originalTenantRole 原有的租户角色对象。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysTenantRole tenantRole, SysTenantRole originalTenantRole); + + /** + * 删除指定的租户角色及其关联数据。 + * + * @param tenantRoleId 租户角色主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long tenantRoleId); + + /** + * 获取租户角色列表。 + * + * @param filter 租户角色过滤对象。 + * @param orderBy 排序参数。 + * @return 租户角色列表。 + */ + List getSysTenantRoleList(SysTenantRole filter, String orderBy); + + /** + * 判断指定租户角色下是否包含租户数据。 + * + * @param tenantRoleId 租户角色主键Id。 + * @return 包含返回true,否则false。 + */ + boolean hasTenantByRoleId(Long tenantRoleId); + + /** + * 添加租户角色和租户之间的多对多关系。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantRoleTenantList 租户角色和租户的关联对象列表。 + */ + void addTenantRoleTenantList(Long tenantRoleId, List sysTenantRoleTenantList); + + /** + * 移除租户角色和租户之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param tenantId 租户Id。 + */ + void removeTenantRoleTenant(Long tenantRoleId, Long tenantId); + + /** + * 移除租户角色和租户之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param tenantId 租户Id。 + */ + void removeTenantRoleTenantAndSync(Long tenantRoleId, Long tenantId); + + /** + * 判断指定租户角色下是否包含租户关联数据。 + * + * @param tenantRoleId 租户角色主键Id。 + * @return 包含返回true,否则false。 + */ + boolean hasTenantRelatedDataByTenantRoleId(Long tenantRoleId); + + /** + * 添加租户角色和租户菜单之间的多对多关系。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param sysTenantRoleMenuList 租户角色和租户菜单的关联对象列表。 + */ + void addTenantRoleMenuList(Long tenantRoleId, List sysTenantRoleMenuList); + + /** + * 移除租户角色和租户菜单之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param tenantMenuId 租户菜单Id。 + */ + void removeTenantRoleMenu(Long tenantRoleId, Long tenantMenuId); + + /** + * 填充租户角色与菜单多对多关联实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 移除租户角色和租户菜单之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param menuId 租户菜单Id。 + */ + void removeTenantRoleMenuAndSync(Long tenantRoleId, Long menuId); + + /** + * 添加租户角色和报表数据集之间的多对多关系。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param datasetIds 报表数据集Id集合。 + */ + void addTenantRoleReportDatasetList(Long tenantRoleId, Set datasetIds); + + /** + * 移除租户角色和报表数据集之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param datasetId 报表数据集Id。 + */ + void removeTenantRoleReportDataset(Long tenantRoleId, Long datasetId); + + /** + * 移除租户角色和报表数据集之间的多对多关系,并发送消息到RocketMQ,同步远程数据。 + * + * @param tenantRoleId 租户角色Id。 + * @param datasetId 报表数据集Id。 + */ + void removeTenantRoleReportDatasetAndSync(Long tenantRoleId, Long datasetId); + + /** + * 获取指定租户角色关联的报表数据集Id集合。 + * + * @param tenantRoleId 租户角色主键Id。 + * @return 指定租户角色关联的报表数据集Id集合。 + */ + List getSysTenantRoleReportDatasetIdList(Long tenantRoleId); + + /** + * 获取指定租户角色关联的在线表单页面Id集合。 + * + * @param tenantRoleId 租户角色主键Id。 + * @return 指定租户角色关联的在线表单页面Id集合。 + */ + List getSysTenantRoleOnlinePageIdList(Long tenantRoleId); + + /** + * 添加租户角色和在线表单页面之间的多对多关系。 + * + * @param tenantRoleId 租户角色主键Id。 + * @param pageIds 在线表单页面Id集合。 + */ + void addTenantRoleOnlinePageList(Long tenantRoleId, Set pageIds); + + /** + * 移除租户角色和在线表单页面之间的多对多关系。 + * + * @param tenantRoleId 租户角色Id。 + * @param pageId 在线表单页面Id。 + */ + void removeTenantRoleOnlinePage(Long tenantRoleId, Long pageId); + + /** + * 移除租户角色和线表单页面之间的多对多关系,并发送消息到RocketMQ,同步远程数据。 + * + * @param tenantRoleId 租户角色Id。 + * @param pageId 在线表单页面Id。 + */ + void removeTenantRoleOnlinePageAndSync(Long tenantRoleId, Long pageId); +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantService.java new file mode 100644 index 0000000..e5246f0 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/SysTenantService.java @@ -0,0 +1,100 @@ +package apelet.tenantadmin.tenant.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.tenant.model.SysTenant; + +import java.util.List; +import java.util.Map; + +/** + * 租户数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysTenantService extends IBaseService { + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param tenant 新增的租户实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + SysTenant saveNew(SysTenant tenant); + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param tenant 更新的租户对象。 + * @param originalTenant 原有的租户对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + boolean update(SysTenant tenant, SysTenant originalTenant); + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param sysTenant 租户实体对象。 + * @return 删除数据操作和同步发送消息的结果。 + */ + boolean remove(SysTenant sysTenant); + + /** + * 获取租户列表及其关联数据。 + * + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果列表。 + */ + List getSysTenantList(SysTenant sysTenantFilter, String orderBy); + + /** + * 根据租户角色Id,获取关联的租户从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的租户数据列表。 + */ + List getNotInSysTenantListByRoleId(Long tenantRoleId, SysTenant sysTenantFilter, String orderBy); + + /** + * 根据租户角色Id,获取关联的租户从表中和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的租户数据列表。 + */ + List getSysTenantListByRoleId(Long tenantRoleId, SysTenant sysTenantFilter, String orderBy); + + /** + * 查询租户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param url url过滤条件。 + * @return 包含从租户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysTenantPermListWithDetail(Long tenantId, String url); + + /** + * 查询租户的权限字列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从租户到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysTenantPermCodeListWithDetail(Long tenantId, String permCode); + + /** + * 查询租户的菜单列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从租户到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysTenantMenuListWithDetail(Long tenantId, String menuName); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/DeployPluginServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/DeployPluginServiceImpl.java new file mode 100644 index 0000000..fb369a4 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/DeployPluginServiceImpl.java @@ -0,0 +1,226 @@ +package apelet.tenantadmin.tenant.service.impl; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.exception.MyRuntimeException; +import apelet.common.core.object.MyRelationParam; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.plugin.Plugin; +import apelet.tenantadmin.tenant.dao.DeployPluginMapper; +import apelet.tenantadmin.tenant.model.DeployPlugin; +import apelet.tenantadmin.tenant.plugin.PluginService; +import apelet.tenantadmin.tenant.service.IDeployPluginService; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.extern.slf4j.Slf4j; +import apelet.common.core.base.service.BaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + *

+ * 热部署插件表 服务实现类 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +@Service +@Slf4j +public class DeployPluginServiceImpl extends BaseService implements IDeployPluginService { + + @Autowired + DeployPluginMapper deployPluginMapper; + @Autowired + PluginService pluginService; + @Autowired + private ApplicationConfig appConfig; + + @Override + public List getDeployPluginList(DeployPlugin deployPlugin, String orderBy) { + List sysTenantList = deployPluginMapper.getDeployPluginList(deployPlugin, orderBy); + this.buildRelationForDataList(sysTenantList, MyRelationParam.dictOnly()); + return sysTenantList; + } + + @Override + public List reloadPlugin(List ids) { + List result = new ArrayList<>(); + Set idList = new HashSet<>(ids); + List deployPluginList = this.getInList(idList); + deployPluginList.forEach(f -> { + try { + InputStream in = new URL(f.getFiles()).openStream(); + write(appConfig.getUploadFileBaseDir(), f.getFilename(), in); + List strings = pluginService.reloadPlugin(appConfig.getUploadFileBaseDir() + "\\" + f.getFilename()); + result.addAll(strings); + } catch (Exception e) { + throw new MyRuntimeException(e.getMessage()); + } + }); + ids.forEach(f -> { + DeployPlugin deployPlugin = new DeployPlugin(); + deployPlugin.setId(f); + deployPlugin.setStatus(2); + deployPlugin.setHandleTime(new Date()); + deployPluginMapper.updateById(deployPlugin); + }); + return result; + } + + + //根据指定plug名称,反射执行 + @Override + public Object executePlugin(String pluginName, Map params) { + try { + return pluginService.process(pluginName, params); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + void write(String folderPath, String fileName, InputStream in) { + File outputFolder = new File(folderPath); + if (!outputFolder.exists()) { + outputFolder.mkdirs(); // 如果文件夹不存在,则创建它 + } + File outputFile = new File(outputFolder, fileName); + try { + // 创建FileOutputStream对象 + FileOutputStream fileOutputStream = new FileOutputStream(outputFile); + // 从InputStream中读取数据并写入到FileOutputStream中 + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, bytesRead); + } + // 关闭输入流和输出流 + in.close(); + fileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + + /*public static void main(String[] args) throws Exception { + // 读取ZIP文件中的JAR文件 + File zipFile = new File("D:\\work-file\\ttt\\test.zip"); + JarInputStream jarInputStream = new JarInputStream(new FileInputStream(zipFile)); + JarEntry jarEntry; + while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { + if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".jar")) { + // 加载JAR文件 + URL url = new URL("jar", "", "file:" + zipFile.getAbsolutePath() + "/" + jarEntry.getName()); + URLClassLoader classLoader = new URLClassLoader(new URL[]{url}); + + // 扫描JAR文件中的所有类文件 + JarInputStream innerJarInputStream = new JarInputStream(new FileInputStream(new File(url.getFile()))); + while ((jarEntry = innerJarInputStream.getNextJarEntry()) != null) { + if (jarEntry.getName().endsWith(".class")) { + // 通过反射获取类对象 + String className = jarEntry.getName().replace("/", ".").replace(".class", ""); + Class clazz = classLoader.loadClass(className); + System.out.println("Loaded class: " + clazz.getName()); + } + } + innerJarInputStream.close(); + } + } + jarInputStream.close(); + }*/ + + + public static void main(String[] args) throws IOException { + try { + // 读取ZIP文件 + File zipFile = new File("D:\\tmp\\test.zip"); + JarInputStream jarInputStream = new JarInputStream(new FileInputStream(zipFile)); + JarEntry jarEntry; + while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { + // 检查是否为JAR文件 + if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".jar")) { + // 创建临时目录并将JAR文件解压到该目录 + File tempDir = new File("temp"); + tempDir.mkdir(); + File tempJar = new File(tempDir, jarEntry.getName()); + copyStreamToFile(jarInputStream, tempJar); + // 创建URL表示JAR文件 + URL url = new URL("file:" + tempJar.getAbsolutePath()); + URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, DeployPluginServiceImpl.class.getClassLoader()); + // 加载JAR文件中的主类并执行main方法 + String mainClassName = "apelet.tenantadmin.tenant.plugin.MyTestPlugin"; // 替换成你的主类名 + Class mainClass = classLoader.loadClass(mainClassName); + Object o1 = mainClass.newInstance(); + Method mainMethod = mainClass.getMethod("process", Map.class); + Object invoke = mainMethod.invoke(o1, new HashMap<>()); + System.out.println(invoke); + // 关闭classLoader,释放资源 + classLoader.close(); + tempJar.delete(); + tempDir.delete(); + } + } + jarInputStream.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void t1() throws IOException { + URLClassLoader classLoader = null; + JarInputStream jarInputStream = null; + try { + // 读取ZIP文件 + File zipFile = new File("D:\\tmp\\test.zip"); + jarInputStream = new JarInputStream(Files.newInputStream(zipFile.toPath())); + JarEntry jarEntry; + while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { + // 检查是否为JAR文件 + if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".jar")) { + // 创建URL表示JAR文件 + URL url = new URL("jar", "", "file:" + zipFile.getAbsolutePath() + "!/" + jarEntry.getName()); + classLoader = new URLClassLoader(new URL[]{url}); + + // 加载JAR文件中的主类并执行main方法 + String mainClassName = "apelet.tenantadmin.tenant.plugin.MyTestPlugin"; // 替换成你的主类名 + Class mainClass = classLoader.loadClass(mainClassName); + Object o1 = mainClass.newInstance(); + Method mainMethod = mainClass.getMethod("process", Map.class); + mainMethod.invoke(o1, new HashMap<>()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + classLoader.close(); + jarInputStream.close(); + } + } + + + private static void copyStreamToFile(InputStream inputStream, File outputFile) throws IOException { + try (OutputStream outputStream = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + } + + @Override + protected BaseDaoMapper mapper() { + return deployPluginMapper; + } +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantDatasourceServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantDatasourceServiceImpl.java new file mode 100644 index 0000000..5f39ad9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantDatasourceServiceImpl.java @@ -0,0 +1,73 @@ +package apelet.tenantadmin.tenant.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.util.MyModelUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.tenant.dao.SysTenantDatasourceMapper; +import apelet.tenantadmin.tenant.dao.SysTenantMapper; +import apelet.tenantadmin.tenant.model.*; +import apelet.tenantadmin.tenant.service.SysTenantDatasourceService; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 租户权限资源数据服务实现类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantDatasourceService") +public class SysTenantDatasourceServiceImpl extends BaseService implements SysTenantDatasourceService { + + @Autowired + private SysTenantDatasourceMapper sysTenantDatasourceMapper; + @Autowired + private SysTenantMapper sysTenantMapper; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private ApplicationConfig applicationConfig; + @Autowired + private RedissonClient redissonClient; + + @Override + protected BaseDaoMapper mapper() { + return sysTenantDatasourceMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantDatasource saveNew(SysTenantDatasource datasource) { + datasource.setDatasourceId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(datasource); + sysTenantDatasourceMapper.insert(datasource); + return datasource; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantDatasource datasource, SysTenantDatasource originalDatasource) { + MyModelUtil.fillCommonsForUpdate(datasource, originalDatasource); + return sysTenantDatasourceMapper.updateById(datasource) != 0; + } + + @Override + public List getSysTenantDatasourceList(SysTenantDatasource filter) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.setEntity(filter); + queryWrapper.orderByDesc(SysTenantDatasource::getDatasourceId); + return sysTenantDatasourceMapper.selectList(queryWrapper); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantMenuServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantMenuServiceImpl.java new file mode 100644 index 0000000..7e0a785 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantMenuServiceImpl.java @@ -0,0 +1,287 @@ +package apelet.tenantadmin.tenant.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.constant.DataSyncCommandType; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.SysTenantMenuMapper; +import apelet.tenantadmin.tenant.dao.SysTenantMenuPermCodeMapper; +import apelet.tenantadmin.tenant.dao.SysTenantRoleMenuMapper; +import apelet.tenantadmin.tenant.model.SysTenantMenu; +import apelet.tenantadmin.tenant.model.SysTenantMenuPermCode; +import apelet.tenantadmin.tenant.model.SysTenantRoleMenu; +import apelet.tenantadmin.tenant.service.SysTenantMenuService; +import apelet.tenantadmin.tenant.service.SysTenantPermCodeService; +import apelet.tenantadmin.upms.model.constant.SysMenuType; +import apelet.tenantadmin.config.DataSourceType; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 租户菜单数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantMenuService") +public class SysTenantMenuServiceImpl extends BaseService implements SysTenantMenuService { + + @Autowired + private SysTenantMenuMapper sysTenantMenuMapper; + @Autowired + private SysTenantRoleMenuMapper sysTenantRoleMenuMapper; + @Autowired + private SysTenantMenuPermCodeMapper sysTenantMenuPermCodeMapper; + @Autowired + private SysTenantPermCodeService sysTenantPermCodeService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantMenuMapper; + } + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysMenu 新增的租户菜单对象。 + * @param permCodeIdSet 租户权限字Id列表。 + * @return 新增数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantMenu saveNew(SysTenantMenu sysMenu, Set permCodeIdSet) { + sysMenu.setMenuId(idGenerator.nextLongId()); + sysMenu.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysMenu); + sysTenantMenuMapper.insert(sysMenu); + List sysMenuPermCodeList = new LinkedList<>(); + if (CollectionUtils.isNotEmpty(permCodeIdSet)) { + permCodeIdSet.forEach(permCodeId -> { + SysTenantMenuPermCode menuPermCode = new SysTenantMenuPermCode(); + menuPermCode.setMenuId(sysMenu.getMenuId()); + menuPermCode.setPermCodeId(permCodeId); + sysMenuPermCodeList.add(menuPermCode); + }); + sysTenantMenuPermCodeMapper.insertList(sysMenuPermCodeList); + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantMenu", sysMenu); + messageJsonData.put("sysTenantMenuPermCodeList", sysMenuPermCodeList); + dataSyncProducer.sendOrderly( + sysMenu.getMenuId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.INSERT.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return sysMenu; + } + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysMenu 更新的租户菜单对象。 + * @param originalSysMenu 原有的租户菜单对象。 + * @param permCodeIdSet 租户权限字Id列表。 + * @return 更新数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantMenu sysMenu, SysTenantMenu originalSysMenu, Set permCodeIdSet) { + MyModelUtil.fillCommonsForUpdate(sysMenu, originalSysMenu); + UpdateWrapper updateWrapper = + this.createUpdateQueryForNullValue(sysMenu, sysMenu.getMenuId()); + if (sysTenantMenuMapper.update(sysMenu, updateWrapper) == 0) { + return false; + } + SysTenantMenuPermCode deletedMenuPermCode = new SysTenantMenuPermCode(); + deletedMenuPermCode.setMenuId(sysMenu.getMenuId()); + sysTenantMenuPermCodeMapper.delete(new QueryWrapper<>(deletedMenuPermCode)); + List sysMenuPermCodeList = new LinkedList<>(); + if (CollectionUtils.isNotEmpty(permCodeIdSet)) { + for (Long permCodeId : permCodeIdSet) { + SysTenantMenuPermCode menuPermCode = new SysTenantMenuPermCode(); + menuPermCode.setMenuId(sysMenu.getMenuId()); + menuPermCode.setPermCodeId(permCodeId); + sysMenuPermCodeList.add(menuPermCode); + } + sysTenantMenuPermCodeMapper.insertList(sysMenuPermCodeList); + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantMenu", sysMenu); + messageJsonData.put("originalSysTenantMenu", originalSysMenu); + messageJsonData.put("sysTenantMenuPermCodeList", sysMenuPermCodeList); + dataSyncProducer.sendOrderly( + sysMenu.getMenuId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.UPDATE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 填充租户菜单实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param menuId 租户菜单主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long menuId) { + if (!this.removeById(menuId)) { + return false; + } + SysTenantRoleMenu roleMenu = new SysTenantRoleMenu(); + roleMenu.setMenuId(menuId); + sysTenantRoleMenuMapper.delete(new QueryWrapper<>(roleMenu)); + SysTenantMenuPermCode menuPermCode = new SysTenantMenuPermCode(); + menuPermCode.setMenuId(menuId); + sysTenantMenuPermCodeMapper.delete(new QueryWrapper<>(menuPermCode)); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("menuId", menuId); + dataSyncProducer.sendOrderly( + menuId, + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.DELETE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 根据租户角色Id,获取关联的租户菜单从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的租户菜单数据列表。 + */ + @Override + public List getNotInSysTenantMenuListByRoleId(Long tenantRoleId, String orderBy) { + return sysTenantMenuMapper.getNotInSysTenantMenuListByRoleId(tenantRoleId, orderBy); + } + + /** + * 根据租户角色Id,获取关联的租户菜单从表中和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的租户菜单数据列表。 + */ + @Override + public List getSysTenantMenuListByRoleId(Long tenantRoleId, String orderBy) { + return sysTenantMenuMapper.getSysTenantMenuListByRoleId(tenantRoleId, orderBy); + } + + /** + * 判断当前租户菜单是否存在子菜单。 + * + * @param menuId 租户菜单主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long menuId) { + SysTenantMenu menu = new SysTenantMenu(); + menu.setParentId(menuId); + return this.getCountByFilter(menu) > 0; + } + + /** + * 验证租户菜单对象关联的数据是否都合法。 + * + * @param sysMenu 当前操作的对象。 + * @param originalSysMenu 原有对象。 + * @param permCodeIdListString 逗号分隔的权限Id列表。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData( + SysTenantMenu sysMenu, SysTenantMenu originalSysMenu, String permCodeIdListString) { + // menu、ui fragment和button类型的menu不能没有parentId + if (sysMenu.getParentId() == null && sysMenu.getMenuType() != SysMenuType.TYPE_DIRECTORY) { + return CallResult.error("数据验证失败,当前类型菜单项的上级菜单不能为空!"); + } + if (this.needToVerify(sysMenu, originalSysMenu, SysTenantMenu::getParentId)) { + String errorMessage = checkErrorOfNonDirectoryMenu(sysMenu); + if (errorMessage != null) { + return CallResult.error(errorMessage); + } + } + JSONObject jsonObject = null; + if (StringUtils.isNotBlank(permCodeIdListString)) { + Set permCodeIdSet = Arrays.stream( + permCodeIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysTenantPermCodeService.existAllPrimaryKeys(permCodeIdSet)) { + return CallResult.error("数据验证失败,存在不合法的权限字,请刷新后重试!"); + } + jsonObject = new JSONObject(); + jsonObject.put("permCodeIdSet", permCodeIdSet); + } + return CallResult.ok(jsonObject); + } + + private String checkErrorOfNonDirectoryMenu(SysTenantMenu sysMenu) { + // 判断父节点是否存在 + SysTenantMenu parentSysMenu = getById(sysMenu.getParentId()); + if (parentSysMenu == null) { + return "数据验证失败,关联的上级菜单并不存在,请刷新后重试!"; + } + // 逐个判断每种类型的菜单,他的父菜单的合法性,先从目录类型和菜单类型开始 + if (sysMenu.getMenuType() == SysMenuType.TYPE_DIRECTORY + || sysMenu.getMenuType() == SysMenuType.TYPE_MENU) { + // 他们的上级只能是目录 + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_DIRECTORY) { + return "数据验证失败,当前类型菜单项的上级菜单只能是目录类型!"; + } + } else if (sysMenu.getMenuType() == SysMenuType.TYPE_BUTTON) { + // button的上级只能是menu和ui fragment + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_MENU + && parentSysMenu.getMenuType() != SysMenuType.TYPE_UI_FRAGMENT) { + return "数据验证失败,当前类型菜单项的上级菜单只能是菜单类型和UI片段类型!"; + } + } else if (sysMenu.getMenuType() == SysMenuType.TYPE_UI_FRAGMENT) { + // ui fragment的上级只能是menu类型 + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_MENU) { + return "数据验证失败,当前类型菜单项的上级菜单只能是菜单类型和按钮类型!"; + } + } else { + return "数据验证失败,不支持的菜单类型!"; + } + return null; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermCodeServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermCodeServiceImpl.java new file mode 100644 index 0000000..b6fa343 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermCodeServiceImpl.java @@ -0,0 +1,228 @@ +package apelet.tenantadmin.tenant.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.constant.DataSyncCommandType; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.SysTenantMenuPermCodeMapper; +import apelet.tenantadmin.tenant.dao.SysTenantPermCodeMapper; +import apelet.tenantadmin.tenant.dao.SysTenantPermCodePermMapper; +import apelet.tenantadmin.tenant.model.SysTenantMenuPermCode; +import apelet.tenantadmin.tenant.model.SysTenantPermCode; +import apelet.tenantadmin.tenant.model.SysTenantPermCodePerm; +import apelet.tenantadmin.tenant.service.SysTenantPermCodeService; +import apelet.tenantadmin.tenant.service.SysTenantPermService; +import apelet.tenantadmin.config.DataSourceType; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 租户权限字数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantPermCodeService") +public class SysTenantPermCodeServiceImpl extends BaseService implements SysTenantPermCodeService { + + @Autowired + private SysTenantPermCodeMapper sysTenantPermCodeMapper; + @Autowired + private SysTenantPermCodePermMapper sysTenantPermCodePermMapper; + @Autowired + private SysTenantMenuPermCodeMapper sysTenantMenuPermCodeMapper; + @Autowired + private SysTenantPermService sysTenantPermService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantPermCodeMapper; + } + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysPermCode 新增的租户权限字实体对象。 + * @param permIdSet 租户权限资源Id列表。 + * @return 新增数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantPermCode saveNew(SysTenantPermCode sysPermCode, Set permIdSet) { + sysPermCode.setPermCodeId(idGenerator.nextLongId()); + sysPermCode.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysPermCode); + sysTenantPermCodeMapper.insert(sysPermCode); + List sysPermCodePermList = new LinkedList<>(); + if (permIdSet != null) { + permIdSet.forEach(permId -> { + SysTenantPermCodePerm permCodePerm = new SysTenantPermCodePerm(); + permCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + permCodePerm.setPermId(permId); + sysPermCodePermList.add(permCodePerm); + }); + sysTenantPermCodePermMapper.insertList(sysPermCodePermList); + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPermCode", sysPermCode); + messageJsonData.put("sysTenantPermCodePermList", sysPermCodePermList); + dataSyncProducer.sendOrderly( + sysPermCode.getPermCodeId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.INSERT.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return sysPermCode; + } + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysPermCode 更新的租户权限字对象。 + * @param originalSysPermCode 原有的租户权限字对象。 + * @param permIdSet 租户权限资源Id列表。 + * @return 更新数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantPermCode sysPermCode, SysTenantPermCode originalSysPermCode, Set permIdSet) { + sysPermCode.setParentId(originalSysPermCode.getParentId()); + MyModelUtil.fillCommonsForUpdate(sysPermCode, originalSysPermCode); + UpdateWrapper updateWrapper = + this.createUpdateQueryForNullValue(sysPermCode, sysPermCode.getPermCodeId()); + if (sysTenantPermCodeMapper.update(sysPermCode, updateWrapper) == 0) { + return false; + } + SysTenantPermCodePerm deletedPermCodePerm = new SysTenantPermCodePerm(); + deletedPermCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + sysTenantPermCodePermMapper.delete(new QueryWrapper<>(deletedPermCodePerm)); + List sysPermCodePermList = new LinkedList<>(); + if (permIdSet != null) { + for (Long permId : permIdSet) { + SysTenantPermCodePerm permCodePerm = new SysTenantPermCodePerm(); + permCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + permCodePerm.setPermId(permId); + sysPermCodePermList.add(permCodePerm); + } + sysTenantPermCodePermMapper.insertList(sysPermCodePermList); + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPermCode", sysPermCode); + messageJsonData.put("sysTenantPermCodePermList", sysPermCodePermList); + dataSyncProducer.sendOrderly( + sysPermCode.getPermCodeId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.UPDATE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 填充租户权限字实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param permCodeId 租户权限字主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long permCodeId) { + // 这里先删除主数据 + if (!this.removeById(permCodeId)) { + return false; + } + SysTenantMenuPermCode menuPermCode = new SysTenantMenuPermCode(); + menuPermCode.setPermCodeId(permCodeId); + sysTenantMenuPermCodeMapper.delete(new QueryWrapper<>(menuPermCode)); + SysTenantPermCodePerm permCodePerm = new SysTenantPermCodePerm(); + permCodePerm.setPermCodeId(permCodeId); + sysTenantPermCodePermMapper.delete(new QueryWrapper<>(permCodePerm)); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("permCodeId", permCodeId); + dataSyncProducer.sendOrderly( + permCodeId, + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.DELETE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 判断当前租户权限字是否存在下级权限字对象。 + * + * @param permCodeId 租户权限字主键Id。 + * @return 存在返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean hasChildren(Long permCodeId) { + SysTenantPermCode permCode = new SysTenantPermCode(); + permCode.setParentId(permCodeId); + return this.getCountByFilter(permCode) > 0; + } + + /** + * 验证租户权限字对象关联的数据是否都合法。 + * + * @param sysPermCode 当前操作的对象。 + * @param originalSysPermCode 原有对象。 + * @param permIdListString 逗号分隔的权限资源Id列表。 + * @return 验证结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public CallResult verifyRelatedData( + SysTenantPermCode sysPermCode, SysTenantPermCode originalSysPermCode, String permIdListString) { + if (this.needToVerify(sysPermCode, originalSysPermCode, SysTenantPermCode::getParentId) + && !existId(sysPermCode.getParentId())) { + return CallResult.error("数据验证失败,关联的上级权限字并不存在,请刷新后重试!"); + } + JSONObject jsonObject = null; + if (StringUtils.isNotBlank(permIdListString)) { + Set permIdSet = Arrays.stream( + permIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysTenantPermService.existAllPrimaryKeys(permIdSet)) { + return CallResult.error("数据验证失败,存在不合法的权限资源,请刷新后重试!"); + } + jsonObject = new JSONObject(); + jsonObject.put("permIdSet", permIdSet); + } + return CallResult.ok(jsonObject); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermModuleServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermModuleServiceImpl.java new file mode 100644 index 0000000..3a50046 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermModuleServiceImpl.java @@ -0,0 +1,175 @@ +package apelet.tenantadmin.tenant.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.constant.DataSyncCommandType; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.SysTenantPermModuleMapper; +import apelet.tenantadmin.tenant.model.SysTenantPerm; +import apelet.tenantadmin.tenant.model.SysTenantPermModule; +import apelet.tenantadmin.tenant.service.SysTenantPermModuleService; +import apelet.tenantadmin.tenant.service.SysTenantPermService; +import apelet.tenantadmin.config.DataSourceType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 租户权限资源模块数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantPermModuleService") +public class SysTenantPermModuleServiceImpl extends BaseService implements SysTenantPermModuleService { + + @Autowired + private SysTenantPermModuleMapper sysTenantPermModuleMapper; + @Autowired + private SysTenantPermService sysTenantPermService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantPermModuleMapper; + } + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysPermModule 新增的租户权限资源模块实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantPermModule saveNew(SysTenantPermModule sysPermModule) { + sysPermModule.setModuleId(idGenerator.nextLongId()); + sysPermModule.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysPermModule); + sysTenantPermModuleMapper.insert(sysPermModule); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPermModule", sysPermModule); + dataSyncProducer.sendOrderly( + sysPermModule.getModuleId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.INSERT.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return sysPermModule; + } + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysPermModule 更新的租户权限资源模块对象。 + * @param originalSysPermModule 原有的租户权限资源模块对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantPermModule sysPermModule, SysTenantPermModule originalSysPermModule) { + MyModelUtil.fillCommonsForUpdate(sysPermModule, originalSysPermModule); + UpdateWrapper updateWrapper = + this.createUpdateQueryForNullValue(sysPermModule, sysPermModule.getModuleId()); + if (sysTenantPermModuleMapper.update(sysPermModule, updateWrapper) == 0) { + return false; + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPermModule", sysPermModule); + dataSyncProducer.sendOrderly( + sysPermModule.getModuleId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.UPDATE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 填充租户权限资源模块实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param moduleId 待删除的租户权限资源模块主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long moduleId) { + if (!this.removeById(moduleId)) { + return false; + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("moduleId", moduleId); + dataSyncProducer.sendOrderly( + moduleId, + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.DELETE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 获取权限模块资源及其关联的权限资源列表。 + * + * @return 权限资源模块及其关联的权限资源列表。 + */ + @Override + public List getPermModuleAndPermList() { + return sysTenantPermModuleMapper.getPermModuleAndPermList(); + } + + /** + * 判断是否存在下级租户权限资源模块。 + * + * @param moduleId 租户权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long moduleId) { + SysTenantPermModule permModule = new SysTenantPermModule(); + permModule.setParentId(moduleId); + return this.getCountByFilter(permModule) > 0; + } + + /** + * 判断是否存在租户权限数据。 + * + * @param moduleId 租户权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasModulePerms(Long moduleId) { + SysTenantPerm filter = new SysTenantPerm(); + filter.setModuleId(moduleId); + return sysTenantPermService.getCountByFilter(filter) > 0; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermServiceImpl.java new file mode 100644 index 0000000..e45a643 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantPermServiceImpl.java @@ -0,0 +1,190 @@ +package apelet.tenantadmin.tenant.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.constant.DataSyncCommandType; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.SysTenantPermCodePermMapper; +import apelet.tenantadmin.tenant.dao.SysTenantPermMapper; +import apelet.tenantadmin.tenant.model.SysTenantPerm; +import apelet.tenantadmin.tenant.model.SysTenantPermCodePerm; +import apelet.tenantadmin.tenant.model.SysTenantPermModule; +import apelet.tenantadmin.tenant.service.SysTenantPermModuleService; +import apelet.tenantadmin.tenant.service.SysTenantPermService; +import apelet.tenantadmin.config.DataSourceType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 租户权限资源数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantPermService") +public class SysTenantPermServiceImpl extends BaseService implements SysTenantPermService { + + @Autowired + private SysTenantPermMapper sysTenantPermMapper; + @Autowired + private SysTenantPermCodePermMapper sysTenantPermCodePermMapper; + @Autowired + private SysTenantPermModuleService sysTenantPermModuleService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantPermMapper; + } + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysPerm 新增的租户权限资源实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantPerm saveNew(SysTenantPerm sysPerm) { + sysPerm.setPermId(idGenerator.nextLongId()); + sysPerm.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysPerm); + sysTenantPermMapper.insert(sysPerm); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPerm", sysPerm); + dataSyncProducer.sendOrderly( + sysPerm.getPermId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.INSERT.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return sysPerm; + } + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysPerm 更新的租户权限资源对象。 + * @param originalSysPerm 原有的租户权限资源对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantPerm sysPerm, SysTenantPerm originalSysPerm) { + MyModelUtil.fillCommonsForUpdate(sysPerm, originalSysPerm); + UpdateWrapper updateWrapper = + this.createUpdateQueryForNullValue(sysPerm, sysPerm.getPermId()); + if (sysTenantPermMapper.update(sysPerm, updateWrapper) == 0) { + return false; + } + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenantPerm", sysPerm); + dataSyncProducer.sendOrderly( + sysPerm.getPermId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.UPDATE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 填充租户权限资源实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param permId 待删除的租户权限资源主键Id。 + * @return 删除数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long permId) { + if (!this.removeById(permId)) { + return false; + } + SysTenantPermCodePerm permCodePerm = new SysTenantPermCodePerm(); + permCodePerm.setPermId(permId); + sysTenantPermCodePermMapper.delete(new QueryWrapper<>(permCodePerm)); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("permId", permId); + dataSyncProducer.sendOrderly( + permId, + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.DELETE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 获取租户权限数据列表。 + * + * @param sysPermFilter 过滤对象。 + * @return 权限列表。 + */ + @Override + public List getPermListWithRelation(SysTenantPerm sysPermFilter) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.orderByAsc(this.safeMapToColumnName("showOrder")); + if (ObjectUtil.isNotNull(sysPermFilter.getModuleId())) { + queryWrapper.eq(this.safeMapToColumnName("moduleId"), sysPermFilter.getModuleId()); + } + if (ObjectUtil.isNotNull(sysPermFilter.getUrl())) { + queryWrapper.like("url", "%" + sysPermFilter.getUrl() + "%"); + } + List permList = sysTenantPermMapper.selectList(queryWrapper); + // 这里因为权限只有字典数据,所以仅仅做字典关联。 + this.buildRelationForDataList(permList, MyRelationParam.dictOnly()); + return permList; + } + + /** + * 验证租户权限资源对象关联的数据是否都合法。 + * + * @param sysPerm 当前操作的对象。 + * @param originalSysPerm 原有对象。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData(SysTenantPerm sysPerm, SysTenantPerm originalSysPerm) { + if (this.needToVerify(sysPerm, originalSysPerm, SysTenantPerm::getModuleId)) { + SysTenantPermModule permModule = sysTenantPermModuleService.getById(sysPerm.getModuleId()); + if (permModule == null) { + return CallResult.error("数据验证失败,关联的租户权限模块Id并不存在,请刷新后重试!"); + } + } + return CallResult.ok(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantRoleServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantRoleServiceImpl.java new file mode 100644 index 0000000..48a94da --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantRoleServiceImpl.java @@ -0,0 +1,644 @@ +package apelet.tenantadmin.tenant.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.online.model.OnlinePage; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.common.report.model.ReportTenantDataset; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.*; +import apelet.tenantadmin.tenant.model.*; +import apelet.tenantadmin.tenant.service.SysTenantRoleService; +import apelet.tenantadmin.tenant.service.SysTenantService; +import apelet.tenantadmin.config.DataSourceType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 租户角色数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantRoleService") +public class SysTenantRoleServiceImpl extends BaseService implements SysTenantRoleService { + + @Autowired + private SysTenantRoleMapper sysTenantRoleMapper; + @Autowired + private SysTenantRoleMenuMapper sysTenantRoleMenuMapper; + @Autowired + private SysTenantRoleTenantMapper sysTenantRoleTenantMapper; + @Autowired + private SysTenantRoleReportDatasetMapper sysTenantRoleReportDatasetMapper; + @Autowired + private SysTenantRoleOnlinePageMapper sysTenantRoleOnlinePageMapper; + @Autowired + private SysTenantExtMapper sysTenantExtMapper; + @Autowired + private SysTenantService sysTenantService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + private static final String TENANT_ID_KEY = "tenantId"; + private static final String TENANT_ID_SET_KEY = "tenantIdSet"; + private static final String TENANT_LIST_KEY = "tenantList"; + private static final String TENANT_EXT_LIST_KEY = "tenantExtList"; + private static final String MENU_ID_KEY = "menuId"; + private static final String MENU_ID_SET_KEY = "menuIdSet"; + private static final String REPORT_DATASET_ID_KEY = "datasetId"; + private static final String REPORT_DATASET_ID_SET_KEY = "datasetIdSet"; + private static final String ONLINE_PAGE_ID_SET_KEY = "pageIdSet"; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantRoleMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenantRole saveNew(SysTenantRole sysTenantRole) { + sysTenantRole.setTenantRoleId(idGenerator.nextLongId()); + sysTenantRole.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysTenantRole); + sysTenantRoleMapper.insert(sysTenantRole); + return sysTenantRole; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenantRole sysTenantRole, SysTenantRole originalSysTenantRole) { + MyModelUtil.fillCommonsForUpdate(sysTenantRole, originalSysTenantRole); + return sysTenantRoleMapper.updateById(sysTenantRole) == 1; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long tenantRoleId) { + if (!this.removeById(tenantRoleId)) { + return false; + } + SysTenantRoleTenant tenantRoleTenant = new SysTenantRoleTenant(); + tenantRoleTenant.setTenantRoleId(tenantRoleId); + sysTenantRoleTenantMapper.delete(new QueryWrapper<>(tenantRoleTenant)); + SysTenantRoleMenu tenantRoleMenu = new SysTenantRoleMenu(); + tenantRoleMenu.setTenantRoleId(tenantRoleId); + sysTenantRoleMenuMapper.delete(new QueryWrapper<>(tenantRoleMenu)); + SysTenantRoleReportDataset tenantRoleReportDataset = new SysTenantRoleReportDataset(); + tenantRoleReportDataset.setTenantRoleId(tenantRoleId); + sysTenantRoleReportDatasetMapper.delete(new QueryWrapper<>(tenantRoleReportDataset)); + SysTenantRoleOnlinePage tenantRoleOnlinePage = new SysTenantRoleOnlinePage(); + tenantRoleOnlinePage.setTenantRoleId(tenantRoleId); + sysTenantRoleOnlinePageMapper.delete(new QueryWrapper<>(tenantRoleOnlinePage)); + return true; + } + + @Override + public List getSysTenantRoleList(SysTenantRole filter, String orderBy) { + return sysTenantRoleMapper.getSysTenantRoleList(filter, orderBy); + } + + @Override + public boolean hasTenantByRoleId(Long tenantRoleId) { + SysTenantRoleTenant tenantRoleTenant = new SysTenantRoleTenant(); + tenantRoleTenant.setTenantRoleId(tenantRoleId); + return sysTenantRoleTenantMapper.selectCount(new QueryWrapper<>(tenantRoleTenant)) > 0; + } + + @Override + public boolean hasTenantRelatedDataByTenantRoleId(Long tenantRoleId) { + SysTenantRoleMenu tenantRoleMenu = new SysTenantRoleMenu(); + tenantRoleMenu.setTenantRoleId(tenantRoleId); + if (sysTenantRoleMenuMapper.selectCount(new QueryWrapper<>(tenantRoleMenu)) > 0) { + return true; + } + SysTenantRoleReportDataset tenantRoleReportDataset = new SysTenantRoleReportDataset(); + tenantRoleReportDataset.setTenantRoleId(tenantRoleId); + if (sysTenantRoleReportDatasetMapper.selectCount(new QueryWrapper<>(tenantRoleReportDataset)) > 0) { + return true; + } + SysTenantRoleOnlinePage tenantRoleOnlinePage = new SysTenantRoleOnlinePage(); + tenantRoleOnlinePage.setTenantRoleId(tenantRoleId); + return sysTenantRoleOnlinePageMapper.selectCount(new QueryWrapper<>(tenantRoleOnlinePage)) > 0; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void addTenantRoleTenantList(Long tenantRoleId, List sysTenantRoleTenantList) { + for (SysTenantRoleTenant sysTenantRoleTenant : sysTenantRoleTenantList) { + sysTenantRoleTenant.setTenantRoleId(tenantRoleId); + } + sysTenantRoleTenantMapper.insertList(sysTenantRoleTenantList); + Set tenantIds = sysTenantRoleTenantList.stream() + .map(SysTenantRoleTenant::getTenantId).collect(Collectors.toSet()); + // 如果当前租户角色下没有租户菜单,就不需要在同步到远程租户数据库了。 + SysTenantRoleMenu tenantRoleMenu = new SysTenantRoleMenu(); + tenantRoleMenu.setTenantRoleId(tenantRoleId); + List menuList = sysTenantRoleMenuMapper.selectList(new QueryWrapper<>(tenantRoleMenu)); + if (CollUtil.isNotEmpty(menuList)) { + Set menuIdSet = menuList.stream().map(SysTenantRoleMenu::getMenuId).collect(Collectors.toSet()); + List tenantList = sysTenantService.getInList(tenantIds); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_LIST_KEY, tenantList); + messageJsonData.put(MENU_ID_SET_KEY, menuIdSet); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + SysTenantMenu.class.getSimpleName(), + TenantConstant.INSERT_TENANT_ROLE_TENANT_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + List datasetIds = this.getSysTenantRoleReportDatasetIdList(tenantRoleId); + if (CollUtil.isNotEmpty(datasetIds)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_ID_SET_KEY, tenantIds); + messageJsonData.put(REPORT_DATASET_ID_SET_KEY, datasetIds); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + ReportTenantDataset.class.getSimpleName(), + TenantConstant.INSERT_TENANT_REPORT_DATASET_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + List pageIds = this.getSysTenantRoleOnlinePageIdList(tenantRoleId); + if (CollUtil.isNotEmpty(pageIds)) { + this.processAddTenantOnlinePageAndSync(tenantRoleId, tenantIds, pageIds); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleTenant(Long tenantRoleId, Long tenantId) { + SysTenantRoleTenant sysTenantRoleTenant = new SysTenantRoleTenant(); + sysTenantRoleTenant.setTenantRoleId(tenantRoleId); + sysTenantRoleTenant.setTenantId(tenantId); + sysTenantRoleTenantMapper.delete(new QueryWrapper<>(sysTenantRoleTenant)); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleTenantAndSync(Long tenantRoleId, Long tenantId) { + // 获取当前租户角色关联哪些菜单,当前租户角色肯定是包含菜单的,因为在外面已经做了判断。 + SysTenantRoleMenu tenantRoleMenu = new SysTenantRoleMenu(); + tenantRoleMenu.setTenantRoleId(tenantRoleId); + Set menuIdSet = sysTenantRoleMenuMapper.selectList(new QueryWrapper<>(tenantRoleMenu)) + .stream().map(SysTenantRoleMenu::getMenuId).collect(Collectors.toSet()); + // 获取当前租户还有哪些其他角色。 + // select tenant_role_id from zz_tenant_role_tenant where tenant_id = tenantId and tenant_role_id != roleId + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysTenantRoleTenant::getTenantId, tenantId); + queryWrapper.ne(SysTenantRoleTenant::getTenantRoleId, tenantRoleId); + Set otherRoleIdSet = sysTenantRoleTenantMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleTenant::getTenantRoleId).collect(Collectors.toSet()); + Set resultMenuIdSet = this.filterTenantMenuIdSet(menuIdSet, otherRoleIdSet); + // 本地租户管理数据库中移除他们的关联关系即可了。 + if (CollUtil.isNotEmpty(resultMenuIdSet)) { + SysTenant sysTenant = sysTenantService.getById(tenantId); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenant", sysTenant); + messageJsonData.put(MENU_ID_SET_KEY, resultMenuIdSet); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + SysTenantMenu.class.getSimpleName(), + TenantConstant.DELETE_TENANT_ROLE_TENANT_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + List reportDatasetIdSet = this.getSysTenantRoleReportDatasetIdList(tenantRoleId); + Set resultReportDatasetIdSet = + this.filterTenantReportDatasetIdSet(new HashSet<>(reportDatasetIdSet), otherRoleIdSet); + if (CollUtil.isNotEmpty(resultReportDatasetIdSet)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_ID_KEY, tenantId); + messageJsonData.put(REPORT_DATASET_ID_SET_KEY, resultReportDatasetIdSet); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + ReportTenantDataset.class.getSimpleName(), + TenantConstant.DELETE_TENANT_REPORT_DATASET_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + this.processRemoveTenantOnlinePageAndSync(tenantRoleId, tenantId, otherRoleIdSet); + this.removeTenantRoleTenant(tenantRoleId, tenantId); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void addTenantRoleMenuList(Long tenantRoleId, List sysTenantRoleMenuList) { + for (SysTenantRoleMenu sysTenantRoleMenu : sysTenantRoleMenuList) { + sysTenantRoleMenu.setTenantRoleId(tenantRoleId); + } + sysTenantRoleMenuMapper.insertList(sysTenantRoleMenuList); + // 判断该租户角色是否包含租户,如果包含需要同步远程数据。 + List tenantList = + sysTenantService.getSysTenantListByRoleId(tenantRoleId, null, null); + if (CollUtil.isNotEmpty(tenantList)) { + Set menuIdSet = sysTenantRoleMenuList.stream() + .map(SysTenantRoleMenu::getMenuId).collect(Collectors.toSet()); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_LIST_KEY, tenantList); + messageJsonData.put(MENU_ID_SET_KEY, menuIdSet); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + SysTenantMenu.class.getSimpleName(), + TenantConstant.INSERT_TENANT_ROLE_MENU_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleMenu(Long tenantRoleId, Long tenantMenuId) { + SysTenantRoleMenu sysTenantRoleMenu = new SysTenantRoleMenu(); + sysTenantRoleMenu.setTenantRoleId(tenantRoleId); + sysTenantRoleMenu.setMenuId(tenantMenuId); + sysTenantRoleMenuMapper.delete(new QueryWrapper<>(sysTenantRoleMenu)); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleMenuAndSync(Long tenantRoleId, Long menuId) { + // 获取还有哪些其他租户角色关联了该菜单,得到租户角色Id列表。 + // select * from zz_tenant_role_menu where tenant_role_id != roleId and menu_id = menuId + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysTenantRoleMenu::getMenuId, menuId); + queryWrapper.ne(SysTenantRoleMenu::getTenantRoleId, tenantRoleId); + Set otherTenantRoleIds = sysTenantRoleMenuMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleMenu::getTenantRoleId).collect(Collectors.toSet()); + Set resultTenantIdSet = this.filterTenantIdSet(tenantRoleId, otherTenantRoleIds); + // 如果没有需要同步删除的租户菜单数据列表,仅执行本地关联数据删除即可。 + this.removeTenantRoleMenu(tenantRoleId, menuId); + if (CollUtil.isEmpty(resultTenantIdSet)) { + return; + } + List tenantList = sysTenantService.getInList(resultTenantIdSet); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(MENU_ID_KEY, menuId); + messageJsonData.put(TENANT_LIST_KEY, tenantList); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + SysTenantMenu.class.getSimpleName(), + TenantConstant.DELETE_TENANT_ROLE_MENU_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void addTenantRoleReportDatasetList(Long tenantRoleId, Set datasetIds) { + List dataList = datasetIds.stream().map(datasetId -> { + SysTenantRoleReportDataset data = new SysTenantRoleReportDataset(); + data.setTenantRoleId(tenantRoleId); + data.setDatasetId(datasetId); + return data; + }).collect(Collectors.toList()); + sysTenantRoleReportDatasetMapper.insertList(dataList); + List tenantIds = this.getSysTenantRoleTenantIdsByTenantRoleId(tenantRoleId); + if (CollUtil.isNotEmpty(tenantIds)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_ID_SET_KEY, tenantIds); + messageJsonData.put(REPORT_DATASET_ID_SET_KEY, datasetIds); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + ReportTenantDataset.class.getSimpleName(), + TenantConstant.INSERT_TENANT_REPORT_DATASET_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleReportDataset(Long tenantRoleId, Long datasetId) { + SysTenantRoleReportDataset filter = new SysTenantRoleReportDataset(); + filter.setTenantRoleId(tenantRoleId); + filter.setDatasetId(datasetId); + sysTenantRoleReportDatasetMapper.delete(new QueryWrapper<>(filter)); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void removeTenantRoleReportDatasetAndSync(Long tenantRoleId, Long datasetId) { + // 获取还有哪些其他租户角色关联了该报表数据集Id,得到租户角色Id列表。 + // select * from zz_tenant_role_report_dataset where tenant_role_id != roleId and dataset_id = datasetId + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysTenantRoleReportDataset::getDatasetId, datasetId); + queryWrapper.ne(SysTenantRoleReportDataset::getTenantRoleId, tenantRoleId); + Set otherTenantRoleIds = sysTenantRoleReportDatasetMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleReportDataset::getTenantRoleId).collect(Collectors.toSet()); + Set resultTenantIdSet = this.filterTenantIdSet(tenantRoleId, otherTenantRoleIds); + this.removeTenantRoleReportDataset(tenantRoleId, datasetId); + if (CollUtil.isNotEmpty(resultTenantIdSet)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_ID_SET_KEY, resultTenantIdSet); + messageJsonData.put(REPORT_DATASET_ID_KEY, datasetId); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + ReportTenantDataset.class.getSimpleName(), + TenantConstant.DELETE_TENANT_REPORT_DATASET_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + @Override + public List getSysTenantRoleReportDatasetIdList(Long tenantRoleId) { + List resultList = sysTenantRoleReportDatasetMapper.selectList( + new LambdaQueryWrapper().eq(SysTenantRoleReportDataset::getTenantRoleId, tenantRoleId)); + return resultList.stream().map(SysTenantRoleReportDataset::getDatasetId).collect(Collectors.toList()); + } + + @Override + public List getSysTenantRoleOnlinePageIdList(Long tenantRoleId) { + List resultList = sysTenantRoleOnlinePageMapper.selectList( + new LambdaQueryWrapper().eq(SysTenantRoleOnlinePage::getTenantRoleId, tenantRoleId)); + return resultList.stream().map(SysTenantRoleOnlinePage::getPageId).collect(Collectors.toList()); + } + + @Override + public void addTenantRoleOnlinePageList(Long tenantRoleId, Set pageIds) { + List dataList = pageIds.stream().map(pageId -> { + SysTenantRoleOnlinePage data = new SysTenantRoleOnlinePage(); + data.setTenantRoleId(tenantRoleId); + data.setPageId(pageId); + return data; + }).collect(Collectors.toList()); + sysTenantRoleOnlinePageMapper.insertList(dataList); + List tenantIds = this.getSysTenantRoleTenantIdsByTenantRoleId(tenantRoleId); + if (CollUtil.isNotEmpty(tenantIds)) { + this.processAddTenantOnlinePageAndSync(tenantRoleId, tenantIds, pageIds); + } + } + + @Override + public void removeTenantRoleOnlinePage(Long tenantRoleId, Long pageId) { + SysTenantRoleOnlinePage filter = new SysTenantRoleOnlinePage(); + filter.setTenantRoleId(tenantRoleId); + filter.setPageId(pageId); + sysTenantRoleOnlinePageMapper.delete(new QueryWrapper<>(filter)); + } + + @Override + public void removeTenantRoleOnlinePageAndSync(Long tenantRoleId, Long pageId) { + // 获取还有哪些其他租户角色关联了该在线表单页面Id,得到租户角色Id列表。 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysTenantRoleOnlinePage::getPageId, pageId); + queryWrapper.ne(SysTenantRoleOnlinePage::getTenantRoleId, tenantRoleId); + Set otherTenantRoleIds = sysTenantRoleOnlinePageMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleOnlinePage::getTenantRoleId).collect(Collectors.toSet()); + Set resultTenantIdSet = this.filterTenantIdSet(tenantRoleId, otherTenantRoleIds); + this.removeTenantRoleOnlinePage(tenantRoleId, pageId); + if (CollUtil.isEmpty(resultTenantIdSet)) { + return; + } + List tenantPageIds = new LinkedList<>(); + List tenantExtList = this.getSysTenantExtListWithOnlinePageIds(resultTenantIdSet); + for (SysTenantExt tenantExt : tenantExtList) { + List tenantOnlinePageIdMapList = + this.parseTenantExtOnlinePageIdMap(tenantExt.getTenantOnlinePageIds()); + TenantOnlinePageIdMap foundData = tenantOnlinePageIdMapList.stream() + .filter(d -> d.getPageId().equals(pageId)).findFirst().orElse(null); + if (foundData != null) { + tenantPageIds.add(foundData.getTenantPageId()); + tenantOnlinePageIdMapList.remove(foundData); + tenantExt.setTenantOnlinePageIds(JSON.toJSONString(tenantOnlinePageIdMapList)); + sysTenantExtMapper.updateById(tenantExt); + } + } + if (CollUtil.isNotEmpty(tenantPageIds)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(ONLINE_PAGE_ID_SET_KEY, tenantPageIds); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + OnlinePage.class.getSimpleName(), + TenantConstant.DELETE_TENANT_ONLINE_PAGE_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + private void processAddTenantOnlinePageAndSync( + Long tenantRoleId, Collection tenantIds, Collection pageIds) { + List tenantExtList = this.getSysTenantExtListWithOnlinePageIds(tenantIds); + List resultTenantExtList = new LinkedList<>(); + for (SysTenantExt tenantExt : tenantExtList) { + List tenantOnlinePageIdMapList = + this.parseTenantExtOnlinePageIdMap(tenantExt.getTenantOnlinePageIds()); + List resultList = new LinkedList<>(tenantOnlinePageIdMapList); + Map m = + tenantOnlinePageIdMapList.stream().collect(Collectors.toMap(TenantOnlinePageIdMap::getPageId, c -> c)); + for (Long pageId : pageIds) { + if (!m.containsKey(pageId)) { + resultList.add(new TenantOnlinePageIdMap(pageId, idGenerator.nextLongId())); + } + } + if (resultList.size() != tenantOnlinePageIdMapList.size()) { + tenantExt.setTenantOnlinePageIds(JSON.toJSONString(resultList)); + sysTenantExtMapper.updateById(tenantExt); + resultTenantExtList.add(tenantExt); + } + } + if (CollUtil.isNotEmpty(resultTenantExtList)) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(TENANT_EXT_LIST_KEY, tenantExtList); + messageJsonData.put(ONLINE_PAGE_ID_SET_KEY, pageIds); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + OnlinePage.class.getSimpleName(), + TenantConstant.INSERT_TENANT_ONLINE_PAGE_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + private void processRemoveTenantOnlinePageAndSync(Long tenantRoleId, Long tenantId, Set otherRoleIdSet) { + List pageIdSet = this.getSysTenantRoleOnlinePageIdList(tenantRoleId); + Set resultOnlinePageIdSet = this.filterTenantOnlinePageIdSet(new HashSet<>(pageIdSet), otherRoleIdSet); + if (CollUtil.isEmpty(resultOnlinePageIdSet)) { + return; + } + List tenantPageIds = new LinkedList<>(); + SysTenantExt tenantExt = sysTenantExtMapper.selectById(tenantId); + List tenantOnlinePageIdMapList = + this.parseTenantExtOnlinePageIdMap(tenantExt.getTenantOnlinePageIds()); + for (Long pageId : resultOnlinePageIdSet) { + TenantOnlinePageIdMap foundData = tenantOnlinePageIdMapList.stream() + .filter(d -> d.getPageId().equals(pageId)).findFirst().orElse(null); + if (foundData != null) { + tenantPageIds.add(foundData.getTenantPageId()); + tenantOnlinePageIdMapList.remove(foundData); + } + } + if (CollUtil.isNotEmpty(tenantPageIds)) { + tenantExt.setTenantOnlinePageIds(JSON.toJSONString(tenantOnlinePageIdMapList)); + sysTenantExtMapper.updateById(tenantExt); + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put(ONLINE_PAGE_ID_SET_KEY, tenantPageIds); + dataSyncProducer.sendOrderly( + tenantRoleId, + applicationConfig.getTenantSyncTopic(), + OnlinePage.class.getSimpleName(), + TenantConstant.DELETE_TENANT_ONLINE_PAGE_COMMAND, + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + } + } + + private List parseTenantExtOnlinePageIdMap(String onlinePageIdMap) { + return StrUtil.isNotEmpty(onlinePageIdMap) + ? JSON.parseArray(onlinePageIdMap, TenantOnlinePageIdMap.class) : new LinkedList<>(); + } + + private List getSysTenantRoleTenantIdsByTenantRoleId(Long tenantRoleId) { + SysTenantRoleTenant filter = new SysTenantRoleTenant(); + filter.setTenantRoleId(tenantRoleId); + return sysTenantRoleTenantMapper.selectList(new QueryWrapper<>(filter)) + .stream().map(SysTenantRoleTenant::getTenantId).collect(Collectors.toList()); + } + + private List getSysTenantExtListWithOnlinePageIds(Collection tenantIds) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.select(SysTenantExt::getTenantId, SysTenantExt::getTenantOnlinePageIds); + qw.in(SysTenantExt::getTenantId, tenantIds); + return sysTenantExtMapper.selectList(qw); + } + + private Set filterTenantIdSet(Long tenantRoleId, Set otherRoleIdList) { + Set otherRoleTenantIdSet = null; + if (CollUtil.isNotEmpty(otherRoleIdList)) { + // 从上一步返回的租户角色列表中,找出所有属于他们的租户。 + // select * from zz_tenant_role_tenant where tenant_role_id in (otherRoleIdList) + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysTenantRoleTenant::getTenantRoleId, otherRoleIdList); + otherRoleTenantIdSet = sysTenantRoleTenantMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleTenant::getTenantId).collect(Collectors.toSet()); + } + // 获取当前租户角色的租户列表。因为在外部已经判断过了,当前租户角色是包含租户的 + SysTenantRoleTenant tenantRoleTenant = new SysTenantRoleTenant(); + tenantRoleTenant.setTenantRoleId(tenantRoleId); + Set tenantIdSet = sysTenantRoleTenantMapper.selectList(new QueryWrapper<>(tenantRoleTenant)) + .stream().map(SysTenantRoleTenant::getTenantId).collect(Collectors.toSet()); + Set resultTenantIdSet = tenantIdSet; + if (CollUtil.isNotEmpty(otherRoleTenantIdSet)) { + // 遍历当前租户Id列表,所有出现在 otherRoleTenantIdSet 集合中的tenantId,都不能删除他们对该菜单的可见性。 + resultTenantIdSet = new HashSet<>(); + for (Long tenantId : tenantIdSet) { + if (!CollUtil.contains(otherRoleTenantIdSet, tenantId)) { + resultTenantIdSet.add(tenantId); + } + } + } + return resultTenantIdSet; + } + + private Set filterTenantOnlinePageIdSet(Set onlinePageIdSet, Set otherRoleIdSet) { + Set otherPageIdSet = null; + if (CollUtil.isNotEmpty(otherRoleIdSet)) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.in(SysTenantRoleOnlinePage::getTenantRoleId, otherRoleIdSet); + otherPageIdSet = sysTenantRoleOnlinePageMapper.selectList(qw) + .stream().map(SysTenantRoleOnlinePage::getPageId).collect(Collectors.toSet()); + } + Set resultPageIdSet = onlinePageIdSet; + if (CollUtil.isNotEmpty(otherPageIdSet)) { + resultPageIdSet = new HashSet<>(); + for (Long pageId : onlinePageIdSet) { + if (!CollUtil.contains(otherPageIdSet, pageId)) { + resultPageIdSet.add(pageId); + } + } + } + return resultPageIdSet; + } + + private Set filterTenantReportDatasetIdSet(Set reportDatasetIdSet, Set otherRoleIdSet) { + Set otherDatasetIdSet = null; + if (CollUtil.isNotEmpty(otherRoleIdSet)) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.in(SysTenantRoleReportDataset::getTenantRoleId, otherRoleIdSet); + otherDatasetIdSet = sysTenantRoleReportDatasetMapper.selectList(qw) + .stream().map(SysTenantRoleReportDataset::getDatasetId).collect(Collectors.toSet()); + } + Set resultDatasetIdSet = reportDatasetIdSet; + if (CollUtil.isNotEmpty(otherDatasetIdSet)) { + resultDatasetIdSet = new HashSet<>(); + for (Long datasetId : reportDatasetIdSet) { + if (!CollUtil.contains(otherDatasetIdSet, datasetId)) { + resultDatasetIdSet.add(datasetId); + } + } + } + return resultDatasetIdSet; + } + + private Set filterTenantMenuIdSet(Set menuIdSet, Set otherRoleIdSet) { + Set otherMenuIdSet = null; + if (CollUtil.isNotEmpty(otherRoleIdSet)) { + // 获取当前租户所属其他其他租户角色的菜单Id集合。 + // select menu_id from zz_tenant_role_menu where tenant_role_id in (otherTenantRoleIdSet) + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysTenantRoleMenu::getTenantRoleId, otherRoleIdSet); + otherMenuIdSet = sysTenantRoleMenuMapper.selectList(queryWrapper) + .stream().map(SysTenantRoleMenu::getMenuId).collect(Collectors.toSet()); + } + Set resultMenuIdSet = menuIdSet; + if (CollUtil.isNotEmpty(otherMenuIdSet)) { + resultMenuIdSet = new HashSet<>(); + for (Long menuId : menuIdSet) { + if (!CollUtil.contains(otherMenuIdSet, menuId)) { + resultMenuIdSet.add(menuId); + } + } + } + return resultMenuIdSet; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + static class TenantOnlinePageIdMap { + private Long pageId; + private Long tenantPageId; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantServiceImpl.java new file mode 100644 index 0000000..0f62ba1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/service/impl/SysTenantServiceImpl.java @@ -0,0 +1,245 @@ +package apelet.tenantadmin.tenant.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.util.MyModelUtil; +import apelet.common.datasync.constant.DataSyncCommandType; +import apelet.common.datasync.producer.DataSyncRocketMqProducer; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.common.tenant.constant.TenantConstant; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.tenant.dao.SysTenantExtMapper; +import apelet.tenantadmin.tenant.dao.SysTenantMapper; +import apelet.tenantadmin.tenant.dao.SysTenantRoleTenantMapper; +import apelet.tenantadmin.tenant.model.SysTenant; +import apelet.tenantadmin.tenant.model.SysTenantExt; +import apelet.tenantadmin.tenant.model.SysTenantMenu; +import apelet.tenantadmin.tenant.model.SysTenantRoleTenant; +import apelet.tenantadmin.tenant.service.SysTenantMenuService; +import apelet.tenantadmin.tenant.service.SysTenantService; +import apelet.tenantadmin.config.DataSourceType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 租户数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysTenantService") +public class SysTenantServiceImpl extends BaseService implements SysTenantService { + + @Autowired + private SysTenantMapper sysTenantMapper; + @Autowired + private SysTenantExtMapper sysTenantExtMapper; + @Autowired + private SysTenantRoleTenantMapper sysTenantRoleTenantMapper; + @Autowired + private SysTenantMenuService sysTenantMenuService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private DataSyncRocketMqProducer dataSyncProducer; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysTenantMapper; + } + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地新增操作事务的结果。 + * + * @param sysTenant 新增的租户实体对象。 + * @return 新增数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysTenant saveNew(SysTenant sysTenant) { + sysTenant.setTenantId(idGenerator.nextLongId()); + sysTenant.setAvailable(true); + sysTenant.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysTenant); + sysTenantMapper.insert(sysTenant); + SysTenantExt tenantExt = new SysTenantExt(); + tenantExt.setTenantId(sysTenant.getTenantId()); + sysTenantExtMapper.insert(tenantExt); + List menuList = sysTenantMenuService.getAllList(); + JSONObject messageJsonData = this.makeMessageData(sysTenant); + if (CollUtil.isNotEmpty(menuList)) { + messageJsonData.put("menuList", menuList); + } + dataSyncProducer.sendOrderly( + sysTenant.getTenantId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.INSERT.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return sysTenant; + } + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地更新操作事务的结果。 + * + * @param sysTenant 更新的租户对象。 + * @param originalSysTenant 原有的租户对象。 + * @return 更新数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysTenant sysTenant, SysTenant originalSysTenant) { + MyModelUtil.fillCommonsForUpdate(sysTenant, originalSysTenant); + UpdateWrapper updateWrapper = + this.createUpdateQueryForNullValue(sysTenant, sysTenant.getTenantId()); + if (sysTenantMapper.update(sysTenant, updateWrapper) == 0) { + return false; + } + JSONObject messageJsonData = this.makeMessageData(sysTenant); + dataSyncProducer.sendOrderly( + sysTenant.getTenantId(), + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.UPDATE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 填充租户实体对象中的数据,并发送消息到RocketMQ,同步远程数据。 + * 同时等待本地删除操作事务的结果。 + * + * @param sysTenant 租户实体对象。 + * @return 删除数据操作和同步发送消息的结果。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(SysTenant sysTenant) { + Long tenantId = sysTenant.getTenantId(); + if (!this.removeById(tenantId)) { + return false; + } + sysTenantExtMapper.deleteById(sysTenant.getTenantId()); + SysTenantRoleTenant tenantRoleTenant = new SysTenantRoleTenant(); + tenantRoleTenant.setTenantId(tenantId); + sysTenantRoleTenantMapper.delete(new QueryWrapper<>(tenantRoleTenant)); + JSONObject messageJsonData = this.makeMessageData(sysTenant); + dataSyncProducer.sendOrderly( + tenantId, + applicationConfig.getTenantSyncTopic(), + modelClass.getSimpleName(), + DataSyncCommandType.DELETE.name(), + messageJsonData.toJSONString(), + TenantConstant.MESSAGE_QUEUE_SELECTOR_KEY); + return true; + } + + /** + * 获取租户列表及其关联数据。 + * + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果列表。 + */ + @Override + public List getSysTenantList(SysTenant sysTenantFilter, String orderBy) { + List sysTenantList = sysTenantMapper.getSysTenantList(sysTenantFilter, orderBy); + this.buildRelationForDataList(sysTenantList, MyRelationParam.dictOnly()); + return sysTenantList; + } + + /** + * 根据租户角色Id,获取关联的租户从表中没有和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的租户数据列表。 + */ + @Override + public List getNotInSysTenantListByRoleId( + Long tenantRoleId, SysTenant sysTenantFilter, String orderBy) { + return sysTenantMapper.getNotInSysTenantListByRoleId(tenantRoleId, sysTenantFilter, orderBy); + } + + /** + * 根据租户角色Id,获取关联的租户从表中和主表建立关联关系的数据列表。 + * + * @param tenantRoleId 关联租户角色Id。 + * @param sysTenantFilter 租户过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表建立关联的租户数据列表。 + */ + @Override + public List getSysTenantListByRoleId( + Long tenantRoleId, SysTenant sysTenantFilter, String orderBy) { + return sysTenantMapper.getSysTenantListByRoleId(tenantRoleId, sysTenantFilter, orderBy); + } + + /** + * 查询租户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param url url过滤条件。 + * @return 包含从租户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysTenantPermListWithDetail(Long tenantId, String url) { + return sysTenantMapper.getSysTenantPermListWithDetail(tenantId, url); + } + + /** + * 查询租户的权限字列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从租户到权限字的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysTenantPermCodeListWithDetail(Long tenantId, String permCode) { + return sysTenantMapper.getSysTenantPermCodeListWithDetail(tenantId, permCode); + } + + /** + * 查询租户的菜单列表。同时返回详细的分配路径。 + * + * @param tenantId 租户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从租户到菜单的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysTenantMenuListWithDetail(Long tenantId, String menuName) { + return sysTenantMapper.getSysTenantMenuListWithDetail(tenantId, menuName); + } + + private JSONObject makeMessageData(SysTenant sysTenant) { + JSONObject messageJsonData = new JSONObject(); + messageJsonData.put("sysTenant", sysTenant); + return messageJsonData; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/test/Tt.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/test/Tt.java new file mode 100644 index 0000000..1a5dfa6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/test/Tt.java @@ -0,0 +1,129 @@ +package apelet.tenantadmin.tenant.test; + +import javassist.*; + +import java.lang.reflect.Method; + + +public class Tt { +/* + + public static void main3(String[] args) throws Exception { + // 创建一个新的类池 + ClassPool pool = ClassPool.getDefault(); + // 创建一个新的类 + CtClass ctClass = pool.makeClass("MyClass"); + // 定义方法签名 + CtClass stringType = pool.get("java.lang.String"); + CtClass listType = pool.get("java.util.List"); + CtClass mapType = pool.get("java.util.Map"); + CtClass hashMapType = pool.get("java.util.HashMap"); + CtClass objectType = pool.get("java.lang.Object"); + // 创建方法参数类型数组 + CtClass[] parameterTypes = new CtClass[]{pool.get("java.lang.Double"), pool.get("java.lang.Double")}; + + // 创建方法返回类型 + CtClass returnType = mapType; + + // 创建方法 + CtMethod newMethod = new CtMethod(pool.get("java.lang.Double"), "processList", parameterTypes, ctClass); + // 添加方法体 + String body = "{" + + " Double d = $1 + $2;" + + "return d;" + + "}"; + newMethod.setBody(body); + // 将方法添加到类中 + ctClass.addMethod(newMethod); + // 加载并执行代码 + Class clazz = ctClass.toClass(); + Object instance = clazz.newInstance(); + Method method = clazz.getMethod("processList", Double.class, Double.class); + double keys = 12.0; + double values = 12.0; + Object o = method.invoke(instance, new Object[]{keys, values}); + System.out.println("Result: " + o); + } + + public static void main2(String[] args) throws Exception { + // 创建一个新的类池 + javassist.ClassPool pool = javassist.ClassPool.getDefault(); + // 创建一个新的类 + javassist.CtClass ctClass = pool.makeClass("MyClass"); + // 定义方法签名 + javassist.CtClass stringType = pool.get("java.lang.String"); + javassist.CtClass listType = pool.get("java.util.List"); + javassist.CtClass mapType = pool.get("java.util.Map"); + javassist.CtClass hashMapType = pool.get("java.util.HashMap"); + javassist.CtClass objectType = pool.get("java.lang.Object"); + // 创建方法参数类型数组 + javassist.CtClass[] parameterTypes = new javassist.CtClass[]{stringType, listType}; + // 创建方法返回类型 + javassist.CtClass returnType = mapType; + // 创建方法 + javassist.CtMethod newMethod = new javassist.CtMethod(returnType, "processList", parameterTypes, ctClass); + // 添加方法体 + newMethod.setBody("{" + + " java.util.Map resultMap = new java.util.HashMap();" + + " String[] arr = $1.toString().split(\",\"); " + + " java.util.List list = new java.util.ArrayList();" + + " for(int i=0; i<$2.size(); i++){" + + " resultMap.put(arr[i] , $2.get(i));" + + " System.out.println(\"Result: \" + resultMap);" + + " }" + + " return resultMap;" + + " }"); + // 将方法添加到类中 + ctClass.addMethod(newMethod); + // 加载并执行代码 + Class clazz = ctClass.toClass(); + Object instance = clazz.newInstance(); + Class[] classes = {String.class, List.class}; + Method method = clazz.getDeclaredMethod("processList", classes); + List inputList = Arrays.asList("item1", "item2", "item3"); + Map resultMap = (Map) method.invoke(instance, new Object[]{"key1,key2,key3", inputList}); + System.out.println("Result: " + resultMap); + } +*/ + + + + public static void main(String[] args) throws Exception { + // 创建一个 ClassPool 对象 + ClassPool pool = ClassPool.getDefault(); + + // 创建一个空的 CtClass 对象,表示要生成的类 + CtClass ctClass = pool.makeClass("MyCustomClass"); + + // 定义方法签名 +// CtMethod method = CtNewMethod.make( +// "public double multiply(double a, double b) { return a * b; }", +// ctClass); + CtClass[] parameters = new CtClass[2]; + parameters[0] = CtClass.doubleType; + parameters[1] = CtClass.doubleType; + CtMethod method = new CtMethod( + CtClass.doubleType, // 返回值类型 + "multiply", // 方法名称 + parameters , // 方法参数 + ctClass); + method.setBody("{ return $1 * $2; }"); + // 将方法添加到类中 + ctClass.addMethod(method); + + // 将生成的类写入文件 + ctClass.writeFile("output/"); + + // 加载生成的类 + Class clazz = ctClass.toClass(); + + // 实例化类 + Object instance = clazz.newInstance(); + + // 获取方法并执行 + Method m = clazz.getMethod("multiply", double.class, double.class); + Object invoke = m.invoke(instance, 3.0, 4.0); + System.out.println("Result: " + invoke); // 输出:Result: 12.0 + } + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/DeployPluginVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/DeployPluginVo.java new file mode 100644 index 0000000..3026866 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/DeployPluginVo.java @@ -0,0 +1,21 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.tenantadmin.tenant.model.DeployPlugin; +import lombok.Data; + +import java.io.Serializable; + +/** + *

+ * 自定义模板 + * 热部署插件表 + *

+ * + * @author admin + * @since 2024-06-27 14:58:01 + */ +@Data +public class DeployPluginVo extends DeployPlugin implements Serializable { + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantDatasourceVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantDatasourceVo.java new file mode 100644 index 0000000..77175d5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantDatasourceVo.java @@ -0,0 +1,72 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 租户数据源Vo对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户数据源Vo对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantDatasourceVo extends BaseVo { + + /** + * 租户数据源Id。 + */ + @Schema(description = "租户数据源Id") + private Long datasourceId; + + /** + * 租户数据源名称。 + */ + @Schema(description = "租户数据源名称") + private String datasourceName; + + /** + * 租户所在数据源的类型值。 + */ + @Schema(description = "租户所在数据源的类型值") + private Integer datasourceType; + + /** + * 主机名。 + */ + @Schema(description = "主机名") + private String databaseHost; + + /** + * 端口号。 + */ + @Schema(description = "端口号") + private Integer port; + + /** + * 用户名。 + */ + @Schema(description = "用户名") + private String username; + + /** + * 密码。 + */ + @Schema(description = "密码") + private String password; + + /** + * 模式名。 + */ + @Schema(description = "模式名") + private String schemaName; + + /** + * 数据库名。 + */ + @Schema(description = "数据库名") + private String databaseName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantMenuVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantMenuVo.java new file mode 100644 index 0000000..28452fd --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantMenuVo.java @@ -0,0 +1,93 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 租户菜单资源VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户菜单资源VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantMenuVo extends BaseVo { + + /** + * 租户菜单Id。 + */ + @Schema(description = "租户菜单Id") + private Long menuId; + + /** + * 菜单显示名称。 + */ + @Schema(description = "菜单显示名称") + private String menuName; + + /** + * 菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @Schema(description = "菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)") + private Integer menuType; + + /** + * 父菜单Id,目录菜单的父菜单为null。 + */ + @Schema(description = "父菜单Id") + private Long parentId; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @Schema(description = "前端表单路由名称") + private String formRouterName; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @Schema(description = "统计页面主键Id") + private Long reportPageId; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @Schema(description = "在线表单主键Id") + private Long onlineFormId; + + /** + * 在线表单菜单的权限控制类型,具体值可参考SysOnlineMenuPermType常量对象。 + */ + @Schema(description = "在线表单菜单的权限控制类型") + private Integer onlineMenuPermType; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @Schema(description = "菜单显示顺序") + private Integer showOrder; + + /** + * 菜单图标。 + */ + @Schema(description = "菜单图标") + private String icon; + + /** + * 附加信息。 + */ + @Schema(description = "附加信息") + private String extraData; + + /** + * 菜单与权限字关联对象列表。 + */ + @Schema(description = "菜单与权限字关联对象列表") + private List> sysMenuPermCodeList; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermCodeVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermCodeVo.java new file mode 100644 index 0000000..2a3271c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermCodeVo.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 租户权限字VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限字VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantPermCodeVo extends BaseVo { + + /** + * 租户权限字Id。 + */ + @Schema(description = "租户权限字Id") + private Long permCodeId; + + /** + * 上级权限字Id。 + */ + @Schema(description = "上级权限字Id") + private Long parentId; + + /** + * 租户权限字标识(一般为有含义的英文字符串)。 + */ + @Schema(description = "租户权限字标识") + private String permCode; + + /** + * 租户权限类型(0: 表单 1: UI片段 2: 操作)。 + */ + @Schema(description = "租户权限类型") + private Integer permCodeType; + + /** + * 租户权限字显示顺序(数值越小,越靠前)。 + */ + @Schema(description = "租户权限字显示顺序") + private Integer showOrder; + + /** + * 租户权限字显示名称。 + */ + @Schema(description = "租户权限字显示名称") + private String showName; + + /** + * 权限字与权限资源关联对象列表。 + */ + @Schema(description = "权限字与权限资源关联对象列表") + private List> sysPermCodePermList; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermModuleVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermModuleVo.java new file mode 100644 index 0000000..6898532 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermModuleVo.java @@ -0,0 +1,57 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 租户权限模块VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限模块VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantPermModuleVo extends BaseVo { + + /** + * 租户权限模块Id。 + */ + @Schema(description = "租户权限模块Id") + private Long moduleId; + + /** + * 上级权限模块Id。 + */ + @Schema(description = "上级权限模块Id") + private Long parentId; + + /** + * 租户权限模块名称。 + */ + @Schema(description = "租户权限模块名称") + private String moduleName; + + /** + * 租户权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @Schema(description = "租户权限模块类型") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @Schema(description = "权限模块在当前层级下的顺序") + private Integer showOrder; + + /** + * 权限资源对象列表。 + */ + @Schema(description = "权限资源对象列表") + private List> sysPermList; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermVo.java new file mode 100644 index 0000000..6d08aa6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantPermVo.java @@ -0,0 +1,56 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 租户权限资源VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户权限资源VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantPermVo extends BaseVo { + + /** + * 租户权限资源Id。 + */ + @Schema(description = "租户权限资源Id") + private Long permId; + + /** + * 租户权限所在的权限模块Id。 + */ + @Schema(description = "租户权限所在的权限模块Id") + private Long moduleId; + + /** + * 租户权限名称。 + */ + @Schema(description = "租户权限名称") + private String permName; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @Schema(description = "权限在当前模块下的顺序") + private Integer showOrder; + + /** + * 租户权限关联的URL。 + */ + @Schema(description = "租户权限关联的URL") + private String url; + + /** + * 权限模块字典数据。 + */ + @Schema(description = "权限模块字典数据") + private Map moduleIdDictMap; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantRoleVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantRoleVo.java new file mode 100644 index 0000000..a5e7d96 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantRoleVo.java @@ -0,0 +1,30 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 租户角色VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户角色VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantRoleVo extends BaseVo { + + /** + * 租户角色Id。 + */ + @Schema(description = "租户角色Id") + private Long tenantRoleId; + + /** + * 租户角色名称。 + */ + @Schema(description = "租户角色名称") + private String roleName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantVo.java new file mode 100644 index 0000000..efbfc4e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/vo/SysTenantVo.java @@ -0,0 +1,56 @@ +package apelet.tenantadmin.tenant.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 租户VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "租户VO对象") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTenantVo extends BaseVo { + + /** + * 租户Id。 + */ + @Schema(description = "租户Id") + private Long tenantId; + + /** + * 租户的英文编码。 + */ + @Schema(description = "租户的英文编码") + private String tenantCode; + + /** + * 租户的中文显示名称。 + */ + @Schema(description = "租户的中文显示名称") + private String showName; + + /** + * 租户所在数据源的类型值。 + */ + @Schema(description = "租户所在数据源的类型值") + private Integer datasourceType; + + /** + * 是否可用标记。 + */ + @Schema(description = "是否可用标记") + private Boolean available; + + /** + * 租户数据源字典。 + */ + @Schema(description = "租户数据源字典") + private Map datasourceTypeDictMap; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/bo/SysMenuPerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/bo/SysMenuPerm.java new file mode 100644 index 0000000..80e94c5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/bo/SysMenuPerm.java @@ -0,0 +1,67 @@ +package apelet.tenantadmin.upms.bo; + +import lombok.Data; + +import java.util.HashSet; +import java.util.Set; + +/** + * 菜单相关的业务对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +public class SysMenuPerm { + + + /** + * 菜单Id。 + */ + private Long menuId; + + /** + * 父菜单Id,目录菜单的父菜单为null + */ + private Long parentId; + + /** + * 菜单显示名称。 + */ + private String menuName; + + /** + * 菜单类型 (0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + private Integer menuType; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + private Long onlineFormId; + + /** + * 在线表单菜单的权限控制类型,具体值可参考SysOnlineMenuPermType常量对象。 + */ + private Integer onlineMenuPermType; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + private Long reportPageId; + + /** + * 仅用于在线表单的流程Id。 + */ + private Long onlineFlowEntryId; + + /** + * 关联权限URL集合。 + */ + Set permUrlSet = new HashSet<>(); + + /** + * 关联的某一个url。 + */ + String url; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/GlobalDictController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/GlobalDictController.java new file mode 100644 index 0000000..725e64b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/GlobalDictController.java @@ -0,0 +1,343 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.dict.dto.GlobalDictDto; +import apelet.common.dict.dto.GlobalDictItemDto; +import apelet.common.dict.model.GlobalDict; +import apelet.common.dict.model.GlobalDictItem; +import apelet.common.dict.service.GlobalDictItemService; +import apelet.common.dict.service.GlobalDictService; +import apelet.common.dict.vo.GlobalDictVo; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 全局通用字典操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "全局字典管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/globalDict") +public class GlobalDictController { + + @Autowired + private GlobalDictService globalDictService; + @Autowired + private GlobalDictItemService globalDictItemService; + + /** + * 新增全局字典接口。 + * + * @param globalDictDto 新增字典对象。 + * @return 保存后的字典对象。 + */ + @ApiOperationSupport(ignoreParameters = {"globalDictDto.dictId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody GlobalDictDto globalDictDto) { + String errorMessage = MyCommonUtil.getModelValidationError(globalDictDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 这里必须手动校验字典编码是否存在,因为我们缺省的实现是逻辑删除,所以字典编码字段没有设置为唯一索引。 + if (globalDictService.existDictCode(globalDictDto.getDictCode())) { + errorMessage = "数据验证失败,字典编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + GlobalDict globalDict = MyModelUtil.copyTo(globalDictDto, GlobalDict.class); + globalDictService.saveNew(globalDict); + return ResponseResult.success(globalDict.getDictId()); + } + + /** + * 更新全局字典操作。 + * + * @param globalDictDto 更新全局字典对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody GlobalDictDto globalDictDto) { + String errorMessage = MyCommonUtil.getModelValidationError(globalDictDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + GlobalDict originalGlobalDict = globalDictService.getById(globalDictDto.getDictId()); + if (originalGlobalDict == null) { + errorMessage = "数据验证失败,当前全局字典并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + GlobalDict globalDict = MyModelUtil.copyTo(globalDictDto, GlobalDict.class); + if (ObjectUtil.notEqual(globalDict.getDictCode(), originalGlobalDict.getDictCode()) + && globalDictService.existDictCode(globalDict.getDictCode())) { + errorMessage = "数据验证失败,字典编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!globalDictService.update(globalDict, originalGlobalDict)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定的全局字典。 + * + * @param dictId 指定全局字典主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody(required = true) Long dictId) { + if (!globalDictService.remove(dictId)) { + String errorMessage = "数据操作失败,全局字典Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看全局字典列表。 + * + * @param globalDictDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含角色列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody GlobalDictDto globalDictDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + GlobalDict filter = MyModelUtil.copyTo(globalDictDtoFilter, GlobalDict.class); + List globalDictList = + globalDictService.getGlobalDictList(filter, MyOrderParam.buildOrderBy(orderParam, GlobalDict.class)); + List globalDictVoList = + MyModelUtil.copyCollectionTo(globalDictList, GlobalDictVo.class); + long totalCount = 0L; + if (globalDictList instanceof Page) { + totalCount = ((Page) globalDictList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(globalDictVoList, totalCount)); + } + + /** + * 新增全局字典项目接口。 + * + * @param globalDictItemDto 新增字典项目对象。 + * @return 保存后的字典对象。 + */ + @ApiOperationSupport(ignoreParameters = {"globalDictItemDto.id"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/addItem") + public ResponseResult addItem(@MyRequestBody GlobalDictItemDto globalDictItemDto) { + String errorMessage = MyCommonUtil.getModelValidationError(globalDictItemDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!globalDictService.existDictCode(globalDictItemDto.getDictCode())) { + errorMessage = "数据验证失败,字典编码不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (globalDictItemService.existDictCodeAndItemId( + globalDictItemDto.getDictCode(), globalDictItemDto.getItemId())) { + errorMessage = "数据验证失败,该字典编码的项目Id已存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + GlobalDictItem globalDictItem = MyModelUtil.copyTo(globalDictItemDto, GlobalDictItem.class); + globalDictItemService.saveNew(globalDictItem); + return ResponseResult.success(globalDictItem.getId()); + } + + /** + * 更新全局字典项目。 + * + * @param globalDictItemDto 更新全局字典项目对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/updateItem") + public ResponseResult updateItem(@MyRequestBody GlobalDictItemDto globalDictItemDto) { + String errorMessage = MyCommonUtil.getModelValidationError(globalDictItemDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + GlobalDictItem originalGlobalDictItem = globalDictItemService.getById(globalDictItemDto.getId()); + if (originalGlobalDictItem == null) { + errorMessage = "数据验证失败,当前全局字典项目并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + GlobalDictItem globalDictItem = MyModelUtil.copyTo(globalDictItemDto, GlobalDictItem.class); + if (ObjectUtil.notEqual(globalDictItem.getDictCode(), originalGlobalDictItem.getDictCode())) { + errorMessage = "数据验证失败,字典项目的字典编码不能修改!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (ObjectUtil.notEqual(globalDictItem.getItemId(), originalGlobalDictItem.getItemId()) + && globalDictItemService.existDictCodeAndItemId(globalDictItem.getDictCode(), globalDictItem.getItemId())) { + errorMessage = "数据验证失败,该字典编码已经包含了该项目Id!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!globalDictItemService.update(globalDictItem, originalGlobalDictItem)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 更新全局字典项目的状态。 + * + * @param id 更新全局字典项目主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/updateItemStatus") + public ResponseResult updateItemStatus( + @MyRequestBody(required = true) Long id, @MyRequestBody(required = true) Integer status) { + String errorMessage; + GlobalDictItem dictItem = globalDictItemService.getById(id); + if (dictItem == null) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (ObjectUtil.notEqual(dictItem.getStatus(), status)) { + globalDictItemService.updateStatus(dictItem, status); + } + return ResponseResult.success(); + } + + /** + * 删除指定编码的全局字典项目。 + * + * @param id 主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/deleteItem") + public ResponseResult deleteItem(@MyRequestBody(required = true) Long id) { + String errorMessage; + GlobalDictItem dictItem = globalDictItemService.getById(id); + if (dictItem == null) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!globalDictItemService.remove(dictItem)) { + errorMessage = "数据操作失败,全局字典项目Id不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 获取指定字典编码的全局字典项目。字典的键值为[itemId, itemName]。 + * NOTE: 白名单接口。 + * + * @param dictCode 字典编码。 + * @return 应答结果对象。 + */ + @GetMapping("/listDict") + public ResponseResult>> listDict(@RequestParam String dictCode) { + List resultList = + globalDictService.getGlobalDictItemListFromCache(dictCode, null); + resultList = resultList.stream() + .sorted(Comparator.comparing(GlobalDictItem::getStatus)) + .sorted(Comparator.comparing(GlobalDictItem::getShowOrder)) + .collect(Collectors.toList()); + return ResponseResult.success(this.toDictDataList(resultList)); + } + + /** + * 根据字典Id集合,获取查询后的字典数据。 + * NOTE: 白名单接口。 + * + * @param dictCode 字典编码。 + * @param itemIds 字典项目Id集合。 + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listDictByIds") + public ResponseResult>> listDictByIds( + @RequestParam String dictCode, @RequestParam List itemIds) { + List resultList = + globalDictService.getGlobalDictItemListFromCache(dictCode, new HashSet<>(itemIds)); + return ResponseResult.success(this.toDictDataList(resultList)); + } + + /** + * 白名单接口,登录用户均可访问。以字典形式返回全部字典数据集合。 + * fullResultList中的字典列表全部取自于数据库,而cachedResultList全部取自于缓存,前端负责比对。 + * + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listAll") + public ResponseResult listAll(@RequestParam String dictCode) { + List fullResultList = + globalDictItemService.getGlobalDictItemListByDictCode(dictCode); + List cachedList = + globalDictService.getGlobalDictItemListFromCache(dictCode, null); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("fullResultList", this.toDictDataList2(fullResultList)); + jsonObject.put("cachedResultList", this.toDictDataList2(cachedList)); + return ResponseResult.success(jsonObject); + } + + /** + * 将当前字典表的数据重新加载到缓存中。 + * 由于缓存的数据更新,在add/update/delete等接口均有同步处理。因此该接口仅当同步过程中出现问题时, + * 可手工调用,或者每天晚上定时同步一次。 + */ + @OperationLog(type = SysOperationLogType.RELOAD_CACHE) + @GetMapping("/reloadCachedData") + public ResponseResult reloadCachedData(@RequestParam String dictCode) { + globalDictService.reloadCachedData(dictCode); + return ResponseResult.success(true); + } + + private List> toDictDataList(List resultList) { + return resultList.stream().map(item -> { + Map dataMap = new HashMap<>(2); + dataMap.put(ApplicationConstant.DICT_ID, item.getItemId()); + dataMap.put(ApplicationConstant.DICT_NAME, item.getItemName()); + dataMap.put("showOrder", item.getShowOrder()); + dataMap.put("status", item.getStatus()); + return dataMap; + }).collect(Collectors.toList()); + } + + private List> toDictDataList2(List resultList) { + return resultList.stream().map(item -> { + Map dataMap = new HashMap<>(2); + dataMap.put(ApplicationConstant.DICT_ID, item.getId()); + dataMap.put("itemId", item.getItemId()); + dataMap.put(ApplicationConstant.DICT_NAME, item.getItemName()); + dataMap.put("showOrder", item.getShowOrder()); + dataMap.put("status", item.getStatus()); + return dataMap; + }).collect(Collectors.toList()); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java new file mode 100644 index 0000000..67d5f77 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java @@ -0,0 +1,1554 @@ +package apelet.tenantadmin.upms.controller; + + +import apelet.common.botp.service.execute.entity.BillData; +import apelet.common.core.annotation.DisableDataFilter; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.annotation.NoAuthInterface; +import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.upload.*; +import apelet.common.core.util.*; +import apelet.common.datafilter.config.DataFilterProperties; +import apelet.common.eas.util.EasConfig; +import apelet.common.eas.util.EasUtil; +import apelet.common.flow.online.service.FlowOnlineOperationService; +import apelet.common.generator.utils.OrmGenDataSourceUtil; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.mobile.model.MobileEntry; +import apelet.common.mobile.object.MobileEntryExtraData; +import apelet.common.mobile.service.MobileEntryService; +import apelet.common.online.service.OnlineOperationService; +import apelet.common.orm.impl.*; +import apelet.common.redis.cache.SessionCacheHelper; +import apelet.common.report.service.ReportOperationService; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.upms.bo.SysMenuPerm; +import apelet.tenantadmin.upms.model.*; +import apelet.tenantadmin.upms.model.constant.*; +import apelet.tenantadmin.upms.service.*; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.github.xiaoymin.knife4j.annotations.ApiSupport; +import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.redisson.api.RBucket; +import org.redisson.api.RKeys; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import apelet.common.eas.util.EasUtil; +/** + * 登录接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@ApiSupport(order = 1) +@Tag(name = "用户登录接口") +@DisableDataFilter +@Slf4j +@RestController +@RefreshScope +@RequestMapping("/tenantadmin/upms/login") +public class LoginController { + + @Autowired + private MobileEntryService mobileEntryService; + @Autowired + private SysUserService sysUserService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysMenuService sysMenuService; + @Autowired + private SysPermCodeService sysPermCodeService; + @Autowired + private SysPermService sysPermService; + @Autowired + private SysPostService sysPostService; + @Autowired + private SysRoleService sysRoleService; + @Autowired + private SysDataPermService sysDataPermService; + @Autowired + private SysPermWhitelistService sysPermWhitelistService; + @Autowired + private OnlineOperationService onlineOperationService; + @Autowired + private FlowOnlineOperationService flowOnlineOperationService; + @Autowired + private ReportOperationService reportOperationService; + @Autowired + private ApplicationConfig appConfig; + @Autowired + private RedissonClient redissonClient; + @Autowired + private SessionCacheHelper cacheHelper; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private CaptchaService captchaService; + @Autowired + private UpDownloaderFactory upDownloaderFactory; + @Autowired + private DataFilterProperties dataFilterProperties; + @Value("${hubCloud.secret}") + private String HubCloudSecret; + @Value("${dingTalk.secret}") + private String dingTalkSecret; + + @Value("${hubCloud.appId}") + private String hubCloudAppId = "500316667"; + + private static final String IS_ADMIN = "isAdmin"; + private static final String SESSION_ID = "sessionId"; + private static final String SHOW_NAME_FIELD = "showName"; + private static final String SHOW_ORDER_FIELD = "showOrder"; + private static final String HEAD_IMAGE_URL_FIELD = "headImageUrl"; + private static final String THIRD_PARTY_CONFIG = "third_party_config"; + + + @Autowired + private OrmGenDataSourceUtil ormGenDataSourceUtil; + + + + + @PostMapping("/getEhrLtpa") + @NoAuthInterface + public ResponseResult getEhrLtap( + @MyRequestBody String username + ){ + EasConfig easConfig = ApplicationContextHolder.getBean("easConfig"); + String path = easConfig.getLtpaToken(); + // String path = "/ehrapp/yd/LtpaToken.properties"; // 测试环境地址 + System.out.println("getEasSsoUrl path = " + path); + String password = LtpaTokenManager.generate(username, path).toString(); + JSONObject jsonData = new JSONObject(); + jsonData.put("ltap",password); + return ResponseResult.success(jsonData); + } + + @PostMapping("/test") + @NoAuthInterface + public ResponseResult test( + @MyRequestBody String datasourceVariableName,//im_purinbill + @MyRequestBody List> selector, + @MyRequestBody List> filter + + ) throws Exception { + + Selector selector1 = new Selector(); + for (int i = 0; selector != null && i < selector.size(); i++) { + selector1.getList().add(new SelectorItem((String) selector.get(i).get("property"))); + } + Filter filter1 = new Filter(); + for (int i = 0; filter != null && i < filter.size(); i++) { + filter1.add(new FilterItem((String) filter.get(i).get("property"), (String) filter.get(i).get("cp"), filter.get(i).get("value"))); + } + // selector.getList().add(new SelectorItem("number")); + // selector.getList().add(new SelectorItem("storage_org.name")); + // Filter filter = new Filter() ; + // filter.getList().add(new FilterItem("name",FilterItem.like,"%测%")); + + + ObjectCollection obCol = ormGenDataSourceUtil.query(datasourceVariableName, filter1, selector1); + List billDataList = new ArrayList<>(); + + for (int i = 0; i < obCol.size(); i++) { + billDataList.add(convertOvToBillData(obCol.getObject(i))); + } + MyPageData myPageData = new MyPageData(billDataList, (long) billDataList.size()); + + return ResponseResult.success(myPageData); + } + + private BillData convertOvToBillData(ObjectValue objectValue) { + + BillData billData = new BillData(); + Map headData = new HashMap<>(); + Map>> entryDataCol = new HashMap<>(); + if (objectValue.size() > 0) { + Iterator it = objectValue.getValues().keySet().iterator(); + while (it.hasNext()) { + String fieldKey = (String) it.next(); + if (objectValue.get(fieldKey) == null) { + continue; + } + if (objectValue.get(fieldKey) instanceof ObjectCollection) { + ObjectCollection objectCollection = objectValue.getObjectCollection(fieldKey); + List> entryList = new ArrayList<>(); + Map entryMap = new HashMap<>(); + for (int i = 0; i < objectCollection.size(); i++) { + ObjectValue entryValue = objectCollection.getObject(i); + Iterator entryIt = entryValue.getValues().keySet().iterator(); + while (entryIt.hasNext()) { + String entryFiledKey = (String) entryIt.next(); + if (entryValue.get(entryFiledKey) instanceof ObjectValue) { + ObjectValue entityValue = (ObjectValue) entryValue.get(entryFiledKey); + Long pk = entityValue.getPkValue(); + if (pk == null || pk == 0L) { + continue; + } + entityValue = ormGenDataSourceUtil.queryOne(entityValue.getTableName(), pk); + if (entityValue == null) { + continue; + } + entryMap.put(entryFiledKey, entityValue.get("id")); + entryMap.put(entryFiledKey + "_", entityValue.getValues()); + } else { + entryMap.put(entryFiledKey, entryValue.get(entryFiledKey)); + } + } + entryList.add(entryMap); + } + entryDataCol.put(fieldKey, entryList); + } else if (objectValue.get(fieldKey) instanceof ObjectValue) { + ObjectValue entityValue = (ObjectValue) objectValue.get(fieldKey); + Long pk = entityValue.getPkValue(); + if (pk == null || pk == 0L) { + continue; + } + entityValue = ormGenDataSourceUtil.queryOne(entityValue.getTableName(), pk); + if (entityValue == null) { + continue; + } + headData.put(fieldKey, entityValue.get("id")); + headData.put(fieldKey + "_", entityValue.getValues()); + } else { + headData.put(fieldKey, objectValue.get(fieldKey)); + } + } + } + billData.setHeadData(headData); + billData.setEntryData(entryDataCol); + + return billData; + } + + @PostMapping("/delete") + @NoAuthInterface + public ResponseResult delete( + @MyRequestBody String datasourceVariableName,//im_purinbill + @MyRequestBody Filter filter + ) throws Exception { + + + return ResponseResult.success(ormGenDataSourceUtil.delete(datasourceVariableName, filter)); + } + + @PostMapping("/update") + @NoAuthInterface + public ResponseResult update( + @MyRequestBody String datasourceVariableName,//im_purinbill + @MyRequestBody JSONObject objectValue + ) throws Exception { + + ObjectValue oValue = new ObjectValue(datasourceVariableName); + + Iterator it = objectValue.keySet().iterator(); + while (it.hasNext()) { + String key = (String) it.next(); + Object obj = objectValue.get(key); + if (obj instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) obj; + ObjectValue ov = new ObjectValue(null); + ov.put("id", jsonObject.get("id")); + oValue.put(key, ov); + } else if (obj instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) obj; + ObjectCollection objectCollection = new ObjectCollection(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Iterator it1 = jsonObject.keySet().iterator(); + ObjectValue ov = new ObjectValue(null); + while (it1.hasNext()) { + String colKey = (String) it1.next(); + ov.put(colKey, jsonObject.get(colKey)); + } + objectCollection.addObject(ov); + } + oValue.put(key, objectCollection); + } else { + oValue.put(key, obj); + } + + } + + Filter filter = new Filter(); + filter.add(new FilterItem("id", FilterItem.equals, oValue.get("id"))); + return ResponseResult.success(ormGenDataSourceUtil.update(datasourceVariableName, oValue, null)); + } + + + @PostMapping("/addnew") + @NoAuthInterface + public ResponseResult addnew( + @MyRequestBody String datasourceVariableName,//im_purinbill + @MyRequestBody JSONObject objectValue + ) throws Exception { + + ObjectValue oValue = new ObjectValue(datasourceVariableName); + + Iterator it = objectValue.keySet().iterator(); + while (it.hasNext()) { + String key = (String) it.next(); + Object obj = objectValue.get(key); + if (obj instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) obj; + ObjectValue ov = new ObjectValue(null); + ov.put("id", jsonObject.get("id")); + oValue.put(key, ov); + } else if (obj instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) obj; + ObjectCollection objectCollection = new ObjectCollection(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Iterator it1 = jsonObject.keySet().iterator(); + ObjectValue ov = new ObjectValue(null); + while (it1.hasNext()) { + String colKey = (String) it1.next(); + ov.put(colKey, jsonObject.get(colKey)); + } + objectCollection.addObject(ov); + } + oValue.put(key, objectCollection); + } else { + oValue.put(key, obj); + } + + } + + + return ResponseResult.success(ormGenDataSourceUtil.addNew(datasourceVariableName, oValue)); + } + + /** + * 登录接口。 + * + * @param loginName 登录名。 + * @param password 密码。 + * @param captchaVerification 验证码。 + * @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。 + */ + @Parameters({ + // 这里包含密码密文,仅用于方便开发期间的接口测试,集成测试和发布阶段,需要将当前注解去掉。 + // 如果您重新生成了公钥和私钥,请替换password的缺省值。 + @Parameter(name = "loginName", example = "admin"), + @Parameter(name = "password", example = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D"), + @Parameter(name = "captchaVerification", example = "为了方便测试,这里可以修改一下代码,hardcode一个每次都ok的验证码") + }) + @NoAuthInterface + @OperationLog(type = SysOperationLogType.LOGIN, saveResponse = false) + @PostMapping("/doLogin") + public ResponseResult doLogin( + @MyRequestBody String loginName, + @MyRequestBody String password, + @MyRequestBody String captchaVerification) throws UnsupportedEncodingException { + if (MyCommonUtil.existBlankArgument(loginName, password, captchaVerification)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(captchaVerification); + ResponseModel response = captchaService.verification(captchaVO); + // if (!response.isSuccess()) { + // //验证码校验失败,返回信息告诉前端 + // //repCode 0000 无异常,代表成功 + // //repCode 9999 服务器内部异常 + // //repCode 0011 参数不能为空 + // //repCode 6110 验证码已失效,请重新获取 + // //repCode 6111 验证失败 + // //repCode 6112 获取验证码失败,请联系管理员 + // errorMessage = String.format("数据验证失败,验证码错误,错误码 [%s] 错误信息 [%s]", + // response.getRepCode(), response.getRepMsg()); + // return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + // } + ResponseResult verifyResult = this.verifyAndHandleLoginUser(loginName, password); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + JSONObject jsonData = this.buildLoginData(verifyResult.getData(), null, null); + return ResponseResult.success(jsonData); + } + + + /** + * 登录接口。 + * + * @param loginName 登录名。 + * @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。 + */ + @Parameters({ + // 这里包含密码密文,仅用于方便开发期间的接口测试,集成测试和发布阶段,需要将当前注解去掉。 + // 如果您重新生成了公钥和私钥,请替换password的缺省值。 + @Parameter(name = "loginName", example = "admin"), + @Parameter(name = "password", example = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D"), + @Parameter(name = "captchaVerification", example = "为了方便测试,这里可以修改一下代码,hardcode一个每次都ok的验证码") + }) + @NoAuthInterface + @OperationLog(type = SysOperationLogType.LOGIN, saveResponse = false) + @PostMapping("/doLogin1") + public ResponseResult doLogin1( + @MyRequestBody String loginName, + @MyRequestBody String appKey, + @MyRequestBody String appSecurity + ) throws UnsupportedEncodingException { + if (MyCommonUtil.existBlankArgument(loginName, appKey, appSecurity)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + + String errorMessage; + CaptchaVO captchaVO = new CaptchaVO(); +// captchaVO.setCaptchaVerification(captchaVerification); + ResponseModel response = captchaService.verification(captchaVO); + // if (!response.isSuccess()) { + // //验证码校验失败,返回信息告诉前端 + // //repCode 0000 无异常,代表成功 + // //repCode 9999 服务器内部异常 + // //repCode 0011 参数不能为空 + // //repCode 6110 验证码已失效,请重新获取 + // //repCode 6111 验证失败 + // //repCode 6112 获取验证码失败,请联系管理员 + // errorMessage = String.format("数据验证失败,验证码错误,错误码 [%s] 错误信息 [%s]", + // response.getRepCode(), response.getRepMsg()); + // return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + // } +// ResponseResult verifyResult = this.verifyAndHandleLoginUser(loginName, password); + //此处验证方法待补充 + ResponseResult verifyResult = this.verifyAndHandleLoginUser(loginName, appKey, appSecurity); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } +// JSONObject jsonData = this.buildLoginData(verifyResult.getData(),appKey,appSecurity); + JSONObject jsonData = buildLoginSecurity(appKey, appSecurity, verifyResult); + return ResponseResult.success(jsonData); + } + + private @NotNull JSONObject buildLoginSecurity(String appKey, String appSecurity, ResponseResult verifyResult) { + int deviceType = MyCommonUtil.getDeviceType(); + SysUser user = verifyResult.getData(); + String sessionId = user.getLoginName() + "_" + deviceType + "_" + MyCommonUtil.generateUuid(); +// JSONObject jsonData = this.createResponseData(user, sessionId); + Map claims = new HashMap<>(3); + claims.put(SESSION_ID, sessionId); + String token = JwtUtil.generateToken(claims, appConfig.getExpiration(), appConfig.getTokenSigningKey()); + JSONObject jsonData = new JSONObject(); + jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token); + TokenData tokenData = this.buildTokenData(user, sessionId, deviceType, appKey, appSecurity); + this.putTokenDataToSessionCache(tokenData); + // 这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志。 + TokenData.addToRequest(tokenData); + return jsonData; + } + + /** + * 登录移动端接口。 + * + * @param loginName 登录名。 + * @param password 密码。 + * @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。 + */ + @Parameters({ + // 这里包含密码密文,仅用于方便开发期间的接口测试,集成测试和发布阶段,需要将当前注解去掉。 + // 如果您重新生成了公钥和私钥,请替换password的缺省值。 + @Parameter(name = "loginName", example = "admin"), + @Parameter(name = "password", example = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D") + }) + @NoAuthInterface + @OperationLog(type = SysOperationLogType.LOGIN_MOBILE, saveResponse = false) + @PostMapping("/doMobileLogin") + public ResponseResult doMobileLogin( + @MyRequestBody String loginName, + @MyRequestBody String password) throws UnsupportedEncodingException { + if (MyCommonUtil.existBlankArgument(loginName, password)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + ResponseResult verifyResult = this.verifyAndHandleLoginUser(loginName, password); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + JSONObject jsonData = this.buildMobileLoginData(verifyResult.getData()); + return ResponseResult.success(jsonData); + } + + /** + * 登出操作。同时将Session相关的信息从缓存中删除。 + * + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.LOGOUT) + @PostMapping("/doLogout") + public ResponseResult doLogout() { + String sessionId = TokenData.takeFromRequest().getSessionId(); + String sessionIdKey = RedisKeyUtil.makeSessionIdKey(sessionId); + redissonClient.getBucket(sessionIdKey).delete(); + redissonClient.getBucket(RedisKeyUtil.makeSessionWhiteListPermKey(sessionId)).deleteAsync(); + String menuPermPattern = RedisKeyUtil.getSessionMenuPermPrefix(sessionId) + "*"; + redissonClient.getKeys().deleteByPatternAsync(menuPermPattern); + sysDataPermService.removeDataPermCache(sessionId); + sysPermService.removeUserSysPermCache(sessionId); + cacheHelper.removeAllSessionCache(sessionId); + return ResponseResult.success(); + } + + /** + * 在登录之后,通过token再次获取登录信息。 + * 用于在当前浏览器登录系统后,在新tab页中可以免密登录。 + * + * @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。 + */ + @GetMapping("/getLoginInfo") + public ResponseResult getLoginInfo() { + TokenData tokenData = TokenData.takeFromRequest(); + // 这里解释一下为什么没有缓存menuList和permCodeList。 + // 1. 该操作和权限验证不同,属于低频操作。 + // 2. 第一次登录和再次获取登录信息之间,如果修改了用户的权限,那么本次获取的是最新权限。 + // 3. 上一个问题无法避免,因为即便缓存也是有过期时间的,过期之后还是要从数据库获取的。 + JSONObject jsonData = new JSONObject(); + jsonData.put(SHOW_NAME_FIELD, tokenData.getShowName()); + jsonData.put(IS_ADMIN, tokenData.getIsAdmin()); + if (StrUtil.isNotBlank(tokenData.getHeadImageUrl())) { + jsonData.put(HEAD_IMAGE_URL_FIELD, tokenData.getHeadImageUrl()); + } + Collection menuList; + Collection permCodeList; + if (BooleanUtil.isTrue(tokenData.getIsAdmin())) { + menuList = sysMenuService.getAllListByOrder(SHOW_ORDER_FIELD); + permCodeList = sysPermCodeService.getAllPermCodeList(); + } else { + menuList = sysMenuService.getMenuListByUserId(tokenData.getUserId()); + permCodeList = sysPermCodeService.getPermCodeListByUserId(tokenData.getUserId()); + } + OnlinePermData onlinePermData = this.getAndCacheOnlineMenuPermData(menuList, true); + permCodeList.addAll(onlinePermData.permCodeSet); + OnlinePermData onlineFlowPermData = this.getAndCacheFlowOnlineMenuPermData(menuList, true); + permCodeList.addAll(onlineFlowPermData.permCodeSet); + menuList = menuList.stream().filter(m -> m.getMenuType() <= SysMenuType.TYPE_MENU).collect(Collectors.toList()); + jsonData.put("menuList", menuList); + jsonData.put("permCodeList", permCodeList); + return ResponseResult.success(jsonData); + } + + /** + * 用户修改自己的密码。 + * + * @param oldPass 原有密码。 + * @param newPass 新密码。 + * @return 应答结果对象。 + */ + @PostMapping("/changePassword") + public ResponseResult changePassword( + @MyRequestBody String oldPass, @MyRequestBody String newPass) throws UnsupportedEncodingException { + if (MyCommonUtil.existBlankArgument(newPass, oldPass)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + TokenData tokenData = TokenData.takeFromRequest(); + SysUser user = sysUserService.getById(tokenData.getUserId()); + oldPass = URLDecoder.decode(oldPass, StandardCharsets.UTF_8.name()); + // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。 + // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。 + oldPass = RsaUtil.decrypt(oldPass, ApplicationConstant.PRIVATE_KEY); + if (user == null || !passwordEncoder.matches(oldPass, user.getPassword())) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD); + } + newPass = URLDecoder.decode(newPass, StandardCharsets.UTF_8.name()); + newPass = RsaUtil.decrypt(newPass, ApplicationConstant.PRIVATE_KEY); + if (!sysUserService.changePassword(tokenData.getUserId(), newPass)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 上传并修改用户头像。 + * + * @param uploadFile 上传的头像文件。 + */ + @PostMapping("/changeHeadImage") + public void changeHeadImage(@RequestParam("uploadFile") MultipartFile uploadFile) throws IOException { + UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, HEAD_IMAGE_URL_FIELD); + BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType()); + UploadResponseInfo responseInfo = upDownloader.doUpload(null, + appConfig.getUploadFileBaseDir(), SysUser.class.getSimpleName(), HEAD_IMAGE_URL_FIELD, true, uploadFile); + if (BooleanUtil.isTrue(responseInfo.getUploadFailed())) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, + ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage())); + return; + } + responseInfo.setDownloadUri("/tenantadmin/upms/login/downloadHeadImage"); + String newHeadImage = JSONArray.toJSONString(CollUtil.newArrayList(responseInfo)); + if (!sysUserService.changeHeadImage(TokenData.takeFromRequest().getUserId(), newHeadImage)) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST)); + return; + } + ResponseResult.output(ResponseResult.success(responseInfo)); + } + + /** + * 下载用户头像。 + * + * @param filename 文件名。如果没有提供该参数,就从当前记录的指定字段中读取。 + * @param response Http 应答对象。 + */ + @GetMapping("/downloadHeadImage") + public void downloadHeadImage(String filename, HttpServletResponse response) { + try { + UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, HEAD_IMAGE_URL_FIELD); + BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType()); + upDownloader.doDownload(appConfig.getUploadFileBaseDir(), + SysUser.class.getSimpleName(), HEAD_IMAGE_URL_FIELD, filename, true, response); + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + log.error(e.getMessage(), e); + } + } + + + /** + * pc云之家登录 + * + * @param ticket + * @param appid + * @param request + * @param response + * @return + * @throws Exception + */ + @GetMapping("/doLoginValidateHubCloud") + @NoAuthInterface + public ResponseResult doLoginValidateHubCloud(@RequestParam(name = "ticket") String ticket, + @RequestParam(name = "appid") String appid, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + log.info("Rest api login."); + try { + // Object tokenObj = redissonClient.getBucket(appid).get(); + String accessToken=""; + // if (tokenObj != null) { + // accessToken=tokenObj.toString(); + // }else { + String tokenUrl = "https://www.yunzhijia.com/gateway/oauth2/token/getAccessToken"; + Map paramMap = new HashMap<>(); + paramMap.put("appId", appid); + paramMap.put("secret", HubCloudSecret); + paramMap.put("timestamp", System.currentTimeMillis()); + paramMap.put("scope", "app"); + // 发送 POST 请求 + String tokenData = HttpUtil.post(tokenUrl, paramMap); + if (StringUtils.isBlank(tokenData)){ + return ResponseResult.error("500", "通过appId获取AccessToken失败,appId:" + appid); + } + JSONObject tokenJson = JSONObject.parseObject(tokenData); + if (!tokenJson.getBoolean("success")) { + return ResponseResult.error("500", "通过appId获取AccessToken失败,appId:" + appid); + } + accessToken = ((JSONObject) (tokenJson.get("data"))).get("accessToken").toString(); + //accessToken的有效时间为6400秒,在该有效期内多次获取均返回同一token, + //连许多次请求会被拦截,因此需要本地缓存。为防止极端情况,设置过期时间为6200s + redissonClient.getBucket(appid).set(accessToken,6200,TimeUnit.SECONDS); + // } + String jsonInputString = String.format("{\"appid\":\"%s\", \"ticket\":\"%s\"}", appid, ticket); + String infoUrl="https://www.yunzhijia.com/gateway/ticket/user/acquirecontext?accessToken="+accessToken; + String userStr = HttpUtil.post(infoUrl, jsonInputString); + log.info("validateHubCloudLogin appid:{}, ticket:{} ", appid, ticket); + JSONObject userObj = JSONObject.parseObject(userStr); + if (!userObj.getBoolean("success")) { + //若失败则可能是token失效的原因,需要删除redis中的token + redissonClient.getBucket(appid).delete(); + return ResponseResult.error("500", userObj.getString("message")); + } + log.info(" get user result:", userObj); + //通过OpenId 获取系统用户 + String openId = userObj.getJSONObject("data").getString("openid"); + SysUser user = sysUserService.getUserByHubOpenId(openId); + if (user == null) { + return ResponseResult.error("500", "该账号暂未绑定系统用户"); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, "登录失败,用户账号被锁定!"); + } + JSONObject jsonData = this.buildLoginData(user, null, null); + return ResponseResult.success(jsonData); + } catch (Exception e) { + e.printStackTrace(); + return ResponseResult.error("500", "自动登录失败"); + } + } + + /** + * 移动云之家登录 + * + * @param ticket + * @param appid + * @param request + * @param response + * @return + * @throws Exception + */ + + @GetMapping("/doLoginValidateHubCloudm") + @NoAuthInterface + public ResponseResult doLoginValidateHubCloudm(@RequestParam(name = "ticket") String ticket, + @RequestParam(name = "appid") String appid, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + log.info("Rest api mlogin."); + try { + // Object tokenObj = redissonClient.getBucket(appid).get(); + String accessToken=""; + // if (tokenObj != null) { + // accessToken=tokenObj.toString(); + // }else { + if(StringUtils.isEmpty(appid)){ + appid = hubCloudAppId; + } + String tokenUrl = "https://www.yunzhijia.com/gateway/oauth2/token/getAccessToken"; + Map paramMap = new HashMap<>(); + paramMap.put("appId", appid); + paramMap.put("secret", HubCloudSecret); + paramMap.put("timestamp", System.currentTimeMillis()); + paramMap.put("scope", "app"); + // 发送 POST 请求 + String tokenData = HttpUtil.post(tokenUrl, paramMap); + if (StringUtils.isBlank(tokenData)){ + return ResponseResult.error("500", "通过appId获取AccessToken失败,appId:" + appid); + } + JSONObject tokenJson = JSONObject.parseObject(tokenData); + if (!tokenJson.getBoolean("success")) { + return ResponseResult.error("500", "通过appId获取AccessToken失败,appId:" + appid); + } + accessToken = ((JSONObject) (tokenJson.get("data"))).get("accessToken").toString(); + //accessToken的有效时间为6400秒,在该有效期内多次获取均返回同一token, + //连许多次请求会被拦截,因此需要本地缓存。为防止极端情况,设置过期时间为6200s + redissonClient.getBucket(appid).set(accessToken,6200,TimeUnit.SECONDS); + // } + String jsonInputString = String.format("{\"appid\":\"%s\", \"ticket\":\"%s\"}", appid, ticket); + String infoUrl="https://www.yunzhijia.com/gateway/ticket/user/acquirecontext?accessToken="+accessToken; + String userStr = HttpUtil.post(infoUrl, jsonInputString); + log.info("validateHubCloudLogin appid:{}, ticket:{} ", appid, ticket); + JSONObject userObj = JSONObject.parseObject(userStr); + if (!userObj.getBoolean("success")) { + //若失败则可能是token失效的原因,需要删除redis中的token + redissonClient.getBucket(appid).delete(); + return ResponseResult.error("500", userObj.getString("message")); + } + log.info(" get user result:", userObj); + //通过OpenId 获取系统用户 + String openId = userObj.getJSONObject("data").getString("openid"); + SysUser user = sysUserService.getUserByHubOpenId(openId); + if (user == null) { + return ResponseResult.error("500", "该账号暂未绑定系统用户"); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, "登录失败,用户账号被锁定!"); + } + JSONObject jsonData = this.buildMobileLoginData(user); + return ResponseResult.success(jsonData); + } catch (Exception e) { + e.printStackTrace(); + return ResponseResult.error("500", "自动登录失败"); + } + } + + /** + * pc钉钉登录 + * + * @param code + * @param appKey + * @param request + * @param response + * @return + * @throws Exception + */ + + @GetMapping("/doLoginValidateDingTalk") + @NoAuthInterface + public ResponseResult doLoginValidateDingTalk(@RequestParam(name = "code") String code, + @RequestParam(name = "appKey") String appKey, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + log.info("Rest api login."); + try { + Object tokenObj = redissonClient.getBucket(appKey).get(); + String accessToken=""; + if (tokenObj != null) { + accessToken=tokenObj.toString(); + }else { + String tokenUrl = "https://api.dingtalk.com/v1.0/oauth2/accessToken"; + String jsonInputString = String.format( + "{\"appKey\":\"%s\", \"appSecret\":\"%s\"}", + appKey, dingTalkSecret); + // 发送 POST 请求 + String tokenData = HttpUtil.post(tokenUrl, jsonInputString); + if (StringUtils.isBlank(tokenData)){ + return ResponseResult.error("500", "通过appKey获取AccessToken失败,appKey:" + appKey); + } + JSONObject tokenJson = JSONObject.parseObject(tokenData); + accessToken = tokenJson.get("accessToken").toString(); + //accessToken的有效时间为7200秒,在该有效期内多次获取均返回同一token, + //连许多次请求会被拦截,因此需要本地缓存。为防止极端情况,设置过期时间为6200s + redissonClient.getBucket(appKey).set(accessToken,7000,TimeUnit.SECONDS); + } + String jsonInputString = String.format("{\"code\":\"%s\"}",code); + String infoUrl="https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token="+accessToken; + String userStr = HttpUtil.post(infoUrl, jsonInputString); + log.info("validateHubCloudLogin appKey:{}, code:{} ", appKey, code); + JSONObject userObj = JSONObject.parseObject(userStr); +// if (!userObj.getBoolean("success")) { +// return ResponseResult.error("500", userObj.getString("message")); +// } + log.info(" get user result:", userObj); + //通过userId 获取系统用户 + String userId = userObj.getJSONObject("result").getString("userid"); + SysUser user = sysUserService.getUserByDingTalkOpenId(userId); + if (user == null) { + return ResponseResult.error("500", "该账号暂未绑定系统用户"); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, "登录失败,用户账号被锁定!"); + } + JSONObject jsonData = this.buildLoginData(user,null,null); + return ResponseResult.success(jsonData); + } catch (Exception e) { + e.printStackTrace(); + return ResponseResult.error("500", "自动登录失败"); + } + } + + /** + * 九州通移动钉钉登录 + * + * @param ticketUrl + * @param ticket + * @param request + * @param response + * @return + * @throws Exception + */ + @GetMapping("/doLoginValidateDingTalkmJzt") + @NoAuthInterface + public ResponseResult doLoginValidateDingTalkmJzt(@RequestParam(name = "ticketUrl") String ticketUrl, + @RequestParam(name = "ticket") String ticket, + @RequestParam(name = "appId") String appId, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + log.info("Rest api login."); + try { + + if(StringUtils.isBlank(ticket)){ + return ResponseResult.error("500","ticket不能为空"); + } + String jsonInputString = String.format("{\"ticket\":\"%s\",\"appId\":\"%s\",\"checkType\":1}",ticket,appId); + log.info("doLoginValidateDingTalkmJzt ", jsonInputString); + String userStr = HttpUtil.post(ticketUrl, jsonInputString); + if(StringUtils.isBlank(userStr)){ + return ResponseResult.error("500","ticket异常:"+ticket); + } + log.info(" get user result:", userStr); + JSONObject userObj = JSONObject.parseObject(userStr); + if (!"0".equals(userObj.getString("code"))) { + return ResponseResult.error("500", userObj.getString("msg")); + } + //通过userId 获取系统用户 + String userId = userObj.getJSONObject("data").getString("username"); + SysUser user = sysUserService.getUserByDingTalkOpenId(userId); + if (user == null) { + return ResponseResult.error("500", "该账号暂未绑定系统用户"); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, "登录失败,用户账号被锁定!"); + } + JSONObject jsonData = this.buildMobileLoginData(user); + return ResponseResult.success(jsonData); + } catch (Exception e) { + e.printStackTrace(); + return ResponseResult.error("500", "自动登录失败:"+e.getMessage()); + } + } + + /** + * 移动钉钉登录 + * + * @param code + * @param appKey + * @param request + * @param response + * @return + * @throws Exception + */ + @GetMapping("/doLoginValidateDingTalkm") + @NoAuthInterface + public ResponseResult doLoginValidateDingTalkm(@RequestParam(name = "code") String code, + @RequestParam(name = "appKey") String appKey, + @RequestParam(name = "userName") String userName, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + log.info("Rest api login."); + try { +// Object tokenObj = redissonClient.getBucket(appKey).get(); +// String accessToken=""; +// if (tokenObj != null) { +// accessToken=tokenObj.toString(); +// }else { +// String tokenUrl = "https://api.dingtalk.com/v1.0/oauth2/accessToken"; +// String jsonInputString = String.format( +// "{\"appKey\":\"%s\", \"appSecret\":\"%s\"}", +// appKey, dingTalkSecret); +// // 发送 POST 请求 +// String tokenData = HttpUtil.post(tokenUrl, jsonInputString); +// if (StringUtils.isBlank(tokenData)){ +// return ResponseResult.error("500", "通过appKey获取AccessToken失败,appKey:" + appKey); +// } +// JSONObject tokenJson = JSONObject.parseObject(tokenData); +// accessToken = tokenJson.get("accessToken").toString(); +// //accessToken的有效时间为7200秒,在该有效期内多次获取均返回同一token, +// //连许多次请求会被拦截,因此需要本地缓存。为防止极端情况,设置过期时间为6200s +// redissonClient.getBucket(appKey).set(accessToken,7000,TimeUnit.SECONDS); +// } +// String jsonInputString = String.format("{\"code\":\"%s\"}",code); +// String infoUrl="https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token="+accessToken; +// String userStr = HttpUtil.post(infoUrl, jsonInputString); +// log.info("validateHubCloudLogin appKey:{}, code:{} ", appKey, code); +// JSONObject userObj = JSONObject.parseObject(userStr); +// // if (!userObj.getBoolean("success")) { +// // return ResponseResult.error("500", userObj.getString("message")); +// // } +// log.info(" get user result:", userObj); +// //通过userId 获取系统用户 +// String userId = userObj.getJSONObject("result").getString("userid"); + + String userId = userName; + SysUser user = sysUserService.getUserByDingTalkOpenId(userId); + if (user == null) { + return ResponseResult.error("500", "该账号暂未绑定系统用户"); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, "登录失败,用户账号被锁定!"); + } + JSONObject jsonData = this.buildMobileLoginData(user); + return ResponseResult.success(jsonData); + } catch (Exception e) { + e.printStackTrace(); + return ResponseResult.error("500", "自动登录失败"); + } + } + + + /** + * 清除非Session的缓存。 + * + * @return 应答结果对象。 + */ + @PostMapping("/cleanRedis") + public ResponseResult cleanRedis() { + RKeys rKeys = redissonClient.getKeys(); + // 使用SCAN分批次遍历所有key(每批100个,避免内存溢出) + Iterable allKeys = rKeys.getKeysByPattern("*", 100); + List keysToDelete = new ArrayList<>(100); + + for (String key : allKeys) { + if (!key.contains("SESSION")) { + keysToDelete.add(key); + // 每满100个key触发一次异步非阻塞删除 + if (keysToDelete.size() == 100) { + rKeys.unlinkAsync(keysToDelete.toArray(new String[0])); + keysToDelete.clear(); + } + } + } + + // 删除最后一批未满100的key + if (!keysToDelete.isEmpty()) { + rKeys.unlinkAsync(keysToDelete.toArray(new String[0])); + } + + return ResponseResult.success(); + } + + private ResponseResult verifyAndHandleLoginUser( + String loginName, String password) throws UnsupportedEncodingException { + String errorMessage; + SysUser user = sysUserService.getSysUserByLoginName(loginName); + password = URLDecoder.decode(password, StandardCharsets.UTF_8.name()); + // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。 + // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。 + password = RsaUtil.decrypt(password, ApplicationConstant.PRIVATE_KEY); + if (user == null || !passwordEncoder.matches(password, user.getPassword())) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + errorMessage = "登录失败,用户账号被锁定!"; + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage); + } + if (BooleanUtil.isTrue(appConfig.getExcludeLogin())) { + String patternKey = RedisKeyUtil.getSessionIdPrefix(user.getLoginName(), MyCommonUtil.getDeviceType()) + "*"; + redissonClient.getKeys().deleteByPatternAsync(patternKey); + } + return ResponseResult.success(user); + } + + private ResponseResult verifyAndHandleLoginUser( + String loginName, String appKey, String appSecurity) throws UnsupportedEncodingException { + String errorMessage; + SysUser user = sysUserService.getSysUserByLoginName(loginName); +// password = URLDecoder.decode(password, StandardCharsets.UTF_8.name()); +// // NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。 +// // 执行RsaUtil工具类中的main函数,可以生成新的公钥和私钥。 +// password = RsaUtil.decrypt(password, ApplicationConstant.PRIVATE_KEY); + if (user == null) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD); + } + if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) { + errorMessage = "登录失败,用户账号被锁定!"; + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage); + } + Filter filter = new Filter(); + filter.add(new FilterItem("number", FilterItem.equals, appKey)); + ObjectCollection collection = ormGenDataSourceUtil.query(THIRD_PARTY_CONFIG, filter, null); + if (collection.isEmpty()) { + return ResponseResult.error(ErrorCodeEnum.INVALID_TENANT_CODE); + } else { + ObjectValue obj = collection.getObject(0); + if (!obj.getString("token_key").equals(appSecurity)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_ACCESS_TOKEN); + } + if (obj.getLong("apply_user") != user.getUserId()) { + return ResponseResult.error(ErrorCodeEnum.INVALID_USER_TENANT); + } + if (obj.getBoolean("is_enable") == false) { + return ResponseResult.error(ErrorCodeEnum.INVALID_TENANT_STATUS); + } + } + if (BooleanUtil.isTrue(appConfig.getExcludeLogin())) { + String patternKey = RedisKeyUtil.getSessionIdPrefix(user.getLoginName(), MyCommonUtil.getDeviceType()) + "*"; + redissonClient.getKeys().deleteByPatternAsync(patternKey); + } + return ResponseResult.success(user); + } + + private JSONObject buildLoginData(SysUser user, String appKey, String appSecurity) { + int deviceType = MyCommonUtil.getDeviceType(); + boolean isAdmin = user.getUserType() == SysUserType.TYPE_ADMIN; + String sessionId = user.getLoginName() + "_" + deviceType + "_" + MyCommonUtil.generateUuid(); + JSONObject jsonData = this.createResponseData(user, sessionId); + TokenData tokenData = this.buildTokenData(user, sessionId, deviceType, appKey, appSecurity); + this.putTokenDataToSessionCache(tokenData); + // 这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志。 + TokenData.addToRequest(tokenData); + Collection allMenuList; + Collection permCodeList; + if (isAdmin) { + allMenuList = sysMenuService.getAllListByOrder(SHOW_ORDER_FIELD); + permCodeList = sysPermCodeService.getAllPermCodeList(); + } else { + allMenuList = sysMenuService.getMenuListByUserId(user.getUserId()); + permCodeList = sysPermCodeService.getPermCodeListByUserId(user.getUserId()); + } + List menuList = allMenuList.stream() + .filter(m -> m.getMenuType() <= SysMenuType.TYPE_MENU).collect(Collectors.toList()); + jsonData.put("menuList", menuList); + jsonData.put("permCodeList", permCodeList); + Set permSet = null; + if (!isAdmin) { + // 所有登录用户都有白名单接口的访问权限。 + List whitelist = this.getAndCacheWhitelist(sessionId); + permSet = new HashSet<>(whitelist); + if (StrUtil.isNotBlank(tokenData.getRoleIds())) { + List roleIds = StrUtil.split(tokenData.getRoleIds(), ',') + .stream().map(Long::valueOf).collect(Collectors.toList()); + Set menuPermSet = this.getAndCacheMenuPermData(allMenuList, roleIds); + permSet.addAll(menuPermSet); + } + } + OnlinePermData onlinePermData = this.getAndCacheOnlineMenuPermData(allMenuList, false); + permCodeList.addAll(onlinePermData.permCodeSet); + OnlinePermData onlineFlowPermData = this.getAndCacheFlowOnlineMenuPermData(allMenuList, false); + permCodeList.addAll(onlineFlowPermData.permCodeSet); + if (!isAdmin) { + permSet.addAll(onlinePermData.permUrlSet); + permSet.addAll(onlineFlowPermData.permUrlSet); + List reportMenuList = allMenuList.stream() + .filter(m -> m.getReportPageId() != null).collect(Collectors.toList()); + Set reportPermSet = this.getAndCacheReportMenuPermData(reportMenuList); + permSet.addAll(reportPermSet); + // 缓存用户的权限资源 + sysPermService.putUserSysPermCache(sessionId, user.getUserId(), permSet); + sysDataPermService.putDataPermCache(sessionId, user.getUserId(), user.getDeptId()); + } + return jsonData; + } + + + private String getShrRole(String zwcjName){ + String roleId = ""; + if(!StringUtils.isEmpty(zwcjName)){ + SysRole role = new SysRole(); + if(!zwcjName.equals("员工")){//非员工 + role.setRoleName("非员工"); + List roleList = sysRoleService.getSysRoleList(role,""); + if(roleList.size() > 0){ + roleId = roleId + roleList.get(0).getRoleId(); + } + } + role.setRoleName(zwcjName); + List roleList = sysRoleService.getSysRoleList(role,""); + if(roleList.size() > 0){ + if(StringUtils.isEmpty(roleId)){ + roleId = roleList.get(0).getRoleId()+""; + }else{ + roleId = roleId +","+roleList.get(0).getRoleId(); + } + } + } + return roleId; + } + + private JSONObject buildMobileLoginData(SysUser user) { + int deviceType = MyCommonUtil.getDeviceType(); + boolean isAdmin = user.getUserType() == SysUserType.TYPE_ADMIN; + String sessionId = user.getLoginName() + "_" + deviceType + "_" + MyCommonUtil.generateUuid(); + JSONObject jsonData = this.createResponseData(user, sessionId); + TokenData tokenData = this.buildTokenData(user, sessionId, deviceType); + String zwcjName = null; + try { + String result = (String)EasUtil.executeOsfServiceWs("com.kingdee.shr.custom.plat.osf.service.GetPersonalDataService" + ,user.getLoginName(),new HashMap()); + JSONObject personData = JSONObject.parseObject(result); + tokenData.setPersonId((String) personData.get("personId")); + jsonData.put("personData", JSONObject.parseObject(result)); + zwcjName = (String)personData.get("zwcjName"); + }catch(Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + this.putTokenDataToSessionCache(tokenData); + // 这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志。 + TokenData.addToRequest(tokenData); + List mobileEntryList; + String roleId = getShrRole(zwcjName); + if(!StringUtils.isEmpty(roleId)){ + mobileEntryList = mobileEntryService.getMobileEntryListByRoleIds(roleId); + }else{ + if (isAdmin) { + mobileEntryList = mobileEntryService.getAllListByOrder(SHOW_ORDER_FIELD); + } else { + mobileEntryList = mobileEntryService.getMobileEntryListByRoleIds(tokenData.getRoleIds()); + } + } + + jsonData.put("mobileEntryList", mobileEntryList); + Set permSet = new HashSet<>(); + if (!isAdmin) { + // 所有登录用户都有白名单接口的访问权限。 + List whitelist = this.getAndCacheWhitelist(sessionId); + permSet.addAll(whitelist); + } + mobileEntryList.stream().filter(m -> m.getExtraData() != null) + .forEach(m -> m.setExtraObject(JSON.parseObject(m.getExtraData(), MobileEntryExtraData.class))); + + for (MobileEntry mobileEntry : mobileEntryList) { + if (mobileEntry.getPcurl() != null) { + mobileEntry.setPcurl(EasUtil.getEasSsoUrl(user.getLoginName(), mobileEntry.getPcurl())); + } + if(mobileEntry.getExtraUrl() != null){ + if(mobileEntry.getExtraUrl().contains("?")){ + mobileEntry.setExtraUrl(mobileEntry.getExtraUrl()+"&userNumber="+user.getLoginName()); + }else{ + mobileEntry.setExtraUrl(mobileEntry.getExtraUrl()+"?userNumber="+user.getLoginName()); + } + } + } + + OnlinePermData onlinePermData = this.getAndCacheOnlineMobileEntryPermData(mobileEntryList); + OnlinePermData onlineFlowPermData = this.getAndCacheFlowOnlineMobileEntryPermData(mobileEntryList); + if (!isAdmin) { + permSet.addAll(onlinePermData.permUrlSet); + permSet.addAll(onlineFlowPermData.permUrlSet); + Set reportPermSet = this.getAndCacheReportMobileEntryPermData(mobileEntryList); + permSet.addAll(reportPermSet); + // 缓存用户的权限资源 + sysPermService.putUserSysPermCache(sessionId, user.getUserId(), permSet); + sysDataPermService.putDataPermCache(sessionId, user.getUserId(), user.getDeptId()); + } + + + return jsonData; + } + + + private JSONObject createResponseData(SysUser user, String sessionId) { + Map claims = new HashMap<>(3); + claims.put(SESSION_ID, sessionId); + String token = JwtUtil.generateToken(claims, appConfig.getExpiration(), appConfig.getTokenSigningKey()); + JSONObject jsonData = new JSONObject(); + jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token); + jsonData.put(SHOW_NAME_FIELD, user.getShowName()); + jsonData.put(IS_ADMIN, user.getUserType() == SysUserType.TYPE_ADMIN); + if (user.getDeptId() != null) { + SysDept dept = sysDeptService.getById(user.getDeptId()); + if(dept != null){ + jsonData.put("deptName", dept.getDeptName()); + } + + } + if (StrUtil.isNotBlank(user.getHeadImageUrl())) { + jsonData.put(HEAD_IMAGE_URL_FIELD, user.getHeadImageUrl()); + } + return jsonData; + } + + private TokenData buildTokenData(SysUser user, String sessionId, int deviceType) { + TokenData tokenData = new TokenData(); + tokenData.setSessionId(sessionId); + tokenData.setUserId(user.getUserId()); + tokenData.setDeptId(user.getDeptId()); + tokenData.setLoginName(user.getLoginName()); + tokenData.setShowName(user.getShowName()); + tokenData.setIsAdmin(user.getUserType().equals(SysUserType.TYPE_ADMIN)); + tokenData.setLoginIp(IpUtil.getRemoteIpAddress(ContextUtil.getHttpRequest())); + tokenData.setLoginTime(new Date()); + int a = 1; + tokenData.setDeviceType(deviceType); + tokenData.setHeadImageUrl(user.getHeadImageUrl()); + List userPostList = sysPostService.getSysUserPostListByUserId(user.getUserId()); + if (CollUtil.isNotEmpty(userPostList)) { + Set deptPostIdSet = userPostList.stream().map(SysUserPost::getDeptPostId).collect(Collectors.toSet()); + tokenData.setDeptPostIds(StrUtil.join(",", deptPostIdSet)); + Set postIdSet = userPostList.stream().map(SysUserPost::getPostId).collect(Collectors.toSet()); + tokenData.setPostIds(StrUtil.join(",", postIdSet)); + } + List userRoleList = sysRoleService.getSysUserRoleListByUserId(user.getUserId()); + if (CollUtil.isNotEmpty(userRoleList)) { + Set userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet()); + tokenData.setRoleIds(StrUtil.join(",", userRoleIdSet)); + } + return tokenData; + } + + private TokenData buildTokenData(SysUser user, String sessionId, int deviceType, String appKey, String appSecurity) { + TokenData tokenData = new TokenData(); + tokenData.setSessionId(sessionId); + tokenData.setUserId(user.getUserId()); + tokenData.setDeptId(user.getDeptId()); + tokenData.setLoginName(user.getLoginName()); + tokenData.setShowName(user.getShowName()); + tokenData.setIsAdmin(user.getUserType().equals(SysUserType.TYPE_ADMIN)); + tokenData.setLoginIp(IpUtil.getRemoteIpAddress(ContextUtil.getHttpRequest())); + tokenData.setLoginTime(new Date()); + int a = 1; + tokenData.setDeviceType(deviceType); + tokenData.setHeadImageUrl(user.getHeadImageUrl()); + tokenData.setAppSecurity(appSecurity); + tokenData.setAppKey(appKey); + List userPostList = sysPostService.getSysUserPostListByUserId(user.getUserId()); + if (CollUtil.isNotEmpty(userPostList)) { + Set deptPostIdSet = userPostList.stream().map(SysUserPost::getDeptPostId).collect(Collectors.toSet()); + tokenData.setDeptPostIds(StrUtil.join(",", deptPostIdSet)); + Set postIdSet = userPostList.stream().map(SysUserPost::getPostId).collect(Collectors.toSet()); + tokenData.setPostIds(StrUtil.join(",", postIdSet)); + } + List userRoleList = sysRoleService.getSysUserRoleListByUserId(user.getUserId()); + if (CollUtil.isNotEmpty(userRoleList)) { + Set userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet()); + tokenData.setRoleIds(StrUtil.join(",", userRoleIdSet)); + } + return tokenData; + } + + private void putTokenDataToSessionCache(TokenData tokenData) { + String sessionIdKey = RedisKeyUtil.makeSessionIdKey(tokenData.getSessionId()); + String sessionData = JSON.toJSONString(tokenData, SerializerFeature.WriteNonStringValueAsString); + RBucket bucket = redissonClient.getBucket(sessionIdKey); + bucket.set(sessionData); + bucket.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + + private void putMenuPermToCache(String sessionId, SysMenuPerm menuPerm) { + if (BooleanUtil.isTrue(dataFilterProperties.getEnableMenuPermVerify())) { + String menuPermKey = RedisKeyUtil.makeSessionMenuPermKey(sessionId, menuPerm.getMenuId()); + RBucket cachedData = redissonClient.getBucket(menuPermKey); + cachedData.set(JSON.toJSONString(menuPerm.getPermUrlSet())); + cachedData.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + } + + private List getAndCacheWhitelist(String sessionId) { + List whitelistUrls = sysPermWhitelistService.getWhitelistPermList(); + if (BooleanUtil.isTrue(dataFilterProperties.getEnableMenuPermVerify())) { + // 白名单的数据缓存主要用户数据权限过滤是,如果菜单关联的权限中并不包含当前请求的url,则需要判断是否为白名单url。 + RBucket cachedData = redissonClient.getBucket(RedisKeyUtil.makeSessionWhiteListPermKey(sessionId)); + cachedData.set(JSON.toJSONString(whitelistUrls)); + cachedData.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + return whitelistUrls; + } + + private Set getAndCacheMenuPermData(Collection allMenuList, List roleIds) { + List allMenuPermList = MyModelUtil.copyCollectionTo(allMenuList, SysMenuPerm.class); + allMenuPermList = allMenuPermList.stream() + .filter(m -> m.getMenuType() != SysMenuType.TYPE_DIRECTORY).collect(Collectors.toList()); + Map allMenuPermMap = + allMenuPermList.stream().collect(Collectors.toMap(SysMenuPerm::getMenuId, m -> m)); + List> menuPermDataList = sysMenuService.getMenuAndPermListByRoleIds(roleIds); + // 将查询出的菜单权限数据,挂接到完整的菜单树上。 + for (Map menuPermData : menuPermDataList) { + Long menuId = (Long) menuPermData.get("menuId"); + SysMenuPerm menuPerm = allMenuPermMap.get(menuId); + menuPerm.getPermUrlSet().add(menuPermData.get("url").toString()); + } + // 根据菜单的上下级关联关系,将菜单列表还原为菜单树。 + List> menuTreeList = + TreeNode.build(allMenuPermList, SysMenuPerm::getMenuId, SysMenuPerm::getParentId, null); + Set permSet = new HashSet<>(); + String sessionId = TokenData.takeFromRequest().getSessionId(); + // 递归菜单树上每个菜单节点,将子菜单关联的所有permUrlSet,都合并到一级菜单的permUrlSet中。 + for (TreeNode treeNode : menuTreeList) { + this.buildAllSubMenuPermUrlSet(treeNode.getChildList(), treeNode.getData().getPermUrlSet()); + permSet.addAll(treeNode.getData().getPermUrlSet()); + this.putMenuPermToCache(sessionId, treeNode.getData()); + } + return permSet; + } + + private void buildAllSubMenuPermUrlSet(List> subList, Set rootPermUrlSet) { + for (TreeNode treeNode : subList) { + rootPermUrlSet.addAll(treeNode.getData().getPermUrlSet()); + if (CollUtil.isNotEmpty(treeNode.getChildList())) { + this.buildAllSubMenuPermUrlSet(treeNode.getChildList(), rootPermUrlSet); + } + } + } + + private Set getAndCacheReportMobileEntryPermData(Collection mobileEntryList) { + Set permSet = new HashSet<>(); + List reportMobileEntryList = mobileEntryList.stream() + .filter(m -> m.getExtraObject().getReportPageId() != null).collect(Collectors.toList()); + if (CollUtil.isEmpty(reportMobileEntryList)) { + return permSet; + } + String sessionId = TokenData.takeFromRequest().getSessionId(); + Map> reportMobileEntrymMap = + reportMobileEntryList.stream().collect(Collectors.groupingBy(m -> m.getExtraObject().getReportPageId())); + Set pageIdSet = reportMobileEntrymMap.keySet(); + Map> reportPermDataMap = reportOperationService.calculatePermData(pageIdSet); + for (Long pageId : pageIdSet) { + Set pagePermUrlSet = reportPermDataMap.get(pageId); + permSet.addAll(pagePermUrlSet); + for (MobileEntry mobileEntry : reportMobileEntrymMap.get(pageId)) { + this.putMobilePermToCache(sessionId, mobileEntry.getEntryId(), pagePermUrlSet); + } + } + return permSet; + } + + private OnlinePermData getAndCacheOnlineMobileEntryPermData(Collection mobileEntryList) { + List onlineMobileEntryList = mobileEntryList.stream() + .filter(m -> m.getExtraData() != null + && m.getExtraObject().getOnlineFormId() != null + && m.getExtraObject().getOnlineFlowEntryId() == null) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(onlineMobileEntryList)) { + return new OnlinePermData(); + } + Map> onlineMobileEntryMap = + onlineMobileEntryList.stream().collect(Collectors.groupingBy(m -> m.getExtraObject().getOnlineFormId())); + Set onlineFormIds = onlineMobileEntryMap.keySet(); + Map permDataMap = + onlineOperationService.calculatePermData(onlineFormIds, onlineFormIds, onlineFormIds); + OnlinePermData permData = BeanUtil.mapToBean(permDataMap, OnlinePermData.class, false, null); + String sessionId = TokenData.takeFromRequest().getSessionId(); + for (Map.Entry> entry : onlineMobileEntryMap.entrySet()) { + Set formPermUrlSet = permData.formMenuPermMap.get(entry.getKey()); + formPermUrlSet.addAll(permData.onlineWhitelistUrls); + for (MobileEntry mobileEntry : entry.getValue()) { + this.putMobilePermToCache(sessionId, mobileEntry.getEntryId(), formPermUrlSet); + } + } + permData.permUrlSet.addAll(permData.onlineWhitelistUrls); + return permData; + } + + private OnlinePermData getAndCacheFlowOnlineMobileEntryPermData(Collection mobileEntryList) { + List flowOnlineMobileEntryList = mobileEntryList.stream() + .filter(m -> m.getExtraData() != null && m.getExtraObject().getOnlineFlowEntryId() != null) + .collect(Collectors.toList()); + Set flowEntryIds = flowOnlineMobileEntryList.stream() + .map(m -> m.getExtraObject().getOnlineFlowEntryId()).collect(Collectors.toSet()); + List> flowPermDataList = flowOnlineOperationService.calculatePermData(flowEntryIds); + List flowOnlinePermDataList = + MyModelUtil.mapToBeanList(flowPermDataList, OnlineFlowPermData.class); + Map flowOnlinePermDataMap = + flowOnlinePermDataList.stream().collect(Collectors.toMap(OnlineFlowPermData::getEntryId, c -> c)); + OnlinePermData permData = new OnlinePermData(); + flowOnlinePermDataList.forEach( + onlineFlowPermData -> permData.permUrlSet.addAll(onlineFlowPermData.getPermList())); + String sessionId = TokenData.takeFromRequest().getSessionId(); + Map> flowOnlineMobileEntryMap = + flowOnlineMobileEntryList.stream().collect(Collectors.groupingBy(m -> m.getExtraObject().getOnlineFlowEntryId())); + for (Map.Entry> entry : flowOnlineMobileEntryMap.entrySet()) { + List urls = flowOnlinePermDataMap.get(entry.getKey()).getPermList(); + for (MobileEntry mobileEntry : entry.getValue()) { + this.putMobilePermToCache(sessionId, mobileEntry.getEntryId(), urls); + } + } + return permData; + } + + private void putMobilePermToCache(String sessionId, Long id, Collection urls) { + if (BooleanUtil.isTrue(dataFilterProperties.getEnableMenuPermVerify())) { + String key = RedisKeyUtil.makeSessionMenuPermKey(sessionId, id); + RBucket cachedData = redissonClient.getBucket(key); + cachedData.set(JSON.toJSONString(urls)); + cachedData.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + } + + private Set getAndCacheReportMenuPermData(List reportMenuList) { + Set permSet = new HashSet<>(); + if (CollUtil.isEmpty(reportMenuList)) { + return permSet; + } + String sessionId = TokenData.takeFromRequest().getSessionId(); + List menuPermList = + MyModelUtil.copyCollectionTo(reportMenuList, SysMenuPerm.class); + Map> menuPermMap = + menuPermList.stream().collect(Collectors.groupingBy(SysMenuPerm::getReportPageId)); + Set pageIdSet = reportMenuList.stream().map(SysMenu::getReportPageId).collect(Collectors.toSet()); + Map> reportPermDataMap = reportOperationService.calculatePermData(pageIdSet); + for (Long pageId : pageIdSet) { + Set pagePermUrlSet = reportPermDataMap.get(pageId); + List subMenuPermList = menuPermMap.get(pageId); + for (SysMenuPerm menuPerm : subMenuPermList) { + menuPerm.getPermUrlSet().addAll(pagePermUrlSet); + this.putMenuPermToCache(sessionId, menuPerm); + } + permSet.addAll(pagePermUrlSet); + } + return permSet; + } + + private OnlinePermData getAndCacheOnlineMenuPermData(Collection allMenuList, boolean permCodeOnly) { + List onlineMenuList = allMenuList.stream() + .filter(m -> m.getOnlineFormId() != null && m.getMenuType().equals(SysMenuType.TYPE_BUTTON)) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(onlineMenuList)) { + return new OnlinePermData(); + } + List allMenuPermList = MyModelUtil.copyCollectionTo(allMenuList, SysMenuPerm.class); + List onlineMenuPermList = allMenuPermList.stream() + .filter(m -> m.getOnlineFormId() != null + // && m.getOnlineFlowEntryId() == null + && m.getMenuType().equals(SysMenuType.TYPE_MENU)) + .collect(Collectors.toList()); + Map> onlineMenuPermMap = + onlineMenuPermList.stream().collect(Collectors.groupingBy(SysMenuPerm::getOnlineFormId)); + Set viewFormIds = onlineMenuList.stream() + .filter(m -> m.getOnlineMenuPermType() == SysOnlineMenuPermType.TYPE_VIEW) + .map(SysMenu::getOnlineFormId) + .collect(Collectors.toSet()); + Set editFormIds = onlineMenuList.stream() + .filter(m -> m.getOnlineMenuPermType() == SysOnlineMenuPermType.TYPE_EDIT) + .map(SysMenu::getOnlineFormId) + .collect(Collectors.toSet()); + Map permDataMap = + onlineOperationService.calculatePermData(onlineMenuPermMap.keySet(), viewFormIds, editFormIds); + OnlinePermData permData = BeanUtil.mapToBean(permDataMap, OnlinePermData.class, false, null); + for (Map.Entry> entry : onlineMenuPermMap.entrySet()) { + Set formPermUrlSet = permData.formMenuPermMap.get(entry.getKey()); + for (SysMenuPerm menuPerm : entry.getValue()) { + menuPerm.getPermUrlSet().addAll(formPermUrlSet); + } + } + if (permCodeOnly) { + return permData; + } + // 这个非常非常重要,不能删除。因为在线表单的url前缀是可以配置的,那么表单字典接口的url也是动态。 + // 所以就不能把这个字典列表接口放到数据库的白名单表中。 + String sessionId = TokenData.takeFromRequest().getSessionId(); + for (SysMenuPerm menuPerm : onlineMenuPermList) { + menuPerm.getPermUrlSet().addAll(permData.onlineWhitelistUrls); + this.putMenuPermToCache(sessionId, menuPerm); + } + permData.permUrlSet.addAll(permData.onlineWhitelistUrls); + return permData; + } + + private OnlinePermData getAndCacheFlowOnlineMenuPermData(Collection allMenuList, boolean permCodeOnly) { + List onlineFlowMenuList = allMenuList.stream() + .filter(m -> m.getOnlineFlowEntryId() != null).collect(Collectors.toList()); + Set entryIdSet = onlineFlowMenuList.stream() + .map(SysMenu::getOnlineFlowEntryId).collect(Collectors.toSet()); + List> flowPermDataList = flowOnlineOperationService.calculatePermData(entryIdSet); + List onlineFlowPermDataList = + MyModelUtil.mapToBeanList(flowPermDataList, OnlineFlowPermData.class); + OnlinePermData permData = new OnlinePermData(); + onlineFlowPermDataList.forEach(onlineFlowPermData -> { + permData.permCodeSet.addAll(onlineFlowPermData.getPermCodeList()); + permData.permUrlSet.addAll(onlineFlowPermData.getPermList()); + }); + if (permCodeOnly) { + return permData; + } + List onlineFlowMenuPermList = MyModelUtil.copyCollectionTo(onlineFlowMenuList, SysMenuPerm.class); + Map> onlineFlowMenuPermMap = + onlineFlowMenuPermList.stream().collect(Collectors.groupingBy(SysMenuPerm::getOnlineFlowEntryId)); + for (OnlineFlowPermData data : onlineFlowPermDataList) { + List menuPermList = onlineFlowMenuPermMap.get(data.getEntryId()); + for (SysMenuPerm menuPerm : menuPermList) { + menuPerm.getPermUrlSet().addAll(data.getPermList()); + } + } + for (SysMenuPerm menuPerm : onlineFlowMenuPermList) { + this.putMenuPermToCache(TokenData.takeFromRequest().getSessionId(), menuPerm); + } + return permData; + } + + static class OnlinePermData { + public final Set permCodeSet = new HashSet<>(); + public final Set permUrlSet = new HashSet<>(); + public final Map> formMenuPermMap = new HashMap<>(); + public final List onlineWhitelistUrls = new LinkedList<>(); + } + + @Data + static class OnlineFlowPermData { + private Long entryId; + private List permCodeList; + private List permList; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginUserController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginUserController.java new file mode 100644 index 0000000..1ea25e5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginUserController.java @@ -0,0 +1,83 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.object.*; +import apelet.common.core.util.RedisKeyUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RBucket; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.LinkedList; +import java.util.List; + +/** + * 在线用户控制器对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "在线用户接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/loginUser") +public class LoginUserController { + + @Autowired + private RedissonClient redissonClient; + + /** + * 显示在线用户列表。 + * + * @param loginName 登录名过滤。 + * @param pageParam 分页参数。 + * @return 登录用户信息列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody String loginName, @MyRequestBody MyPageParam pageParam) { + int skipCount = (pageParam.getPageNum() - 1) * pageParam.getPageSize(); + String patternKey; + if (StrUtil.isBlank(loginName)) { + patternKey = RedisKeyUtil.getSessionIdPrefix() + "*"; + } else { + patternKey = RedisKeyUtil.getSessionIdPrefix(loginName) + "*"; + } + List loginUserInfoList = new LinkedList<>(); + Iterable keys = redissonClient.getKeys().getKeysByPattern(patternKey); + for (String key : keys) { + loginUserInfoList.add(this.buildTokenDataByRedisKey(key)); + } + loginUserInfoList.sort((o1, o2) -> (int) (o2.getLoginTime().getTime() - o1.getLoginTime().getTime())); + int toIndex = Math.min(skipCount + pageParam.getPageSize(), loginUserInfoList.size()); + List resultList = loginUserInfoList.subList(skipCount, toIndex); + return ResponseResult.success(new MyPageData<>(resultList, (long) loginUserInfoList.size())); + } + + /** + * 强制下线指定登录会话。 + * + * @param sessionId 待强制下线的SessionId。 + * @return 应答结果对象。 + */ + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody String sessionId) { + // 为了保证被剔除用户正在进行的操作不被干扰,这里只是删除sessionIdKey即可,这样可以使强制下线操作更加平滑。 + // 比如,如果删除操作权限或数据权限的redis session key,那么正在请求数据的操作就会报错。 + redissonClient.getBucket(RedisKeyUtil.makeSessionIdKey(sessionId)).delete(); + return ResponseResult.success(); + } + + private LoginUserInfo buildTokenDataByRedisKey(String key) { + RBucket sessionData = redissonClient.getBucket(key); + TokenData tokenData = JSON.parseObject(sessionData.get(), TokenData.class); + return BeanUtil.copyProperties(tokenData, LoginUserInfo.class); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDataPermController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDataPermController.java new file mode 100644 index 0000000..592ae3d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDataPermController.java @@ -0,0 +1,330 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysDataPermDto; +import apelet.tenantadmin.upms.dto.SysUserDto; +import apelet.tenantadmin.upms.model.SysDataPerm; +import apelet.tenantadmin.upms.model.SysUser; +import apelet.tenantadmin.upms.service.SysDataPermService; +import apelet.tenantadmin.upms.service.SysUserService; +import apelet.tenantadmin.upms.vo.SysDataPermVo; +import apelet.tenantadmin.upms.vo.SysUserVo; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.TypeReference; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 数据权限接口控制器对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "数据权限管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysDataPerm") +public class SysDataPermController { + + @Autowired + private SysDataPermService sysDataPermService; + @Autowired + private SysUserService sysUserService; + + /** + * 添加新数据权限操作。 + * + * @param sysDataPermDto 新增对象。 + * @param deptIdListString 数据权限关联的部门Id列表,多个之间逗号分隔。 + * @param menuIdListString 数据权限关联的菜单Id列表,多个之间逗号分隔。 + * @param entryIdListString 数据权限关联的移动端入口Id列表,多个之间逗号分隔。 + * @return 应答结果对象。包含新增数据权限对象的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = { + "sysDataPermDto.dataPermId", + "sysDataPermDto.createTimeStart", + "sysDataPermDto.createTimeEnd", + "sysDataPermDto.searchString"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysDataPermDto sysDataPermDto, + @MyRequestBody String deptIdListString, + @MyRequestBody String menuIdListString, + @MyRequestBody String entryIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysDataPermDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysDataPerm sysDataPerm = MyModelUtil.copyTo(sysDataPermDto, SysDataPerm.class); + CallResult result = sysDataPermService.verifyRelatedData(sysDataPerm, deptIdListString, menuIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set menuIdSet = null; + if (result.getData() != null) { + menuIdSet = result.getData().getObject("menuIdSet", new TypeReference>(){}); + } + Set deptIdSet = null; + if (result.getData() != null) { + deptIdSet = result.getData().getObject("deptIdSet", new TypeReference>(){}); + } + Set entryIdSet = null; + if (StrUtil.isNotBlank(entryIdListString)) { + entryIdSet = StrUtil.split(entryIdListString, ",") + .stream().map(Long::valueOf).collect(Collectors.toSet()); + } + sysDataPermService.saveNew(sysDataPerm, deptIdSet, menuIdSet, entryIdSet); + return ResponseResult.success(sysDataPerm.getDataPermId()); + } + + /** + * 更新数据权限操作。 + * + * @param sysDataPermDto 更新的数据权限对象。 + * @param deptIdListString 数据权限关联的部门Id列表,多个之间逗号分隔。 + * @param menuIdListString 数据权限关联的菜单Id列表,多个之间逗号分隔。 + * @param entryIdListString 数据权限关联的移动端入口Id列表,多个之间逗号分隔。 + * @return 应答结果对象。 + */ + @ApiOperationSupport(ignoreParameters = { + "sysDataPermDto.createTimeStart", + "sysDataPermDto.createTimeEnd", + "sysDataPermDto.searchString"}) + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysDataPermDto sysDataPermDto, + @MyRequestBody String deptIdListString, + @MyRequestBody String menuIdListString, + @MyRequestBody String entryIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysDataPermDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysDataPerm originalSysDataPerm = sysDataPermService.getById(sysDataPermDto.getDataPermId()); + if (originalSysDataPerm == null) { + errorMessage = "数据验证失败,当前数据权限并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysDataPerm sysDataPerm = MyModelUtil.copyTo(sysDataPermDto, SysDataPerm.class); + CallResult result = sysDataPermService.verifyRelatedData(sysDataPerm, deptIdListString, menuIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set deptIdSet = null; + if (result.getData() != null) { + deptIdSet = result.getData().getObject("deptIdSet", new TypeReference>(){}); + } + Set menuIdSet = null; + if (result.getData() != null) { + menuIdSet = result.getData().getObject("menuIdSet", new TypeReference>(){}); + } + Set entryIdSet = null; + if (StrUtil.isNotBlank(entryIdListString)) { + entryIdSet = StrUtil.split(entryIdListString, ",") + .stream().map(Long::valueOf).collect(Collectors.toSet()); + } + if (!sysDataPermService.update(sysDataPerm, originalSysDataPerm, deptIdSet, menuIdSet, entryIdSet)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除数据权限操作。 + * + * @param dataPermId 待删除数据权限主键Id。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long dataPermId) { + if (MyCommonUtil.existBlankArgument(dataPermId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysDataPermService.remove(dataPermId)) { + String errorMessage = "数据操作失败,数据权限不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看数据权限列表。 + * + * @param sysDataPermDtoFilter 数据权限查询过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象。包含数据权限列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysDataPermDto sysDataPermDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysDataPerm filter = MyModelUtil.copyTo(sysDataPermDtoFilter, SysDataPerm.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysDataPerm.class); + List dataPermList = sysDataPermService.getSysDataPermListWithRelation(filter, orderBy); + List dataPermVoList = MyModelUtil.copyCollectionTo(dataPermList, SysDataPermVo.class); + long totalCount = 0L; + if (dataPermList instanceof Page) { + totalCount = ((Page) dataPermList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(dataPermVoList, totalCount)); + } + + /** + * 查看单条数据权限详情。 + * + * @param dataPermId 数据权限的主键Id。 + * @return 应答结果对象,包含数据权限的详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long dataPermId) { + if (MyCommonUtil.existBlankArgument(dataPermId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysDataPerm dataPerm = sysDataPermService.getByIdWithRelation(dataPermId, MyRelationParam.full()); + if (dataPerm == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysDataPermVo dataPermVo = MyModelUtil.copyTo(dataPerm, SysDataPermVo.class); + return ResponseResult.success(dataPermVo); + } + + /** + * 获取不包含指定数据权限Id的用户列表。 + * 用户和数据权限是多对多关系,当前接口将返回没有赋值指定DataPermId的用户列表。可用于给数据权限添加新用户。 + * + * @param dataPermId 数据权限主键Id。 + * @param sysUserDtoFilter 用户数据的过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含用户列表数据。 + */ + @PostMapping("/listNotInDataPermUser") + public ResponseResult> listNotInDataPermUser( + @MyRequestBody Long dataPermId, + @MyRequestBody SysUserDto sysUserDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doDataPermUserVerify(dataPermId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysUser filter = MyModelUtil.copyTo(sysUserDtoFilter, SysUser.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class); + List userList = + sysUserService.getNotInSysUserListByDataPermId(dataPermId, filter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(userList, SysUser.INSTANCE)); + } + + /** + * 拥有指定数据权限的用户列表。 + * + * @param dataPermId 数据权限Id。 + * @param sysUserDtoFilter 用户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含用户列表数据。 + */ + @PostMapping("/listDataPermUser") + public ResponseResult> listDataPermUser( + @MyRequestBody Long dataPermId, + @MyRequestBody SysUserDto sysUserDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doDataPermUserVerify(dataPermId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysUser filter = MyModelUtil.copyTo(sysUserDtoFilter, SysUser.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class); + List userList = sysUserService.getSysUserListByDataPermId(dataPermId, filter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(userList, SysUser.INSTANCE)); + } + + private ResponseResult doDataPermUserVerify(Long dataPermId) { + if (MyCommonUtil.existBlankArgument(dataPermId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysDataPermService.existId(dataPermId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + return ResponseResult.success(); + } + + /** + * 为指定数据权限添加用户列表。该操作可同时给一批用户赋值数据权限,并在同一事务内完成。 + * + * @param dataPermId 数据权限主键Id。 + * @param userIdListString 逗号分隔的用户Id列表。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addDataPermUser") + public ResponseResult addDataPermUser( + @MyRequestBody Long dataPermId, @MyRequestBody String userIdListString) { + if (MyCommonUtil.existBlankArgument(dataPermId, userIdListString)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + Set userIdSet = + Arrays.stream(userIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysDataPermService.existId(dataPermId) + || !sysUserService.existUniqueKeyList("userId", userIdSet)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + sysDataPermService.addDataPermUserList(dataPermId, userIdSet); + return ResponseResult.success(); + } + + /** + * 为指定用户移除指定数据权限。 + * + * @param dataPermId 指定数据权限主键Id。 + * @param userId 指定用户主键Id。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteDataPermUser") + public ResponseResult deleteDataPermUser( + @MyRequestBody Long dataPermId, @MyRequestBody Long userId) { + if (MyCommonUtil.existBlankArgument(dataPermId, userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysDataPermService.removeDataPermUser(dataPermId, userId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDeptController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDeptController.java new file mode 100644 index 0000000..56ad43d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDeptController.java @@ -0,0 +1,450 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysDeptDto; +import apelet.tenantadmin.upms.dto.SysDeptPostDto; +import apelet.tenantadmin.upms.dto.SysPostDto; +import apelet.tenantadmin.upms.model.SysDept; +import apelet.tenantadmin.upms.model.SysDeptPost; +import apelet.tenantadmin.upms.model.SysPost; +import apelet.tenantadmin.upms.service.SysDeptService; +import apelet.tenantadmin.upms.service.SysPostService; +import apelet.tenantadmin.upms.vo.SysDeptPostVo; +import apelet.tenantadmin.upms.vo.SysDeptVo; +import apelet.tenantadmin.upms.vo.SysPostVo; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 部门管理操作控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "部门管理管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysDept") +public class SysDeptController { + + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysPostService sysPostService; + + /** + * 新增部门管理数据。 + * + * @param sysDeptDto 新增对象。 + * @return 应答结果对象,包含新增对象主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysDeptDto.deptId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysDeptDto sysDeptDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysDeptDto, false); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysDept sysDept = MyModelUtil.copyTo(sysDeptDto, SysDept.class); + // 验证父Id的数据合法性 + SysDept parentSysDept = null; + if (MyCommonUtil.isNotBlankOrNull(sysDept.getParentId())) { + parentSysDept = sysDeptService.getById(sysDept.getParentId()); + if (parentSysDept == null) { + errorMessage = "数据验证失败,关联的父节点并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + } + sysDept = sysDeptService.saveNew(sysDept, parentSysDept); + return ResponseResult.success(sysDept.getDeptId()); + } + + /** + * 更新部门管理数据。 + * + * @param sysDeptDto 更新对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysDeptDto sysDeptDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysDeptDto, true); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysDept sysDept = MyModelUtil.copyTo(sysDeptDto, SysDept.class); + SysDept originalSysDept = sysDeptService.getById(sysDept.getDeptId()); + if (originalSysDept == null) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [数据] 并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + // 验证父Id的数据合法性 + if (MyCommonUtil.isNotBlankOrNull(sysDept.getParentId()) + && ObjectUtil.notEqual(sysDept.getParentId(), originalSysDept.getParentId())) { + SysDept parentSysDept = sysDeptService.getById(sysDept.getParentId()); + if (parentSysDept == null) { + errorMessage = "数据验证失败,关联的父节点并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + } + if (!sysDeptService.update(sysDept, originalSysDept)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除部门管理数据。 + * + * @param deptId 删除对象主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long deptId) { + if (MyCommonUtil.existBlankArgument(deptId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return this.doDelete(deptId); + } + + /** + * 列出符合过滤条件的部门管理列表。 + * + * @param sysDeptDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含查询结果集。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysDeptDto sysDeptDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysDept sysDeptFilter = MyModelUtil.copyTo(sysDeptDtoFilter, SysDept.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysDept.class); + List sysDeptList = sysDeptService.getSysDeptListWithRelation(sysDeptFilter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(sysDeptList, SysDept.INSTANCE)); + } + + /** + * 查看指定部门管理对象详情。 + * + * @param deptId 指定对象主键Id。 + * @return 应答结果对象,包含对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long deptId) { + SysDept sysDept = sysDeptService.getByIdWithRelation(deptId, MyRelationParam.full()); + if (sysDept == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysDeptVo sysDeptVo = SysDept.INSTANCE.fromModel(sysDept); + return ResponseResult.success(sysDeptVo); + } + + /** + * 列出不与指定部门管理存在多对多关系的 [岗位管理] 列表数据。通常用于查看添加新 [岗位管理] 对象的候选列表。 + * + * @param deptId 主表关联字段。 + * @param sysPostDtoFilter [岗位管理] 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,返回符合条件的数据列表。 + */ + @PostMapping("/listNotInSysDeptPost") + public ResponseResult> listNotInSysDeptPost( + @MyRequestBody Long deptId, + @MyRequestBody SysPostDto sysPostDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (MyCommonUtil.isNotBlankOrNull(deptId) && !sysDeptService.existId(deptId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysPost filter = MyModelUtil.copyTo(sysPostDtoFilter, SysPost.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysPost.class); + List sysPostList; + if (MyCommonUtil.isNotBlankOrNull(deptId)) { + sysPostList = sysPostService.getNotInSysPostListByDeptId(deptId, filter, orderBy); + } else { + sysPostList = sysPostService.getSysPostList(filter, orderBy); + sysPostService.buildRelationForDataList(sysPostList, MyRelationParam.dictOnly()); + } + return ResponseResult.success(MyPageUtil.makeResponseData(sysPostList, SysPost.INSTANCE)); + } + + /** + * 列出与指定部门管理存在多对多关系的 [岗位管理] 列表数据。 + * + * @param deptId 主表关联字段。 + * @param sysPostDtoFilter [岗位管理] 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,返回符合条件的数据列表。 + */ + @PostMapping("/listSysDeptPost") + public ResponseResult> listSysDeptPost( + @MyRequestBody(required = true) Long deptId, + @MyRequestBody SysPostDto sysPostDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (!sysDeptService.existId(deptId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysPost filter = MyModelUtil.copyTo(sysPostDtoFilter, SysPost.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysPost.class); + List sysPostList = sysPostService.getSysPostListByDeptId(deptId, filter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(sysPostList, SysPost.INSTANCE)); + } + + /** + * 批量添加部门管理和 [岗位管理] 对象的多对多关联关系数据。 + * + * @param deptId 主表主键Id。 + * @param sysDeptPostDtoList 关联对象列表。 + * @return 应答结果对象。 + */ + @PostMapping("/addSysDeptPost") + public ResponseResult addSysDeptPost( + @MyRequestBody Long deptId, @MyRequestBody List sysDeptPostDtoList) { + if (MyCommonUtil.existBlankArgument(deptId, sysDeptPostDtoList)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage = MyCommonUtil.getModelValidationError(sysDeptPostDtoList); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + Set postIdSet = sysDeptPostDtoList.stream().map(SysDeptPostDto::getPostId).collect(Collectors.toSet()); + if (!sysDeptService.existId(deptId) || !sysPostService.existUniqueKeyList("postId", postIdSet)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + List sysDeptPostList = MyModelUtil.copyCollectionTo(sysDeptPostDtoList, SysDeptPost.class); + sysDeptService.addSysDeptPostList(sysDeptPostList, deptId); + return ResponseResult.success(); + } + + /** + * 更新指定部门管理和指定 [岗位管理] 的多对多关联数据。 + * + * @param sysDeptPostDto 对多对中间表对象。 + * @return 应答结果对象。 + */ + @PostMapping("/updateSysDeptPost") + public ResponseResult updateSysDeptPost(@MyRequestBody SysDeptPostDto sysDeptPostDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysDeptPostDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysDeptPost sysDeptPost = MyModelUtil.copyTo(sysDeptPostDto, SysDeptPost.class); + if (!sysDeptService.updateSysDeptPost(sysDeptPost)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 显示部门管理和指定 [岗位管理] 的多对多关联详情数据。 + * + * @param deptId 主表主键Id。 + * @param postId 从表主键Id。 + * @return 应答结果对象,包括中间表详情。 + */ + @GetMapping("/viewSysDeptPost") + public ResponseResult viewSysDeptPost(@RequestParam Long deptId, @RequestParam Long postId) { + SysDeptPost sysDeptPost = sysDeptService.getSysDeptPost(deptId, postId); + if (sysDeptPost == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysDeptPostVo sysDeptPostVo = MyModelUtil.copyTo(sysDeptPost, SysDeptPostVo.class); + return ResponseResult.success(sysDeptPostVo); + } + + /** + * 移除指定部门管理和指定 [岗位管理] 的多对多关联关系。 + * + * @param deptId 主表主键Id。 + * @param postId 从表主键Id。 + * @return 应答结果对象。 + */ + @PostMapping("/deleteSysDeptPost") + public ResponseResult deleteSysDeptPost(@MyRequestBody Long deptId, @MyRequestBody Long postId) { + if (MyCommonUtil.existBlankArgument(deptId, postId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysDeptService.removeSysDeptPost(deptId, postId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 获取部门岗位多对多关联数据,及其关联的部门和岗位数据。 + * + * @param deptId 部门Id,如果为空,返回全部数据列表。 + * @return 部门岗位多对多关联数据,及其关联的部门和岗位数据 + */ + @GetMapping("/listSysDeptPostWithRelation") + public ResponseResult>> listSysDeptPostWithRelation( + @RequestParam(required = false) Long deptId) { + return ResponseResult.success(sysDeptService.getSysDeptPostListWithRelationByDeptId(deptId)); + } + + /** + * 以字典形式返回全部部门管理数据集合。字典的键值为[deptId, deptName]。 + * 白名单接口,登录用户均可访问。 + * + * @param filter 过滤对象。 + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listDict") + public ResponseResult>> listDict(@ParameterObject SysDeptVo filter) { + List resultList = + sysDeptService.getListByFilter(MyModelUtil.copyTo(filter, SysDept.class)); + return ResponseResult.success(MyCommonUtil.toDictDataList( + resultList, SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId)); + } + + /** + * 根据字典Id集合,获取查询后的字典数据。 + * + * @param dictIds 字典Id集合。 + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listDictByIds") + public ResponseResult>> listDictByIds(@RequestParam List dictIds) { + List resultList = sysDeptService.getInList(new HashSet<>(dictIds)); + return ResponseResult.success(MyCommonUtil.toDictDataList( + resultList, SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId)); + } + + /** + * 根据父主键Id,以字典的形式返回其下级数据列表。 + * 白名单接口,登录用户均可访问。 + * + * @param parentId 父主键Id。 + * @return 按照字典的形式返回下级数据列表。 + */ + @GetMapping("/listDictByParentId") + public ResponseResult>> listDictByParentId(@RequestParam(required = false) Long parentId) { + List resultList = sysDeptService.getListByParentId("parentId", parentId); + return ResponseResult.success(MyCommonUtil.toDictDataList( + resultList, SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId)); + } + + /** + * 根据父主键Id列表,获取当前部门Id及其所有下级部门Id列表。 + * 白名单接口,登录用户均可访问。 + * + * @param parentIds 父主键Id列表,多个Id之间逗号分隔。 + * @return 获取当前部门Id及其所有下级部门Id列表。 + */ + @GetMapping("/listAllChildDeptIdByParentIds") + public ResponseResult> listAllChildDeptIdByParentIds( + @RequestParam(required = false) String parentIds) { + List parentIdList = StrUtil.split(parentIds, ',') + .stream().map(Long::valueOf).collect(Collectors.toList()); + return ResponseResult.success(sysDeptService.getAllChildDeptIdByParentIds(parentIdList)); + } + + /** + * 根据部门Id获取该部门领导岗位的部门岗位Id集合。目前仅用于内部服务间调用。 + * + * @param deptId 部门Id。 + * @return 部门领导岗位的部门岗位Id集合。 + */ + @GetMapping("/getLeaderDeptPostIdList") + public ResponseResult> getLeaderDeptPostIdList(@RequestParam Long deptId) { + return ResponseResult.success(sysDeptService.getLeaderDeptPostIdList(deptId)); + } + + /** + * 根据部门Id获取上级部门领导岗位的部门岗位Id集合。目前仅用于内部服务间调用。 + * + * @param deptId 部门Id。 + * @return 上级部门领导岗位的部门岗位Id集合。 + */ + @GetMapping("/getUpLeaderDeptPostIdList") + public ResponseResult> getUpLeaderDeptPostIdList(@RequestParam Long deptId) { + return ResponseResult.success(sysDeptService.getUpLeaderDeptPostIdList(deptId)); + } + + /** + * 获取指定部门Id和岗位Id集合的部门岗位多对多关联数据列表。 + * + * @param deptId 部门Id。 + * @param postIdSet 指定的岗位Id集合。 + * @return 部门岗位多对多数据列表。 + */ + @GetMapping("/getSysDeptPostList") + public ResponseResult> getSysDeptPostList(@RequestParam Long deptId, @RequestParam Set postIdSet) { + List sysDeptPostList = sysDeptService.getSysDeptPostList(deptId, postIdSet); + return ResponseResult.success(MyModelUtil.copyCollectionTo(sysDeptPostList, SysDeptPostVo.class)); + } + + /** + * 获取指定部门Id的同级兄弟部门和指定岗位Id集合的部门岗位多对多关联数据列表。 + * + * @param deptId 部门Id。 + * @param postIdSet 指定的岗位Id集合。 + * @return 部门岗位多对多数据列表。 + */ + @GetMapping("/getSiblingSysDeptPostList") + public ResponseResult> getSiblingSysDeptPostList( + @RequestParam Long deptId, @RequestParam Set postIdSet) { + List sysDeptPostList = sysDeptService.getSiblingSysDeptPostList(deptId, postIdSet); + return ResponseResult.success(MyModelUtil.copyCollectionTo(sysDeptPostList, SysDeptPostVo.class)); + } + + private ResponseResult doDelete(Long deptId) { + String errorMessage; + // 验证关联Id的数据合法性 + SysDept originalSysDept = sysDeptService.getById(deptId); + if (originalSysDept == null) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (sysDeptService.hasChildren(deptId)) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [对象存在子对象],请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (sysDeptService.hasChildrenUser(deptId)) { + errorMessage = "数据验证失败,请先移除部门用户数据后,再删除当前部门!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysDeptService.remove(deptId)) { + errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysMenuController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysMenuController.java new file mode 100644 index 0000000..b71dea5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysMenuController.java @@ -0,0 +1,266 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysMenuDto; +import apelet.tenantadmin.upms.model.SysDataPerm; +import apelet.tenantadmin.upms.model.SysMenu; +import apelet.tenantadmin.upms.model.constant.SysMenuType; +import apelet.tenantadmin.upms.service.SysDataPermService; +import apelet.tenantadmin.upms.service.SysMenuService; +import apelet.tenantadmin.upms.vo.SysMenuVo; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.TypeReference; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "菜单管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysMenu") +public class SysMenuController { + + @Autowired + private SysMenuService sysMenuService; + @Autowired + private SysDataPermService sysDataPermService; + + /** + * 添加新菜单操作。 + * + * @param sysMenuDto 新菜单对象。 + * @param permCodeIdListString 与当前菜单Id绑定的权限Id列表,多个权限之间逗号分隔。 + * @return 应答结果对象,包含新增菜单的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysMenuDto.menuId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysMenuDto sysMenuDto, @MyRequestBody String permCodeIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysMenuDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysMenu sysMenu = MyModelUtil.copyTo(sysMenuDto, SysMenu.class); + if (sysMenu.getParentId() != null) { + SysMenu parentSysMenu = sysMenuService.getById(sysMenu.getParentId()); + if (parentSysMenu == null) { + errorMessage = "数据验证失败,关联的父菜单不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (parentSysMenu.getOnlineFormId() != null) { + errorMessage = "数据验证失败,不能为动态表单菜单添加子菜单!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + } + CallResult result = sysMenuService.verifyRelatedData(sysMenu, null, permCodeIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set permCodeIdSet = null; + if (result.getData() != null) { + permCodeIdSet = result.getData().getObject("permCodeIdSet", new TypeReference>(){}); + } + sysMenuService.saveNew(sysMenu, permCodeIdSet); + return ResponseResult.success(sysMenu.getMenuId()); + } + + /** + * 更新菜单数据操作。 + * + * @param sysMenuDto 更新菜单对象。 + * @param permCodeIdListString 与当前菜单Id绑定的权限Id列表,多个权限之间逗号分隔。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysMenuDto sysMenuDto, @MyRequestBody String permCodeIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysMenuDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysMenu originalSysMenu = sysMenuService.getById(sysMenuDto.getMenuId()); + if (originalSysMenu == null) { + errorMessage = "数据验证失败,当前菜单并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysMenu sysMenu = MyModelUtil.copyTo(sysMenuDto, SysMenu.class); + if (ObjectUtil.notEqual(originalSysMenu.getOnlineFormId(), sysMenu.getOnlineFormId())) { + if (originalSysMenu.getOnlineFormId() == null) { + errorMessage = "数据验证失败,不能为当前菜单添加在线表单Id属性!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (sysMenu.getOnlineFormId() == null) { + errorMessage = "数据验证失败,不能去掉当前菜单的在线表单Id属性!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + } + if (originalSysMenu.getOnlineFormId() != null + && originalSysMenu.getMenuType().equals(SysMenuType.TYPE_BUTTON)) { + errorMessage = "数据验证失败,在线表单的内置菜单不能编辑!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + CallResult result = sysMenuService.verifyRelatedData(sysMenu, originalSysMenu, permCodeIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set permCodeIdSet = null; + if (result.getData() != null) { + permCodeIdSet = result.getData().getObject("permCodeIdSet", new TypeReference>(){}); + } + if (!sysMenuService.update(sysMenu, originalSysMenu, permCodeIdSet)) { + errorMessage = "数据验证失败,当前权限字并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定菜单操作。 + * + * @param menuId 指定菜单主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long menuId) { + if (MyCommonUtil.existBlankArgument(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + SysMenu menu = sysMenuService.getById(menuId); + if (menu == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + if (menu.getOnlineFormId() != null && menu.getMenuType().equals(SysMenuType.TYPE_BUTTON)) { + errorMessage = "数据验证失败,在线表单的内置菜单不能删除!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 对于在线表单,无需进行子菜单的验证,而是在删除的时候,连同子菜单一起删除。 + if (menu.getOnlineFormId() == null && sysMenuService.hasChildren(menuId)) { + errorMessage = "数据验证失败,当前菜单存在下级菜单!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + List dataPermList = sysDataPermService.getSysDataPermListByMenuId(menuId); + if (CollUtil.isNotEmpty(dataPermList)) { + SysDataPerm dataPerm = dataPermList.get(0); + errorMessage = "数据验证失败,当前菜单正在被数据权限 [" + dataPerm.getDataPermName() + "] 引用,不能直接删除!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (!sysMenuService.remove(menu)) { + errorMessage = "数据操作失败,菜单不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 获取全部菜单列表。 + * + * @return 应答结果对象,包含全部菜单数据列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List sysMenuList = sysMenuService.getAllListByOrder("showOrder"); + return ResponseResult.success(SysMenu.INSTANCE.fromModelList(sysMenuList)); + } + + /** + * 获取全部菜单列表。 + * + * @return 应答结果对象,包含全部菜单数据列表。 + */ + @GetMapping("/listByPageIds") + public ResponseResult> listByPageIds(@RequestParam("pageIdList") List pageIdList) { + List menuList = + sysMenuService.getMenuListByPageIds(pageIdList); + return ResponseResult.success(SysMenu.INSTANCE.fromModelList(menuList)); + } + + /** + * 查看指定菜单数据详情。 + * + * @param menuId 指定菜单主键Id。 + * @return 应答结果对象,包含菜单详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long menuId) { + if (MyCommonUtil.existBlankArgument(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysMenu sysMenu = sysMenuService.getByIdWithRelation(menuId, MyRelationParam.full()); + if (sysMenu == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysMenuVo sysMenuVo = MyModelUtil.copyTo(sysMenu, SysMenuVo.class); + return ResponseResult.success(sysMenuVo); + } + + /** + * 以字典形式返回目录和菜单类型的菜单管理数据集合。字典的键值为[menuId, menuName]。 + * 白名单接口,登录用户均可访问。 + * + * @return 应答结果对象,包含的数据为 List>,map中包含两条记录,key的值分别是id和name,value对应具体数据。 + */ + @GetMapping("/listMenuDict") + public ResponseResult>> listMenuDict() { + Collection resultList = sysMenuService.getAllListByOrder("showOrder"); + resultList = resultList.stream() + .filter(m -> m.getMenuType() <= SysMenuType.TYPE_MENU).collect(Collectors.toList()); + return ResponseResult.success( + MyCommonUtil.toDictDataList(resultList, SysMenu::getMenuId, SysMenu::getMenuName, SysMenu::getParentId)); + } + + /** + * 查询菜单的权限资源地址列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param url 权限资源地址过滤条件。 + * @return 应答对象,包含从菜单到权限资源的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysPermWithDetail") + public ResponseResult>> listSysPermWithDetail(Long menuId, String url) { + if (MyCommonUtil.isBlankOrNull(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysMenuService.getSysPermListWithDetail(menuId, url)); + } + + /** + * 查询菜单的用户列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param loginName 登录名。 + * @return 应答对象,包含从菜单到用户的完整权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysUserWithDetail") + public ResponseResult>> listSysUserWithDetail(Long menuId, String loginName) { + if (MyCommonUtil.isBlankOrNull(menuId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysMenuService.getSysUserListWithDetail(menuId, loginName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysOperationLogController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysOperationLogController.java new file mode 100644 index 0000000..e1718d5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysOperationLogController.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.log.dto.SysOperationLogDto; +import apelet.common.log.model.SysOperationLog; +import apelet.common.log.service.SysOperationLogService; +import apelet.common.log.vo.SysOperationLogVo; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 操作日志接口控制器对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "操作日志接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysOperationLog") +public class SysOperationLogController { + + @Autowired + private SysOperationLogService operationLogService; + + /** + * 数据权限列表。 + * + * @param sysOperationLogDtoFilter 操作日志查询过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象。包含操作日志列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysOperationLogDto sysOperationLogDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysOperationLog filter = MyModelUtil.copyTo(sysOperationLogDtoFilter, SysOperationLog.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysOperationLog.class); + List operationLogList = operationLogService.getSysOperationLogList(filter, orderBy); + List operationLogVoList = MyModelUtil.copyCollectionTo(operationLogList, SysOperationLogVo.class); + long totalCount = 0L; + if (operationLogList instanceof Page) { + totalCount = ((Page) operationLogList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(operationLogVoList, totalCount)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermCodeController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermCodeController.java new file mode 100644 index 0000000..64e502a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermCodeController.java @@ -0,0 +1,200 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysPermCodeDto; +import apelet.tenantadmin.upms.model.SysPermCode; +import apelet.tenantadmin.upms.service.SysPermCodeService; +import apelet.tenantadmin.upms.vo.SysPermCodeVo; +import com.alibaba.fastjson.TypeReference; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 权限字管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "权限字管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysPermCode") +public class SysPermCodeController { + + @Autowired + private SysPermCodeService sysPermCodeService; + + /** + * 新增权限字操作。 + * + * @param sysPermCodeDto 新增权限字对象。 + * @param permIdListString 与当前权限Id绑定的权限资源Id列表,多个权限资源之间逗号分隔。 + * @return 应答结果对象,包含新增权限字的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysPermCodeDto.permCodeId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysPermCodeDto sysPermCodeDto, @MyRequestBody String permIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermCodeDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED); + } + SysPermCode sysPermCode = MyModelUtil.copyTo(sysPermCodeDto, SysPermCode.class); + CallResult result = sysPermCodeService.verifyRelatedData(sysPermCode, null, permIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set permIdSet = null; + if (result.getData() != null) { + permIdSet = result.getData().getObject("permIdSet", new TypeReference>(){}); + } + sysPermCode = sysPermCodeService.saveNew(sysPermCode, permIdSet); + return ResponseResult.success(sysPermCode.getPermCodeId()); + } + + /** + * 更新权限字操作。 + * + * @param sysPermCodeDto 更新权限字对象。 + * @param permIdListString 与当前权限Id绑定的权限资源Id列表,多个权限资源之间逗号分隔。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysPermCodeDto sysPermCodeDto, @MyRequestBody String permIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermCodeDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPermCode originalSysPermCode = sysPermCodeService.getById(sysPermCodeDto.getPermCodeId()); + if (originalSysPermCode == null) { + errorMessage = "数据验证失败,当前权限字并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysPermCode sysPermCode = MyModelUtil.copyTo(sysPermCodeDto, SysPermCode.class); + CallResult result = sysPermCodeService.verifyRelatedData(sysPermCode, originalSysPermCode, permIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set permIdSet = null; + if (result.getData() != null) { + permIdSet = result.getData().getObject("permIdSet", new TypeReference>(){}); + } + try { + if (!sysPermCodeService.update(sysPermCode, originalSysPermCode, permIdSet)) { + errorMessage = "数据验证失败,当前权限字并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + } catch (DuplicateKeyException e) { + errorMessage = "数据操作失败,权限字编码已经存在!"; + return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定权限字操作。 + * + * @param permCodeId 指定的权限字主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long permCodeId) { + if (MyCommonUtil.existBlankArgument(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + if (sysPermCodeService.hasChildren(permCodeId)) { + errorMessage = "数据验证失败,当前权限字存在下级权限字!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysPermCodeService.remove(permCodeId)) { + errorMessage = "数据操作失败,权限字不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看权限字列表。 + * + * @return 应答结果对象,包含权限字列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List sysPermCodeList = + sysPermCodeService.getAllListByOrder("permCodeType", "showOrder"); + return ResponseResult.success(SysPermCode.INSTANCE.fromModelList(sysPermCodeList)); + } + + /** + * 查看权限字对象详情。 + * + * @param permCodeId 指定权限字主键Id。 + * @return 应答结果对象,包含权限字对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long permCodeId) { + if (MyCommonUtil.existBlankArgument(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysPermCode sysPermCode = + sysPermCodeService.getByIdWithRelation(permCodeId, MyRelationParam.full()); + if (sysPermCode == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysPermCodeVo sysPermCodeVo = MyModelUtil.copyTo(sysPermCode, SysPermCodeVo.class); + return ResponseResult.success(sysPermCodeVo); + } + + /** + * 查询权限字的用户列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param loginName 登录名。 + * @return 应答对象。包含从权限字到用户的完整权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysUserWithDetail") + public ResponseResult>> listSysUserWithDetail(Long permCodeId, String loginName) { + if (MyCommonUtil.isBlankOrNull(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysPermCodeService.getSysUserListWithDetail(permCodeId, loginName)); + } + + /** + * 查询权限字的角色列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param roleName 角色名。 + * @return 应答对象。包含从权限字到角色的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysRoleWithDetail") + public ResponseResult>> listSysRoleWithDetail(Long permCodeId, String roleName) { + if (MyCommonUtil.isBlankOrNull(permCodeId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysPermCodeService.getSysRoleListWithDetail(permCodeId, roleName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermController.java new file mode 100644 index 0000000..f2f32ed --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermController.java @@ -0,0 +1,198 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysPermDto; +import apelet.tenantadmin.upms.model.SysPerm; +import apelet.tenantadmin.upms.service.SysPermService; +import apelet.tenantadmin.upms.vo.SysPermVo; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.List; +import java.util.Map; + +/** + * 权限资源管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "权限资源管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysPerm") +public class SysPermController { + + @Autowired + private SysPermService sysPermService; + + /** + * 新增权限资源操作。 + * + * @param sysPermDto 新增权限资源对象。 + * @return 应答结果对象,包含新增权限资源的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysPermDto.permId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysPermDto sysPermDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPerm sysPerm = MyModelUtil.copyTo(sysPermDto, SysPerm.class); + CallResult result = sysPermService.verifyRelatedData(sysPerm, null); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + sysPerm = sysPermService.saveNew(sysPerm); + return ResponseResult.success(sysPerm.getPermId()); + } + + /** + * 更新权限资源操作。 + * + * @param sysPermDto 更新权限资源对象。 + * @return 应答结果对象,包含更新权限资源的主键Id。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysPermDto sysPermDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPerm originalPerm = sysPermService.getById(sysPermDto.getPermId()); + if (originalPerm == null) { + errorMessage = "数据验证失败,当前权限资源并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysPerm sysPerm = MyModelUtil.copyTo(sysPermDto, SysPerm.class); + CallResult result = sysPermService.verifyRelatedData(sysPerm, originalPerm); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + sysPermService.update(sysPerm, originalPerm); + return ResponseResult.success(); + } + + /** + * 删除指定权限资源操作。 + * + * @param permId 指定的权限资源主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long permId) { + if (MyCommonUtil.existBlankArgument(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysPermService.remove(permId)) { + String errorMessage = "数据操作失败,权限不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看权限资源对象详情。 + * + * @param permId 指定权限资源主键Id。 + * @return 应答结果对象,包含权限资源对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long permId) { + if (MyCommonUtil.existBlankArgument(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysPerm perm = sysPermService.getById(permId); + if (perm == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysPermVo permVo = MyModelUtil.copyTo(perm, SysPermVo.class); + return ResponseResult.success(permVo); + } + + /** + * 查看权限资源列表。 + * + * @param sysPermDtoFilter 过滤对象。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含权限资源列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysPermDto sysPermDtoFilter, @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysPerm filter = MyModelUtil.copyTo(sysPermDtoFilter, SysPerm.class); + List permList = sysPermService.getPermListWithRelation(filter); + List permVoList = MyModelUtil.copyCollectionTo(permList, SysPermVo.class); + long totalCount = 0L; + if (permList instanceof Page) { + totalCount = ((Page) permList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(permVoList, totalCount)); + } + + /** + * 查询权限资源地址的用户列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param loginName 登录名。 + * @return 应答对象。包含从权限资源到用户的完整权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysUserWithDetail") + public ResponseResult>> listSysUserWithDetail(Long permId, String loginName) { + if (MyCommonUtil.isBlankOrNull(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysPermService.getSysUserListWithDetail(permId, loginName)); + } + + /** + * 查询权限资源地址的角色列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param roleName 角色名。 + * @return 应答对象。包含从权限资源到角色的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysRoleWithDetail") + public ResponseResult>> listSysRoleWithDetail(Long permId, String roleName) { + if (MyCommonUtil.isBlankOrNull(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysPermService.getSysRoleListWithDetail(permId, roleName)); + } + + /** + * 查询权限资源地址的菜单列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param menuName 菜单名。 + * @return 应答对象。包含从权限资源到菜单的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysMenuWithDetail") + public ResponseResult>> listSysMenuWithDetail(Long permId, String menuName) { + if (MyCommonUtil.isBlankOrNull(permId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysPermService.getSysMenuListWithDetail(permId, menuName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermModuleController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermModuleController.java new file mode 100644 index 0000000..bf3fdce --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPermModuleController.java @@ -0,0 +1,167 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysPermModuleDto; +import apelet.tenantadmin.upms.model.SysPerm; +import apelet.tenantadmin.upms.model.SysPermModule; +import apelet.tenantadmin.upms.service.SysPermModuleService; +import apelet.tenantadmin.upms.vo.SysPermModuleVo; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.groups.Default; +import java.util.*; + +/** + * 权限资源模块管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "权限资源模块管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysPermModule") +public class SysPermModuleController { + + @Autowired + private SysPermModuleService sysPermModuleService; + + /** + * 新增权限资源模块操作。 + * + * @param sysPermModuleDto 新增权限资源模块对象。 + * @return 应答结果对象,包含新增权限资源模块的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysPermModuleDto.moduleId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysPermModuleDto sysPermModuleDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermModuleDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPermModule sysPermModule = MyModelUtil.copyTo(sysPermModuleDto, SysPermModule.class); + if (sysPermModule.getParentId() != null + && sysPermModuleService.getById(sysPermModule.getParentId()) == null) { + errorMessage = "数据验证失败,关联的上级权限模块并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + sysPermModuleService.saveNew(sysPermModule); + return ResponseResult.success(sysPermModule.getModuleId()); + } + + /** + * 更新权限资源模块操作。 + * + * @param sysPermModuleDto 更新权限资源模块对象。 + * @return 应答结果对象,包含新增权限资源模块的主键Id。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysPermModuleDto sysPermModuleDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPermModuleDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPermModule sysPermModule = MyModelUtil.copyTo(sysPermModuleDto, SysPermModule.class); + SysPermModule originalPermModule = sysPermModuleService.getById(sysPermModule.getModuleId()); + if (originalPermModule == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + if (sysPermModule.getParentId() != null + && !sysPermModule.getParentId().equals(originalPermModule.getParentId()) + && sysPermModuleService.getById(sysPermModule.getParentId()) == null) { + errorMessage = "数据验证失败,关联的上级权限模块并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage); + } + if (!sysPermModuleService.update(sysPermModule, originalPermModule)) { + errorMessage = "数据验证失败,当前模块并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定权限资源模块操作。 + * + * @param moduleId 指定的权限资源模块主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long moduleId) { + if (MyCommonUtil.existBlankArgument(moduleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String errorMessage; + if (sysPermModuleService.hasChildren(moduleId) + || sysPermModuleService.hasModulePerms(moduleId)) { + errorMessage = "数据验证失败,当前权限模块存在子模块或权限资源,请先删除关联数据!"; + return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage); + } + if (!sysPermModuleService.remove(moduleId)) { + errorMessage = "数据操作失败,权限模块不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看全部权限资源模块列表。 + * + * @return 应答结果对象,包含权限资源模块列表。 + */ + @PostMapping("/list") + public ResponseResult> list() { + List permModuleList = sysPermModuleService.getAllListByOrder("showOrder"); + return ResponseResult.success(MyModelUtil.copyCollectionTo(permModuleList, SysPermModuleVo.class)); + } + + /** + * 列出全部权限资源模块及其下级关联的权限资源列表。 + * + * @return 应答结果对象,包含树状列表,结构为权限资源模块和权限资源之间的树状关系。 + */ + @PostMapping("/listAll") + public ResponseResult>> listAll() { + List sysPermModuleList = sysPermModuleService.getPermModuleAndPermList(); + List> resultList = new LinkedList<>(); + for (SysPermModule sysPermModule : sysPermModuleList) { + Map permModuleMap = new HashMap<>(5); + permModuleMap.put("id", sysPermModule.getModuleId()); + permModuleMap.put("name", sysPermModule.getModuleName()); + permModuleMap.put("type", sysPermModule.getModuleType()); + permModuleMap.put("isPerm", false); + if (MyCommonUtil.isNotBlankOrNull(sysPermModule.getParentId())) { + permModuleMap.put("parentId", sysPermModule.getParentId()); + } + resultList.add(permModuleMap); + if (CollectionUtils.isNotEmpty(sysPermModule.getSysPermList())) { + for (SysPerm sysPerm : sysPermModule.getSysPermList()) { + Map permMap = new HashMap<>(4); + permMap.put("id", sysPerm.getPermId()); + permMap.put("name", sysPerm.getPermName()); + permMap.put("isPerm", true); + permMap.put("url", sysPerm.getUrl()); + permMap.put("parentId", sysPermModule.getModuleId()); + resultList.add(permMap); + } + } + } + return ResponseResult.success(resultList); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPostController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPostController.java new file mode 100644 index 0000000..c9732c3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysPostController.java @@ -0,0 +1,181 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysPostDto; +import apelet.tenantadmin.upms.model.SysPost; +import apelet.tenantadmin.upms.service.SysPostService; +import apelet.tenantadmin.upms.vo.SysPostVo; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * 岗位管理操作控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "岗位管理操作管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysPost") +public class SysPostController { + + @Autowired + private SysPostService sysPostService; + + /** + * 新增岗位管理数据。 + * + * @param sysPostDto 新增对象。 + * @return 应答结果对象,包含新增对象主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysPostDto.postId"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add(@MyRequestBody SysPostDto sysPostDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPostDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPost sysPost = MyModelUtil.copyTo(sysPostDto, SysPost.class); + sysPost = sysPostService.saveNew(sysPost); + return ResponseResult.success(sysPost.getPostId()); + } + + /** + * 更新岗位管理数据。 + * + * @param sysPostDto 更新对象。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update(@MyRequestBody SysPostDto sysPostDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysPostDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysPost sysPost = MyModelUtil.copyTo(sysPostDto, SysPost.class); + SysPost originalSysPost = sysPostService.getById(sysPost.getPostId()); + if (originalSysPost == null) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [数据] 并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!sysPostService.update(sysPost, originalSysPost)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除岗位管理数据。 + * + * @param postId 删除对象主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long postId) { + String errorMessage; + if (MyCommonUtil.existBlankArgument(postId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + // 验证关联Id的数据合法性 + SysPost originalSysPost = sysPostService.getById(postId); + if (originalSysPost == null) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!sysPostService.remove(postId)) { + errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 列出符合过滤条件的岗位管理列表。 + * + * @param sysPostDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含查询结果集。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysPostDto sysPostDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysPost sysPostFilter = MyModelUtil.copyTo(sysPostDtoFilter, SysPost.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysPost.class); + List sysPostList = sysPostService.getSysPostListWithRelation(sysPostFilter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(sysPostList, SysPost.INSTANCE)); + } + + /** + * 查看指定岗位管理对象详情。 + * + * @param postId 指定对象主键Id。 + * @return 应答结果对象,包含对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long postId) { + if (MyCommonUtil.existBlankArgument(postId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysPost sysPost = sysPostService.getByIdWithRelation(postId, MyRelationParam.full()); + if (sysPost == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysPostVo sysPostVo = SysPost.INSTANCE.fromModel(sysPost); + return ResponseResult.success(sysPostVo); + } + + /** + * 以字典形式返回全部岗位管理数据集合。字典的键值为[postId, postName]。 + * 白名单接口,登录用户均可访问。 + * + * @param filter 过滤对象。 + * @return 应答结果对象,包含的数据为 List>,map中包含两条记录,key的值分别是id和name,value对应具体数据。 + */ + @GetMapping("/listDict") + public ResponseResult>> listDict(@ParameterObject SysPostVo filter) { + List resultList = sysPostService.getListByFilter(MyModelUtil.copyTo(filter, SysPost.class)); + return ResponseResult.success(MyCommonUtil.toDictDataList(resultList, SysPost::getPostId, SysPost::getPostName)); + } + + /** + * 根据字典Id集合,获取查询后的字典数据。 + * + * @param postIds 字典Id集合。 + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listDictByIds") + public ResponseResult>> listDictByIds(@RequestParam List postIds) { + List resultList = sysPostService.getInList(new HashSet<>(postIds)); + return ResponseResult.success(MyCommonUtil.toDictDataList(resultList, SysPost::getPostId, SysPost::getPostName)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysRoleController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysRoleController.java new file mode 100644 index 0000000..902bbbf --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysRoleController.java @@ -0,0 +1,352 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.validator.UpdateGroup; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.tenantadmin.upms.dto.SysRoleDto; +import apelet.tenantadmin.upms.dto.SysUserDto; +import apelet.tenantadmin.upms.model.SysRole; +import apelet.tenantadmin.upms.model.SysUser; +import apelet.tenantadmin.upms.model.SysUserRole; +import apelet.tenantadmin.upms.service.SysRoleService; +import apelet.tenantadmin.upms.service.SysUserService; +import apelet.tenantadmin.upms.vo.SysRoleVo; +import apelet.tenantadmin.upms.vo.SysUserVo; +import com.alibaba.fastjson.TypeReference; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.groups.Default; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 角色管理接口控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "角色管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysRole") +public class SysRoleController { + + @Autowired + private SysRoleService sysRoleService; + @Autowired + private SysUserService sysUserService; + + /** + * 新增角色操作。 + * + * @param sysRoleDto 新增角色对象。 + * @param menuIdListString 与当前角色Id绑定的menuId列表,多个menuId之间逗号分隔。 + * @return 应答结果对象,包含新增角色的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = {"sysRoleDto.roleId", "sysRoleDto.createTimeStart", "sysRoleDto.createTimeEnd"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysRoleDto sysRoleDto, @MyRequestBody String menuIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysRoleDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysRole sysRole = MyModelUtil.copyTo(sysRoleDto, SysRole.class); + CallResult result = sysRoleService.verifyRelatedData(sysRole, null, menuIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set menuIdSet = null; + if (result.getData() != null) { + menuIdSet = result.getData().getObject("menuIdSet", new TypeReference>(){}); + } + sysRoleService.saveNew(sysRole, menuIdSet); + return ResponseResult.success(sysRole.getRoleId()); + } + + /** + * 更新角色操作。 + * + * @param sysRoleDto 更新角色对象。 + * @param menuIdListString 与当前角色Id绑定的menuId列表,多个menuId之间逗号分隔。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysRoleDto sysRoleDto, @MyRequestBody String menuIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysRoleDto, Default.class, UpdateGroup.class); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysRole originalSysRole = sysRoleService.getById(sysRoleDto.getRoleId()); + if (originalSysRole == null) { + errorMessage = "数据验证失败,当前角色并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + SysRole sysRole = MyModelUtil.copyTo(sysRoleDto, SysRole.class); + CallResult result = sysRoleService.verifyRelatedData(sysRole, originalSysRole, menuIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set menuIdSet = null; + if (result.getData() != null) { + menuIdSet = result.getData().getObject("menuIdSet", new TypeReference>(){}); + } + if (!sysRoleService.update(sysRole, originalSysRole, menuIdSet)) { + errorMessage = "更新失败,数据不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 删除指定角色操作。 + * + * @param roleId 指定角色主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long roleId) { + if (MyCommonUtil.existBlankArgument(roleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysRoleService.remove(roleId)) { + String errorMessage = "数据操作失败,角色不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } + + /** + * 查看角色列表。 + * + * @param sysRoleDtoFilter 角色过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含角色列表。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysRoleDto sysRoleDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysRole filter = MyModelUtil.copyTo(sysRoleDtoFilter, SysRole.class); + List roleList = sysRoleService.getSysRoleList( + filter, MyOrderParam.buildOrderBy(orderParam, SysRole.class)); + List roleVoList = MyModelUtil.copyCollectionTo(roleList, SysRoleVo.class); + long totalCount = 0L; + if (roleList instanceof Page) { + totalCount = ((Page) roleList).getTotal(); + } + return ResponseResult.success(MyPageUtil.makeResponseData(roleVoList, totalCount)); + } + + /** + * 查看角色详情。 + * + * @param roleId 指定角色主键Id。 + * @return 应答结果对象,包含角色详情对象。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long roleId) { + if (MyCommonUtil.existBlankArgument(roleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + SysRole sysRole = sysRoleService.getByIdWithRelation(roleId, MyRelationParam.full()); + if (sysRole == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysRoleVo sysRoleVo = MyModelUtil.copyTo(sysRole, SysRoleVo.class); + return ResponseResult.success(sysRoleVo); + } + + /** + * 获取不包含指定角色Id的用户列表。 + * 用户和角色是多对多关系,当前接口将返回没有赋值指定RoleId的用户列表。可用于给角色添加新用户。 + * + * @param roleId 角色主键Id。 + * @param sysUserDtoFilter 用户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含用户列表数据。 + */ + @PostMapping("/listNotInUserRole") + public ResponseResult> listNotInUserRole( + @MyRequestBody Long roleId, + @MyRequestBody SysUserDto sysUserDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doRoleUserVerify(roleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysUser filter = MyModelUtil.copyTo(sysUserDtoFilter, SysUser.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class); + List userList = sysUserService.getNotInSysUserListByRoleId(roleId, filter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(userList, SysUser.INSTANCE)); + } + + /** + * 拥有指定角色的用户列表。 + * + * @param roleId 角色主键Id。 + * @param sysUserDtoFilter 用户过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含用户列表数据。 + */ + @PostMapping("/listUserRole") + public ResponseResult> listUserRole( + @MyRequestBody Long roleId, + @MyRequestBody SysUserDto sysUserDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + ResponseResult verifyResult = this.doRoleUserVerify(roleId); + if (!verifyResult.isSuccess()) { + return ResponseResult.errorFrom(verifyResult); + } + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysUser filter = MyModelUtil.copyTo(sysUserDtoFilter, SysUser.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class); + List userList = sysUserService.getSysUserListByRoleId(roleId, filter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(userList, SysUser.INSTANCE)); + } + + private ResponseResult doRoleUserVerify(Long roleId) { + if (MyCommonUtil.existBlankArgument(roleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysRoleService.existId(roleId)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + return ResponseResult.success(); + } + + /** + * 为指定角色添加用户列表。该操作可同时给一批用户赋值角色,并在同一事务内完成。 + * + * @param roleId 角色主键Id。 + * @param userIdListString 逗号分隔的用户Id列表。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.ADD_M2M) + @PostMapping("/addUserRole") + public ResponseResult addUserRole(@MyRequestBody Long roleId, @MyRequestBody String userIdListString) { + if (MyCommonUtil.existBlankArgument(roleId, userIdListString)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + Set userIdSet = Arrays.stream( + userIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysRoleService.existId(roleId) + || !sysUserService.existUniqueKeyList("userId", userIdSet)) { + return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); + } + List userRoleList = new LinkedList<>(); + for (Long userId : userIdSet) { + SysUserRole userRole = new SysUserRole(); + userRole.setRoleId(roleId); + userRole.setUserId(userId); + userRoleList.add(userRole); + } + sysRoleService.addUserRoleList(userRoleList); + return ResponseResult.success(); + } + + /** + * 为指定用户移除指定角色。 + * + * @param roleId 指定角色主键Id。 + * @param userId 指定用户主键Id。 + * @return 应答数据结果。 + */ + @OperationLog(type = SysOperationLogType.DELETE_M2M) + @PostMapping("/deleteUserRole") + public ResponseResult deleteUserRole(@MyRequestBody Long roleId, @MyRequestBody Long userId) { + if (MyCommonUtil.existBlankArgument(roleId, userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + if (!sysRoleService.removeUserRole(roleId, userId)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 以字典形式返回全部角色管理数据集合。字典的键值为[roleId, roleName]。 + * 白名单接口,登录用户均可访问。 + * + * @param filter 过滤对象。 + * @return 应答结果对象,包含的数据为 List>,map中包含两条记录,key的值分别是id和name,value对应具体数据。 + */ + @GetMapping("/listDict") + public ResponseResult>> listDict(@ParameterObject SysRoleVo filter) { + List resultList = sysRoleService.getListByFilter(MyModelUtil.copyTo(filter, SysRole.class)); + return ResponseResult.success(MyCommonUtil.toDictDataList(resultList, SysRole::getRoleId, SysRole::getRoleName)); + } + + /** + * 根据字典Id集合,获取查询后的字典数据。 + * + * @param dictIds 字典Id集合。 + * @return 应答结果对象,包含字典形式的数据集合。 + */ + @GetMapping("/listDictByIds") + public ResponseResult>> listDictByIds(@RequestParam List dictIds) { + List resultList = sysRoleService.getInList(new HashSet<>(dictIds)); + return ResponseResult.success(MyCommonUtil.toDictDataList(resultList, SysRole::getRoleId, SysRole::getRoleName)); + } + + /** + * 查询角色的权限资源地址列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param url url过滤条件。 + * @return 应答对象,包含从角色到权限资源的完整权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysPermWithDetail") + public ResponseResult>> listSysPermByWithDetail(Long roleId, String url) { + if (MyCommonUtil.isBlankOrNull(roleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysRoleService.getSysPermListWithDetail(roleId, url)); + } + + /** + * 查询角色的权限字列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param permCode 权限字名称过滤条件。 + * @return 应答对象,包含从角色到权限字的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysPermCodeWithDetail") + public ResponseResult>> listSysPermCodeWithDetail(Long roleId, String permCode) { + if (MyCommonUtil.isBlankOrNull(roleId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysRoleService.getSysPermCodeListWithDetail(roleId, permCode)); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java new file mode 100644 index 0000000..a19d724 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java @@ -0,0 +1,499 @@ +package apelet.tenantadmin.upms.controller; + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.exception.MyRuntimeException; +import apelet.common.core.object.*; +import apelet.common.core.upload.*; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.eas.service.impl.EASSyncService; +import apelet.common.generator.utils.OrmGenDataSourceUtil; +import apelet.common.log.annotation.OperationLog; +import apelet.common.log.model.constant.SysOperationLogType; +import apelet.common.redis.cache.SessionCacheHelper; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.upms.dto.SysUserDto; +import apelet.tenantadmin.upms.model.SysUser; +import apelet.tenantadmin.upms.service.SysUserService; +import apelet.tenantadmin.upms.vo.SysUserVo; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import com.alibaba.fastjson.*; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.page.PageMethod; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import com.xxl.job.core.handler.annotation.XxlJob; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 用户管理操作控制器类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Tag(name = "用户管理管理接口") +@Slf4j +@RestController +@RequestMapping("/tenantadmin/upms/sysUser") +public class SysUserController { + + @Autowired + private SysUserService sysUserService; + @Autowired + private ApplicationConfig appConfig; + @Autowired + private SessionCacheHelper cacheHelper; + @Autowired + private UpDownloaderFactory upDownloaderFactory; + @Autowired + private OrmGenDataSourceUtil ormGenDataSourceUtil; + + /** + * 新增用户操作。 + * + * @param sysUserDto 新增用户对象。 + * @param deptPostIdListString 逗号分隔的部门岗位Id列表。 + * @param dataPermIdListString 逗号分隔的数据权限Id列表。 + * @param roleIdListString 逗号分隔的角色Id列表。 + * @return 应答结果对象,包含新增用户的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = { + "sysUserDto.userId", + "sysUserDto.createTimeStart", + "sysUserDto.createTimeEnd"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/add") + public ResponseResult add( + @MyRequestBody SysUserDto sysUserDto, + @MyRequestBody String deptPostIdListString, + @MyRequestBody String dataPermIdListString, + @MyRequestBody String roleIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); + CallResult result = sysUserService.verifyRelatedData( + sysUser, null, roleIdListString, deptPostIdListString, dataPermIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>() { + }); + Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>() { + }); + Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>() { + }); + sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet); + return ResponseResult.success(sysUser.getUserId()); + } + + + /** + * 新增用户操作。 + * + * @param sysUserDto 新增用户对象。 + * @return 应答结果对象,包含新增用户的主键Id。 + */ + @ApiOperationSupport(ignoreParameters = { + "sysUserDto.createTimeStart", + "sysUserDto.createTimeEnd"}) + @OperationLog(type = SysOperationLogType.ADD) + @PostMapping("/sgptRegister") + public ResponseResult sgptRegister( + @MyRequestBody SysUserDto sysUserDto) { + String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); + CallResult result = sysUserService.verifyRelatedData( + sysUser, null, "1801273688285270000", "1821796396504723500", "1821788867364139000"); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>() { + }); + Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>() { + }); + Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>() { + }); + SysUser sysUserO = sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet); + + try { + ObjectValue userInfoObj = new ObjectValue("sys_user_info"); + /* ObjectValue itemObj = new ObjectValue("sys_item"); + itemObj.put("id",1728540042151497L);*/ + userInfoObj.put("id", sysUserO.getUserId()); + userInfoObj.put("PHONE_NO", sysUserO.getMobile()); + userInfoObj.put("user_id", sysUserO.getUserId()); + userInfoObj.put("USER_TYPE", 1); + ormGenDataSourceUtil.addNew("sys_user_info", userInfoObj); + } catch (Exception ex) { + throw new MyRuntimeException(ex.getMessage()); + } + + + return ResponseResult.success(sysUser.getUserId()); + } + + + /** + * 更新用户操作。 + * + * @param sysUserDto 更新用户对象。 + * @param deptPostIdListString 逗号分隔的部门岗位Id列表。 + * @param dataPermIdListString 逗号分隔的数据权限Id列表。 + * @param roleIdListString 逗号分隔的角色Id列表。 + * @return 应答结果对象。 + */ + @ApiOperationSupport(ignoreParameters = { + "sysUserDto.createTimeStart", + "sysUserDto.createTimeEnd"}) + @OperationLog(type = SysOperationLogType.UPDATE) + @PostMapping("/update") + public ResponseResult update( + @MyRequestBody SysUserDto sysUserDto, + @MyRequestBody String deptPostIdListString, + @MyRequestBody String dataPermIdListString, + @MyRequestBody String roleIdListString) { + String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto, true); + if (errorMessage != null) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SysUser originalUser = sysUserService.getById(sysUserDto.getUserId()); + if (originalUser == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); + CallResult result = sysUserService.verifyRelatedData( + sysUser, originalUser, roleIdListString, deptPostIdListString, dataPermIdListString); + if (!result.isSuccess()) { + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); + } + Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>() { + }); + Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>() { + }); + Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>() { + }); + if (!sysUserService.update(sysUser, originalUser, roleIdSet, deptPostIdSet, dataPermIdSet)) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + return ResponseResult.success(); + } + + /** + * 删除用户管理数据。 + * + * @param userId 删除对象主键Id。 + * @return 应答结果对象。 + */ + @OperationLog(type = SysOperationLogType.DELETE) + @PostMapping("/delete") + public ResponseResult delete(@MyRequestBody Long userId) { + if (MyCommonUtil.existBlankArgument(userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return this.doDelete(userId); + } + + /** + * 列出符合过滤条件的用户管理列表。 + * + * @param sysUserDtoFilter 过滤对象。 + * @param orderParam 排序参数。 + * @param pageParam 分页参数。 + * @return 应答结果对象,包含查询结果集。 + */ + @PostMapping("/list") + public ResponseResult> list( + @MyRequestBody SysUserDto sysUserDtoFilter, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize()); + } + SysUser sysUserFilter = MyModelUtil.copyTo(sysUserDtoFilter, SysUser.class); + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class); + List sysUserList = sysUserService.getSysUserListWithRelation(sysUserFilter, orderBy); + return ResponseResult.success(MyPageUtil.makeResponseData(sysUserList, SysUser.INSTANCE)); + } + + + + + @PostMapping("/syncEasUser") + @XxlJob("syncEasUser") + public ResponseResult syncEasUser() { + return ResponseResult.success(sysUserService.syncEasUSer()); + } + + + + /** + * 查看指定用户管理对象详情。 + * + * @param userId 指定对象主键Id。 + * @return 应答结果对象,包含对象详情。 + */ + @GetMapping("/view") + public ResponseResult view(@RequestParam Long userId) { + // 这里查看用户数据时候,需要把用户多对多关联的角色和数据权限Id一并查出。 + SysUser sysUser = sysUserService.getByIdWithRelation(userId, MyRelationParam.full()); + if (sysUser == null) { + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); + } + SysUserVo sysUserVo = SysUser.INSTANCE.fromModel(sysUser); + return ResponseResult.success(sysUserVo); + } + + /** + * 附件文件下载。 + * 这里将图片和其他类型的附件文件放到不同的父目录下,主要为了便于今后图片文件的迁移。 + * + * @param userId 附件所在记录的主键Id。 + * @param fieldName 附件所属的字段名。 + * @param filename 文件名。如果没有提供该参数,就从当前记录的指定字段中读取。 + * @param asImage 下载文件是否为图片。 + * @param response Http 应答对象。 + */ + @OperationLog(type = SysOperationLogType.DOWNLOAD, saveResponse = false) + @GetMapping("/download") + public void download( + @RequestParam(required = false) Long userId, + @RequestParam String fieldName, + @RequestParam String filename, + @RequestParam Boolean asImage, + HttpServletResponse response) { + if (MyCommonUtil.existBlankArgument(fieldName, filename, asImage)) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + // 使用try来捕获异常,是为了保证一旦出现异常可以返回500的错误状态,便于调试。 + // 否则有可能给前端返回的是200的错误码。 + try { + // 如果请求参数中没有包含主键Id,就判断该文件是否为当前session上传的。 + if (userId == null) { + if (!cacheHelper.existSessionUploadFile(filename)) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN); + return; + } + } else { + SysUser sysUser = sysUserService.getById(userId); + if (sysUser == null) { + ResponseResult.output(HttpServletResponse.SC_NOT_FOUND); + return; + } + String fieldJsonData = (String) ReflectUtil.getFieldValue(sysUser, fieldName); + if (fieldJsonData == null && !cacheHelper.existSessionUploadFile(filename)) { + ResponseResult.output(HttpServletResponse.SC_BAD_REQUEST); + return; + } + if (!BaseUpDownloader.containFile(fieldJsonData, filename) + && !cacheHelper.existSessionUploadFile(filename)) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN); + return; + } + } + UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName); + if (!storeInfo.isSupportUpload()) { + ResponseResult.output(HttpServletResponse.SC_NOT_IMPLEMENTED, + ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD)); + return; + } + BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType()); + upDownloader.doDownload(appConfig.getUploadFileBaseDir(), + SysUser.class.getSimpleName(), fieldName, filename, asImage, response); + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + log.error(e.getMessage(), e); + } + } + + /** + * 文件上传操作。 + * + * @param fieldName 上传文件名。 + * @param asImage 是否作为图片上传。如果是图片,今后下载的时候无需权限验证。否则就是附件上传,下载时需要权限验证。 + * @param uploadFile 上传文件对象。 + */ + @OperationLog(type = SysOperationLogType.UPLOAD, saveResponse = false) + @PostMapping("/upload") + public void upload( + @RequestParam String fieldName, + @RequestParam Boolean asImage, + @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException { + UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName); + // 这里就会判断参数中指定的字段,是否支持上传操作。 + if (!storeInfo.isSupportUpload()) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, + ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD)); + return; + } + // 根据字段注解中的存储类型,通过工厂方法获取匹配的上传下载实现类,从而解耦。 + BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType()); + UploadResponseInfo responseInfo = upDownloader.doUpload(null, + appConfig.getUploadFileBaseDir(), SysUser.class.getSimpleName(), fieldName, asImage, uploadFile); + if (Boolean.TRUE.equals(responseInfo.getUploadFailed())) { + ResponseResult.output(HttpServletResponse.SC_FORBIDDEN, + ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage())); + return; + } + cacheHelper.putSessionUploadFile(responseInfo.getFilename()); + ResponseResult.output(ResponseResult.success(responseInfo)); + } + + /** + * 查询用户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param url url过滤条件。 + * @return 应答对象,包含从用户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysPermWithDetail") + public ResponseResult>> listSysPermWithDetail(Long userId, String url) { + if (MyCommonUtil.isBlankOrNull(userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysUserService.getSysPermListWithDetail(userId, url)); + } + + /** + * 查询用户的权限字列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 应答对象,包含从用户到权限字的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysPermCodeWithDetail") + public ResponseResult>> listSysPermCodeWithDetail(Long userId, String permCode) { + if (MyCommonUtil.isBlankOrNull(userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysUserService.getSysPermCodeListWithDetail(userId, permCode)); + } + + /** + * 查询用户的菜单列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 应答对象,包含从用户到菜单的权限分配路径信息的查询结果列表。 + */ + @GetMapping("/listSysMenuWithDetail") + public ResponseResult>> listSysMenuWithDetail(Long userId, String menuName) { + if (MyCommonUtil.isBlankOrNull(userId)) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(sysUserService.getSysMenuListWithDetail(userId, menuName)); + } + + /** + * 获取指定角色Id集合的用户数据集合。 + * + * @param roleIds 角色Id集合。 + * @return 应该结果对象,包含查询后的用户列表。 + */ + @GetMapping("/getSysUserListByRoleIds") + public ResponseResult> getSysUserListByRoleIds(@RequestParam Set roleIds) { + List resultList = new LinkedList<>(); + for (Long roleId : roleIds) { + List userList = sysUserService.getSysUserListByRoleId(roleId, null, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(SysUser.INSTANCE.fromModelList(userList)); + } + } + return ResponseResult.success(resultList); + } + + /** + * 获取指定部门Id集合的用户数据集合。 + * + * @param deptIds 部门Id集合。 + * @return 应该结果对象,包含查询后的用户列表。 + */ + @GetMapping("/getSysUserListByDeptIds") + public ResponseResult> getSysUserListByDeptIds(@RequestParam Set deptIds) { + List resultList = new LinkedList<>(); + for (Long deptId : deptIds) { + SysUser filter = new SysUser(); + filter.setDeptId(deptId); + List userList = sysUserService.getSysUserList(filter, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(SysUser.INSTANCE.fromModelList(userList)); + } + } + return ResponseResult.success(resultList); + } + + /** + * 获取指定岗位Id集合的用户数据集合。 + * + * @param postIds 岗位Id集合。 + * @return 应该结果对象,包含查询后的用户列表。 + */ + @GetMapping("/getSysUserListByPostIds") + public ResponseResult> getSysUserListByPostIds(@RequestParam Set postIds) { + List resultList = new LinkedList<>(); + for (Long postId : postIds) { + List userList = sysUserService.getSysUserListByPostId(postId, null, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(SysUser.INSTANCE.fromModelList(userList)); + } + } + return ResponseResult.success(resultList); + } + + /** + * 获取指定部门岗位Id集合的用户数据集合。 + * + * @param deptPostIds 部门岗位Id集合。 + * @return 应该结果对象,包含查询后的用户列表。 + */ + @GetMapping("/getSysUserListByDeptPostIds") + public ResponseResult> getSysUserListByDeptPostIds(@RequestParam Set deptPostIds) { + List resultList = new LinkedList<>(); + for (Long deptPostId : deptPostIds) { + List userList = sysUserService.getSysUserListByDeptPostId(deptPostId, null, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(SysUser.INSTANCE.fromModelList(userList)); + } + } + return ResponseResult.success(resultList); + } + + private ResponseResult doDelete(Long userId) { + String errorMessage; + // 验证关联Id的数据合法性 + SysUser originalSysUser = sysUserService.getById(userId); + if (originalSysUser == null) { + // NOTE: 修改下面方括号中的话述 + errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + if (!sysUserService.remove(userId)) { + errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!"; + return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); + } + return ResponseResult.success(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermDeptMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermDeptMapper.java new file mode 100644 index 0000000..8dbd602 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermDeptMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDataPermDept; + +/** + * 数据权限与部门关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDataPermDeptMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMapper.java new file mode 100644 index 0000000..80dae50 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMapper.java @@ -0,0 +1,43 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDataPerm; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 数据权限数据访问操作接口。 + * NOTE: 该对象一定不能被 @EnableDataPerm 注解标注,否则会导致无限递归。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDataPermMapper extends BaseDaoMapper { + + /** + * 获取数据权限列表。 + * + * @param sysDataPermFilter 过滤对象。 + * @param orderBy 排序字符串。 + * @return 过滤后的数据权限列表。 + */ + List getSysDataPermList( + @Param("sysDataPermFilter") SysDataPerm sysDataPermFilter, @Param("orderBy") String orderBy); + + /** + * 获取指定用户的数据权限列表。 + * + * @param userId 用户Id。 + * @return 数据权限列表。 + */ + List getSysDataPermListByUserId(@Param("userId") Long userId); + + /** + * 查询与指定菜单关联的数据权限列表。 + * + * @param menuId 菜单Id。 + * @return 与菜单Id关联的数据权限列表。 + */ + List getSysDataPermListByMenuId(@Param("menuId") Long menuId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMenuMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMenuMapper.java new file mode 100644 index 0000000..f563e43 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermMenuMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDataPermMenu; + +/** + * 数据权限与菜单关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDataPermMenuMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermUserMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermUserMapper.java new file mode 100644 index 0000000..df55197 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDataPermUserMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDataPermUser; + +/** + * 数据权限与用户关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDataPermUserMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptMapper.java new file mode 100644 index 0000000..fdfe95a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptMapper.java @@ -0,0 +1,52 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDept; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Set; + +/** + * 部门管理数据操作访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDeptMapper extends BaseDaoMapper { + + /** + * 批量插入对象列表。 + * + * @param sysDeptList 新增对象列表。 + */ + void insertList(List sysDeptList); + + /** + * 获取过滤后的对象列表。 + * + * @param inFilterColumn 参与(In-list)过滤的数据表列。 + * @param inFilterValues 参与(In-list)过滤的数据表列值集合。 + * @param sysDeptFilter 过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 对象列表。 + */ + List getSysDeptList( + @Param("inFilterColumn") String inFilterColumn, + @Param("inFilterValues") Set inFilterValues, + @Param("sysDeptFilter") SysDept sysDeptFilter, + @Param("orderBy") String orderBy); + + /** + * 获取对象列表,过滤条件中包含like和between条件,以及指定属性的(in list)过滤条件。 + * + * @param inFilterColumn 参与(In-list)过滤的数据表列。 + * @param inFilterValues 参与(In-list)过滤的数据表列值集合。 + * @param sysDeptFilter 过滤对象。 + * @return 对象列表。 + */ + Integer getSysDeptCount( + @Param("inFilterColumn") String inFilterColumn, + @Param("inFilterValues") Set inFilterValues, + @Param("sysDeptFilter") SysDept sysDeptFilter); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptPostMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptPostMapper.java new file mode 100644 index 0000000..d16f52a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptPostMapper.java @@ -0,0 +1,33 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDeptPost; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 部门岗位数据操作访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDeptPostMapper extends BaseDaoMapper { + + /** + * 获取指定部门Id的部门岗位多对多关联数据列表,以及关联的部门和岗位数据。 + * + * @param deptId 部门Id。如果参数为空则返回全部数据。 + * @return 部门岗位多对多数据列表。 + */ + List> getSysDeptPostListWithRelationByDeptId(@Param("deptId") Long deptId); + + /** + * 获取指定部门Id的领导部门岗位列表。 + * + * @param deptId 部门Id。 + * @return 指定部门Id的领导部门岗位列表 + */ + List getLeaderDeptPostList(@Param("deptId") Long deptId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptRelationMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptRelationMapper.java new file mode 100644 index 0000000..a77e9c2 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysDeptRelationMapper.java @@ -0,0 +1,42 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysDeptRelation; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门关系树关联关系表访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDeptRelationMapper extends BaseDaoMapper { + + /** + * 将myDeptId的所有子部门,与其父部门parentDeptId解除关联关系。 + * + * @param parentDeptIds myDeptId的父部门Id列表。 + * @param myDeptId 当前部门。 + */ + void removeBetweenChildrenAndParents( + @Param("parentDeptIds") List parentDeptIds, @Param("myDeptId") Long myDeptId); + + /** + * 批量插入部门关联数据。 + * 由于目前版本(3.4.1)的Mybatis Plus没有提供真正的批量插入,为了保证效率需要自己实现。 + * 目前我们仅仅给出MySQL和PostgresSQL的insert list实现作为参考,其他数据库需要自行修改。 + * + * @param deptRelationList 部门关联关系数据列表。 + */ + void insertList(List deptRelationList); + + /** + * 批量插入当前部门的所有父部门列表,包括自己和自己的关系。 + * + * @param parentDeptId myDeptId的父部门Id。 + * @param myDeptId 当前部门。 + */ + void insertParentList(@Param("parentDeptId") Long parentDeptId, @Param("myDeptId") Long myDeptId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuMapper.java new file mode 100644 index 0000000..185f07a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuMapper.java @@ -0,0 +1,74 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 菜单数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysMenuMapper extends BaseDaoMapper { + + /** + * 获取登录用户的菜单列表。 + * + * @param userId 登录用户。 + * @return 菜单列表。 + */ + List getMenuListByUserId(@Param("userId") Long userId); + + /** + * 获取当前用户有权访问的在线表单菜单。 + * + * @param userId 指定的用户。 + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + List getOnlineMenuListByUserId(@Param("userId") Long userId, @Param("menuType") Integer menuType); + + /** + * 获取当前用户有权访问的在线统计表单的菜单。 + * + * @param userId 指定的用户。 + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + List getReportMenuListByUserId(@Param("userId") Long userId, @Param("menuType") int menuType); + + /** + * 查询角色Id列表下的所有菜单和与其关联的权限资源url。 + * + * @param roleIds 角色Id列表。 + * @return 角色Id列表下的所有菜单和与其关联的权限资源url。 + */ + List> getMenuAndPermListByRoleIds(@Param("roleIds") List roleIds); + + /** + * 查询菜单的权限资源地址列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param url 权限资源地址过滤条件。 + * @return 包含从菜单到权限资源的权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail( + @Param("menuId") Long menuId, @Param("url") String url); + + /** + * 查询菜单的用户列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param loginName 登录名。 + * @return 包含从菜单到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail( + @Param("menuId") Long menuId, @Param("loginName") String loginName); + + List getMenuListByPageIds(@Param("pageIds")List pageIds); + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuPermCodeMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuPermCodeMapper.java new file mode 100644 index 0000000..c91eb71 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysMenuPermCodeMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysMenuPermCode; + +/** + * 菜单与权限字关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysMenuPermCodeMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodeMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodeMapper.java new file mode 100644 index 0000000..2f65e8d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodeMapper.java @@ -0,0 +1,45 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPermCode; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 权限字数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermCodeMapper extends BaseDaoMapper { + + /** + * 获取用户的所有权限字列表。 + * + * @param userId 用户Id。 + * @return 该用户的权限字列表。 + */ + List getPermCodeListByUserId(@Param("userId") Long userId); + + /** + * 查询权限字的用户列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param loginName 登录名。 + * @return 包含从权限字到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail( + @Param("permCodeId") Long permCodeId, @Param("loginName") String loginName); + + /** + * 查询权限字的角色列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param roleName 角色名。 + * @return 包含从权限字到角色的权限分配路径信息的查询结果列表。 + */ + List> getSysRoleListWithDetail( + @Param("permCodeId") Long permCodeId, @Param("roleName") String roleName); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodePermMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodePermMapper.java new file mode 100644 index 0000000..8b2bce1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermCodePermMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPermCodePerm; + +/** + * 权限字与权限资源关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermCodePermMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermMapper.java new file mode 100644 index 0000000..c00a28c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermMapper.java @@ -0,0 +1,55 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPerm; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 权限资源数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermMapper extends BaseDaoMapper { + + /** + * 获取用户的权限列表。 + * + * @param userId 用户Id。 + * @return 该用户的权限标识列表。 + */ + List getPermListByUserId(@Param("userId") Long userId); + + /** + * 查询权限资源地址的用户列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param loginName 登录名。 + * @return 包含从权限资源到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail( + @Param("permId") Long permId, @Param("loginName") String loginName); + + /** + * 查询权限资源地址的角色列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param roleName 角色名。 + * @return 包含从权限资源到角色的权限分配路径信息的查询结果列表。 + */ + List> getSysRoleListWithDetail( + @Param("permId") Long permId, @Param("roleName") String roleName); + + /** + * 查询权限资源地址的菜单列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param menuName 菜单名。 + * @return 包含从权限资源到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysMenuListWithDetail( + @Param("permId") Long permId, @Param("menuName") String menuName); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermModuleMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermModuleMapper.java new file mode 100644 index 0000000..b56186b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermModuleMapper.java @@ -0,0 +1,22 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPermModule; + +import java.util.List; + +/** + * 权限资源模块数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermModuleMapper extends BaseDaoMapper { + + /** + * 获取整个权限模块和权限关联后的全部数据。 + * + * @return 关联的权限模块和权限资源列表。 + */ + List getPermModuleAndPermList(); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermWhitelistMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermWhitelistMapper.java new file mode 100644 index 0000000..97786cd --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPermWhitelistMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPermWhitelist; + +/** + * 权限资源白名单数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermWhitelistMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPostMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPostMapper.java new file mode 100644 index 0000000..0882ef9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysPostMapper.java @@ -0,0 +1,52 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysPost; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 岗位管理数据操作访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPostMapper extends BaseDaoMapper { + + /** + * 获取过滤后的对象列表。 + * + * @param sysPostFilter 主表过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 对象列表。 + */ + List getSysPostList( + @Param("sysPostFilter") SysPost sysPostFilter, @Param("orderBy") String orderBy); + + /** + * 获取指定部门的岗位列表。 + * + * @param deptId 部门Id。 + * @param sysPostFilter 从表过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 岗位数据列表。 + */ + List getSysPostListByDeptId( + @Param("deptId") Long deptId, + @Param("sysPostFilter") SysPost sysPostFilter, + @Param("orderBy") String orderBy); + + /** + * 根据关联主表Id,获取关联从表中没有和主表建立关联关系的数据列表。 + * + * @param deptId 关联主表Id。 + * @param sysPostFilter 过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 与主表没有建立关联的从表数据列表。 + */ + List getNotInSysPostListByDeptId( + @Param("deptId") Long deptId, + @Param("sysPostFilter") SysPost sysPostFilter, + @Param("orderBy") String orderBy); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMapper.java new file mode 100644 index 0000000..bab8d91 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMapper.java @@ -0,0 +1,46 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 角色数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysRoleMapper extends BaseDaoMapper { + + /** + * 获取对象列表,过滤条件中包含like和between条件。 + * + * @param sysRoleFilter 过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 对象列表。 + */ + List getSysRoleList(@Param("sysRoleFilter") SysRole sysRoleFilter, @Param("orderBy") String orderBy); + + /** + * 查询角色的权限资源地址列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param url url过滤条件。 + * @return 包含从角色到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail( + @Param("roleId") Long roleId, @Param("url") String url); + + /** + * 查询角色的权限字列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从角色到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysPermCodeListWithDetail( + @Param("roleId") Long roleId, @Param("permCode") String permCode); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMenuMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMenuMapper.java new file mode 100644 index 0000000..69e685f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysRoleMenuMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysRoleMenu; + +/** + * 角色与菜单操作关联关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysRoleMenuMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserMapper.java new file mode 100644 index 0000000..2c9cf80 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserMapper.java @@ -0,0 +1,254 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysUser; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 用户管理数据操作访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysUserMapper extends BaseDaoMapper { + + /** + * 批量插入对象列表。 + * + * @param sysUserList 新增对象列表。 + */ + void insertList(List sysUserList); + + /** + * 获取过滤后的对象列表。 + * + * @param inFilterColumn 参与(In-list)过滤的数据表列。 + * @param inFilterValues 参与(In-list)过滤的数据表列值集合。 + * @param sysUserFilter 过滤对象。 + * @param orderBy 排序字符串,order by从句的参数。 + * @return 对象列表。 + */ + List getSysUserList( + @Param("inFilterColumn") String inFilterColumn, + @Param("inFilterValues") Set inFilterValues, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 获取对象列表,过滤条件中包含like和between条件,以及指定属性的(in list)过滤条件。 + * + * @param inFilterColumn 参与(In-list)过滤的数据表列。 + * @param inFilterValues 参与(In-list)过滤的数据表列值集合。 + * @param sysUserFilter 过滤对象。 + * @return 对象列表。 + */ + Integer getSysUserCount( + @Param("inFilterColumn") String inFilterColumn, + @Param("inFilterValues") Set inFilterValues, + @Param("sysUserFilter") SysUser sysUserFilter); + + /** + * 根据部门Id集合,获取关联的用户列表。 + * + * @param deptIds 关联的部门Id集合。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和部门Id集合关联的用户列表。 + */ + List getSysUserListByDeptIds( + @Param("deptIds") Set deptIds, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据登录名集合,获取关联的用户列表。 + * @param loginNames 登录名集合。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和登录名集合关联的用户列表。 + */ + List getSysUserListByLoginNames( + @Param("loginNames") List loginNames, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据角色Id,获取关联的用户列表。 + * + * @param roleId 关联的角色Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和角色Id关联的用户列表。 + */ + List getSysUserListByRoleId( + @Param("roleId") Long roleId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据角色Id集合,获取去重后的用户Id列表。 + * + * @param roleIds 关联的角色Id集合。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和角色Id集合关联的去重后的用户Id列表。 + */ + List getUserIdListByRoleIds( + @Param("roleIds") Set roleIds, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据角色Id,获取和当前角色Id没有建立多对多关联关系的用户列表。 + * + * @param roleId 关联的角色Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和RoleId没有建立关联关系的用户列表。 + */ + List getNotInSysUserListByRoleId( + @Param("roleId") Long roleId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据数据权限Id,获取关联的用户列表。 + * + * @param dataPermId 关联的数据权限Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和DataPermId关联的用户列表。 + */ + List getSysUserListByDataPermId( + @Param("dataPermId") Long dataPermId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据数据权限Id,获取和当前数据权限Id没有建立多对多关联关系的用户列表。 + * + * @param dataPermId 关联的数据权限Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和DataPermId没有建立关联关系的用户列表。 + */ + List getNotInSysUserListByDataPermId( + @Param("dataPermId") Long dataPermId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据部门岗位Id集合,获取关联的去重后的用户Id列表。 + * + * @param deptPostIds 关联的部门岗位Id集合。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和部门岗位Id集合关联的去重后的用户Id列表。 + */ + List getUserIdListByDeptPostIds( + @Param("deptPostIds") Set deptPostIds, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据部门岗位Id,获取关联的用户列表。 + * + * @param deptPostId 关联的部门岗位Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和部门岗位Id关联的用户列表。 + */ + List getSysUserListByDeptPostId( + @Param("deptPostId") Long deptPostId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据部门岗位Id,获取和当前部门岗位Id没有建立多对多关联关系的用户列表。 + * + * @param deptPostId 关联的部门岗位Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和deptPostId没有建立关联关系的用户列表。 + */ + List getNotInSysUserListByDeptPostId( + @Param("deptPostId") Long deptPostId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据岗位Id集合,获取关联的去重后的用户Id列表。 + * + * @param postIds 关联的岗位Id集合。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和岗位Id集合关联的去重后的用户Id列表。 + */ + List getUserIdListByPostIds( + @Param("postIds") Set postIds, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 根据岗位Id,获取关联的用户列表。 + * + * @param postId 关联的岗位Id。 + * @param sysUserFilter 用户过滤条件对象。 + * @param orderBy order by从句的参数。 + * @return 和岗位Id关联的用户列表。 + */ + List getSysUserListByPostId( + @Param("postId") Long postId, + @Param("sysUserFilter") SysUser sysUserFilter, + @Param("orderBy") String orderBy); + + /** + * 查询用户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param url url过滤条件。 + * @return 包含从用户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail( + @Param("userId") Long userId, @Param("url") String url); + + /** + * 查询用户的权限字列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从用户到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysPermCodeListWithDetail( + @Param("userId") Long userId, @Param("permCode") String permCode); + + /** + * 查询用户的菜单列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从用户到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysMenuListWithDetail( + @Param("userId") Long userId, @Param("menuName") String menuName); + + /** + * 根据云之家openId获取当前用户信息 + * + * @param openId 云之家用户openId + * @return + */ + SysUser getUserByHubOpenId(String openId); + + /** + * 根据钉钉userId(暂时使用userid,后续可能修改)获取当前用户信息 + * + * @param userId 钉钉用户userId + * @return + */ + SysUser getUserByDingTalkOpenId(String userId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserPostMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserPostMapper.java new file mode 100644 index 0000000..5ddb7d2 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserPostMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysUserPost; + +/** + * 用户岗位数据操作访问接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysUserPostMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserRoleMapper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserRoleMapper.java new file mode 100644 index 0000000..bf1d553 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/SysUserRoleMapper.java @@ -0,0 +1,13 @@ +package apelet.tenantadmin.upms.dao; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.tenantadmin.upms.model.SysUserRole; + +/** + * 用户与角色关联关系数据访问操作接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysUserRoleMapper extends BaseDaoMapper { +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermDeptMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermDeptMapper.xml new file mode 100644 index 0000000..6e42c73 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermDeptMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMapper.xml new file mode 100644 index 0000000..f18fbf7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + AND xy_sys_data_perm.rule_type = #{sysDataPermFilter.ruleType} + + + + AND IFNULL(xy_sys_data_perm.data_perm_name, '') LIKE #{safeSearchString} + + + AND xy_sys_data_perm.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMenuMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMenuMapper.xml new file mode 100644 index 0000000..6b8d4ec --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermMenuMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermUserMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermUserMapper.xml new file mode 100644 index 0000000..fac07c1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDataPermUserMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptMapper.xml new file mode 100644 index 0000000..b2ce9f5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + INSERT INTO xy_sys_dept + (dept_id, + dept_name, + show_order, + parent_id, + deleted_flag, + create_user_id, + update_user_id, + create_time, + update_time) + VALUES + + (#{item.deptId}, + #{item.deptName}, + #{item.showOrder}, + #{item.parentId}, + #{item.deletedFlag}, + #{item.createUserId}, + #{item.updateUserId}, + #{item.createTime}, + #{item.updateTime}) + + + + + + + + AND xy_sys_dept.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + + + AND xy_sys_dept.dept_name LIKE #{safeSysDeptDeptName} + + + AND xy_sys_dept.parent_id = #{sysDeptFilter.parentId} + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptPostMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptPostMapper.xml new file mode 100644 index 0000000..0630d2a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptPostMapper.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptRelationMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptRelationMapper.xml new file mode 100644 index 0000000..0c16e36 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysDeptRelationMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + DELETE a FROM xy_sys_dept_relation a + INNER JOIN xy_sys_dept_relation b ON a.dept_id = b.dept_id + WHERE b.parent_dept_id = #{myDeptId} AND a.parent_dept_id IN + + #{item} + + + + + INSERT INTO xy_sys_dept_relation(parent_dept_id, dept_id) VALUES + + (#{item.parentDeptId}, #{item.deptId}) + + + + + INSERT INTO xy_sys_dept_relation(parent_dept_id, dept_id) + SELECT t.parent_dept_id, #{myDeptId} FROM xy_sys_dept_relation t + WHERE t.dept_id = #{parentDeptId} + UNION ALL + SELECT #{myDeptId}, #{myDeptId} + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuMapper.xml new file mode 100644 index 0000000..9625de3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuMapper.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuPermCodeMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuPermCodeMapper.xml new file mode 100644 index 0000000..6b345e0 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysMenuPermCodeMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodeMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodeMapper.xml new file mode 100644 index 0000000..be0f008 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodeMapper.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodePermMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodePermMapper.xml new file mode 100644 index 0000000..49aa06f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermCodePermMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermMapper.xml new file mode 100644 index 0000000..dd650bc --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermMapper.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermModuleMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermModuleMapper.xml new file mode 100644 index 0000000..b6f4f56 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermModuleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermWhitelistMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermWhitelistMapper.xml new file mode 100644 index 0000000..5c99493 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPermWhitelistMapper.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPostMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPostMapper.xml new file mode 100644 index 0000000..30ab7aa --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysPostMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + AND xy_sys_post.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + + + AND xy_sys_post.post_name LIKE #{safeSysPostPostName} + + + AND xy_sys_post.leader_post = #{sysPostFilter.leaderPost} + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMapper.xml new file mode 100644 index 0000000..a654e88 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMapper.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + AND role_name LIKE #{safeRoleName} + + + AND deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMenuMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMenuMapper.xml new file mode 100644 index 0000000..c39200e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysRoleMenuMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserMapper.xml new file mode 100644 index 0000000..457d7b9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserMapper.xml @@ -0,0 +1,426 @@ + + + + + + + + + + + + + + + + + + + + + + INSERT INTO xy_sys_user( + user_id, + login_name, + password, + dept_id, + show_name, + user_type, + head_image_url, + user_status, + email, + mobile, + create_user_id, + update_user_id, + create_time, + update_time, + deleted_flag, + cloudhub_id, + dingtalk_id, + eas_id + ) + VALUES + + ( + #{item.userId}, + #{item.loginName}, + #{item.password}, + #{item.deptId}, + #{item.showName}, + #{item.userType}, + #{item.headImageUrl}, + #{item.userStatus}, + #{item.email}, + #{item.mobile}, + #{item.createUserId}, + #{item.updateUserId}, + #{item.createTime}, + #{item.updateTime}, + #{item.deletedFlag}, + #{item.cloudhubId}, + #{item.dingtalkId}, + #{item.easId} + ) + + + + + + + + AND xy_sys_user.deleted_flag = ${@apelet.common.core.constant.GlobalDeletedFlag@NORMAL} + + + + + + + + AND xy_sys_user.login_name LIKE #{safeSysUserLoginName} + + + AND (EXISTS (SELECT 1 FROM xy_sys_dept_relation WHERE + xy_sys_dept_relation.parent_dept_id = #{sysUserFilter.deptId} + AND xy_sys_user.dept_id = xy_sys_dept_relation.dept_id)) + + + + AND xy_sys_user.show_name LIKE #{safeSysUserShowName} + + + AND xy_sys_user.user_status = #{sysUserFilter.userStatus} + + + AND xy_sys_user.create_time >= #{sysUserFilter.createTimeStart} + + + AND xy_sys_user.create_time <= #{sysUserFilter.createTimeEnd} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserPostMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserPostMapper.xml new file mode 100644 index 0000000..312491c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserPostMapper.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserRoleMapper.xml b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserRoleMapper.xml new file mode 100644 index 0000000..d5386ef --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dao/mapper/SysUserRoleMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLFieldVisitor.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLFieldVisitor.java new file mode 100644 index 0000000..ac5d58e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLFieldVisitor.java @@ -0,0 +1,12 @@ +package apelet.tenantadmin.upms.dto; + +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; + +public class SQLFieldVisitor extends SQLASTVisitorAdapter { + @Override + public boolean visit(SQLIdentifierExpr x) { + //后面继续看是否能获取数据库字段 + return true; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLParseDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLParseDto.java new file mode 100644 index 0000000..0ea8395 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SQLParseDto.java @@ -0,0 +1,10 @@ +package apelet.tenantadmin.upms.dto; + +import lombok.Data; + +@Data +public class SQLParseDto { + private String name; + private String txt; + private String type; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDeptDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDeptDto.java new file mode 100644 index 0000000..ef66440 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDeptDto.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.upms.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 数据权限与部门关联Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限与部门关联Dto") +@Data +public class SysDataPermDeptDto { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long dataPermId; + + /** + * 关联部门Id。 + */ + @Schema(description = "关联部门Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long deptId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDto.java new file mode 100644 index 0000000..7e3ceca --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermDto.java @@ -0,0 +1,55 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.constant.DataPermRuleType; +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 数据权限Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限Dto") +@Data +public class SysDataPermDto { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据权限Id不能为空!", groups = {UpdateGroup.class}) + private Long dataPermId; + + /** + * 显示名称。 + */ + @Schema(description = "显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据权限名称不能为空!") + private String dataPermName; + + /** + * 数据权限规则类型(0: 全部可见 1: 只看自己 2: 只看本部门 3: 本部门及子部门 4: 多部门及子部门 5: 自定义部门列表)。 + */ + @Schema(description = "数据权限规则类型", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据权限规则类型不能为空!") + @ConstDictRef(constDictClass = DataPermRuleType.class) + private Integer ruleType; + + /** + * 部门Id列表(逗号分隔)。 + */ + @Schema(hidden = true) + private String deptIdListString; + + /** + * 搜索字符串。 + */ + @Schema(description = "LIKE 模糊搜索字符串") + private String searchString; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermMenuDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermMenuDto.java new file mode 100644 index 0000000..58c751b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDataPermMenuDto.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.upms.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 数据权限与菜单关联Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限与菜单关联Dto") +@Data +public class SysDataPermMenuDto { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long dataPermId; + + /** + * 关联菜单Id。 + */ + @Schema(description = "关联菜单Id", requiredMode = Schema.RequiredMode.REQUIRED) + private Long menuId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptDto.java new file mode 100644 index 0000000..057d266 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptDto.java @@ -0,0 +1,46 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * SysDeptDto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "SysDeptDto对象") +@Data +public class SysDeptDto { + + /** + * 部门Id。 + */ + @Schema(description = "部门Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,部门Id不能为空!", groups = {UpdateGroup.class}) + private Long deptId; + + /** + * 部门名称。 + */ + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,部门名称不能为空!") + private String deptName; + + /** + * 显示顺序。 + */ + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,显示顺序不能为空!") + private Integer showOrder; + + /** + * 父部门Id。 + */ + @Schema(description = "父部门Id") + private Long parentId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptPostDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptPostDto.java new file mode 100644 index 0000000..7a1d2f2 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptPostDto.java @@ -0,0 +1,47 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 部门岗位Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "部门岗位Dto") +@Data +public class SysDeptPostDto { + + /** + * 部门岗位Id。 + */ + @Schema(description = "部门岗位Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,部门岗位Id不能为空!", groups = {UpdateGroup.class}) + private Long deptPostId; + + /** + * 部门Id。 + */ + @Schema(description = "部门Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,部门Id不能为空!", groups = {UpdateGroup.class}) + private Long deptId; + + /** + * 岗位Id。 + */ + @Schema(description = "岗位Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,岗位Id不能为空!", groups = {UpdateGroup.class}) + private Long postId; + + /** + * 部门岗位显示名称。 + */ + @Schema(description = "部门岗位显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,部门岗位显示名称不能为空!") + private String postShowName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysMenuDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysMenuDto.java new file mode 100644 index 0000000..eeba970 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysMenuDto.java @@ -0,0 +1,101 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysMenuType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 菜单Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "菜单Dto") +@Data +public class SysMenuDto { + + /** + * 菜单Id。 + */ + @Schema(description = "菜单Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "菜单Id不能为空!", groups = {UpdateGroup.class}) + private Long menuId; + + /** + * 父菜单Id,目录菜单的父菜单为null + */ + @Schema(description = "父菜单Id") + private Long parentId; + + /** + * 菜单显示名称。 + */ + @Schema(description = "菜单显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "菜单显示名称不能为空!") + private String menuName; + + /** + * 菜单类型 (0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @Schema(description = "菜单类型", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "菜单类型不能为空!") + @ConstDictRef(constDictClass = SysMenuType.class, message = "数据验证失败,菜单类型为无效值!") + private Integer menuType; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @Schema(description = "前端表单路由名称") + private String formRouterName; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @Schema(description = "在线表单主键Id") + private Long onlineFormId; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @Schema(description = "统计页面主键Id") + private Long reportPageId; + + /** + * 仅用于在线表单的流程Id。 + */ + @Schema(description = "仅用于在线表单的流程Id") + private Long onlineFlowEntryId; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @Schema(description = "菜单显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "菜单显示顺序不能为空!") + private Integer showOrder; + + /** + * 菜单图标。 + */ + @Schema(description = "菜单显示顺序") + private String icon; + + /** + * 附加信息。 + */ + @Schema(description = "附加信息") + private String extraData; + + /** + * 表单开发。 + */ + @Schema(description = "表单开发") + private String billExploit; + + @Schema(description = "关闭菜单缓存") + private Integer closeCache; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermCodeDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermCodeDto.java new file mode 100644 index 0000000..5cc6c7b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermCodeDto.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysPermCodeType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 权限字Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限字Dto") +@Data +public class SysPermCodeDto { + + /** + * 权限字Id。 + */ + @Schema(description = "权限字Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限字Id不能为空!", groups = {UpdateGroup.class}) + private Long permCodeId; + + /** + * 权限字标识(一般为有含义的英文字符串)。 + */ + @Schema(description = "权限字标识", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "权限字编码不能为空!") + private String permCode; + + /** + * 上级权限字Id。 + */ + @Schema(description = "上级权限字Id") + private Long parentId; + + /** + * 权限字类型(0: 表单 1: UI片段 2: 操作)。 + */ + @Schema(description = "权限字类型", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限字类型不能为空!") + @ConstDictRef(constDictClass = SysPermCodeType.class, message = "数据验证失败,权限类型为无效值!") + private Integer permCodeType; + + /** + * 显示名称。 + */ + @Schema(description = "显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "权限字显示名称不能为空!") + private String showName; + + /** + * 显示顺序(数值越小,越靠前)。 + */ + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限字显示顺序不能为空!") + private Integer showOrder; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermDto.java new file mode 100644 index 0000000..327daf2 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermDto.java @@ -0,0 +1,60 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 权限资源Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限资源Dto") +@Data +public class SysPermDto { + + /** + * 权限资源Id。 + */ + @Schema(description = "权限资源Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限Id不能为空!", groups = {UpdateGroup.class}) + private Long permId; + + /** + * 权限资源名称。 + */ + @Schema(description = "权限资源名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "权限资源名称不能为空!") + private String permName; + + /** + * shiro格式的权限字,如(upms:sysUser:add)。 + */ + @Schema(description = "权限字") + private String permCode; + + /** + * 权限所在的权限模块Id。 + */ + @Schema(description = "权限所在的权限模块Id") + @NotNull(message = "权限模块Id不能为空!") + private Long moduleId; + + /** + * 关联的URL。 + */ + @Schema(description = "关联的URL", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "权限关联的url不能为空!") + private String url; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限显示顺序不能为空!") + private Integer showOrder; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermModuleDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermModuleDto.java new file mode 100644 index 0000000..b7f23fc --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPermModuleDto.java @@ -0,0 +1,56 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysPermModuleType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 权限资源模块Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限资源模块Dto") +@Data +public class SysPermModuleDto { + + /** + * 权限模块Id。 + */ + @Schema(description = "权限模块Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限模块Id不能为空!", groups = {UpdateGroup.class}) + private Long moduleId; + + /** + * 权限模块名称。 + */ + @Schema(description = "权限模块名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "权限模块名称不能为空!") + private String moduleName; + + /** + * 上级权限模块Id。 + */ + @Schema(description = "上级权限模块Id") + private Long parentId; + + /** + * 权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @Schema(description = "权限模块类型", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "模块类型不能为空!") + @ConstDictRef(constDictClass = SysPermModuleType.class, message = "数据验证失败,权限模块类型为无效值!") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "权限模块显示顺序不能为空!") + private Integer showOrder; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPostDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPostDto.java new file mode 100644 index 0000000..6fc1791 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysPostDto.java @@ -0,0 +1,47 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 岗位Dto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "岗位Dto") +@Data +public class SysPostDto { + + /** + * 岗位Id。 + */ + @Schema(description = "岗位Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,岗位Id不能为空!", groups = {UpdateGroup.class}) + private Long postId; + + /** + * 岗位名称。 + */ + @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,岗位名称不能为空!") + private String postName; + + /** + * 岗位层级,数值越小级别越高。 + */ + @Schema(description = "岗位层级", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,岗位层级不能为空!") + private Integer postLevel; + + /** + * 是否领导岗位。 + */ + @Schema(description = "是否领导岗位", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,领导岗位不能为空!", groups = {UpdateGroup.class}) + private Boolean leaderPost; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysRoleDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysRoleDto.java new file mode 100644 index 0000000..09f4a2a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysRoleDto.java @@ -0,0 +1,33 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 角色Dto。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "角色Dto") +@Data +public class SysRoleDto { + + /** + * 角色Id。 + */ + @Schema(description = "角色Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "角色Id不能为空!", groups = {UpdateGroup.class}) + private Long roleId; + + /** + * 角色名称。 + */ + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "角色名称不能为空!") + private String roleName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysUserDto.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysUserDto.java new file mode 100644 index 0000000..e096b69 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysUserDto.java @@ -0,0 +1,104 @@ +package apelet.tenantadmin.upms.dto; + +import apelet.common.core.validator.AddGroup; +import apelet.common.core.validator.ConstDictRef; +import apelet.common.core.validator.UpdateGroup; +import apelet.tenantadmin.upms.model.constant.SysUserStatus; +import apelet.tenantadmin.upms.model.constant.SysUserType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * SysUserDto对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "SysUserDto对象") +@Data +public class SysUserDto { + + /** + * 用户Id。 + */ + @Schema(description = "用户Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,用户Id不能为空!", groups = {UpdateGroup.class}) + private Long userId; + + /** + * 登录用户名。 + */ + @Schema(description = "登录用户名", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,登录用户名不能为空!") + private String loginName; + + /** + * 用户密码。 + */ + @Schema(description = "用户密码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,用户密码不能为空!", groups = {AddGroup.class}) + private String password; + + /** + * 用户部门Id。 + */ + @Schema(description = "用户部门Id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,用户部门Id不能为空!") + private Long deptId; + + /** + * 用户显示名称。 + */ + @Schema(description = "用户显示名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "数据验证失败,用户显示名称不能为空!") + private String showName; + + /** + * 用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)。 + */ + @Schema(description = "用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)不能为空!") + @ConstDictRef(constDictClass = SysUserType.class, message = "数据验证失败,用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)为无效值!") + private Integer userType; + + /** + * 用户头像的Url。 + */ + @Schema(description = "用户头像的Url") + private String headImageUrl; + + /** + * 用户状态(0: 正常 1: 锁定)。 + */ + @Schema(description = "用户状态(0: 正常 1: 锁定)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "数据验证失败,用户状态(0: 正常 1: 锁定)不能为空!") + @ConstDictRef(constDictClass = SysUserStatus.class, message = "数据验证失败,用户状态(0: 正常 1: 锁定)为无效值!") + private Integer userStatus; + + /** + * 用户邮箱。 + */ + @Schema(description = "用户邮箱") + private String email; + + /** + * 用户手机。 + */ + @Schema(description = "用户手机") + private String mobile; + + /** + * createTime 范围过滤起始值(>=)。 + */ + @Schema(description = "createTime 范围过滤起始值(>=)") + private String createTimeStart; + + /** + * createTime 范围过滤结束值(<=)。 + */ + @Schema(description = "createTime 范围过滤结束值(<=)") + private String createTimeEnd; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPerm.java new file mode 100644 index 0000000..1195068 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPerm.java @@ -0,0 +1,108 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.common.core.util.MyCommonUtil; +import apelet.common.mobile.model.MobileEntryDataPerm; +import apelet.tenantadmin.upms.vo.SysDataPermVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 数据权限实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_data_perm") +public class SysDataPerm extends BaseModel { + + /** + * 主键Id。 + */ + @TableId(value = "data_perm_id") + private Long dataPermId; + + /** + * 显示名称。 + */ + @TableField(value = "data_perm_name") + private String dataPermName; + + /** + * 数据权限规则类型(0: 全部可见 1: 只看自己 2: 只看本部门 3: 本部门及子部门 4: 多部门及子部门 5: 自定义部门列表)。 + */ + @TableField(value = "rule_type") + private Integer ruleType; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @TableField(exist = false) + private String deptIdListString; + + @RelationManyToMany( + relationMasterIdField = "dataPermId", + relationModelClass = SysDataPermDept.class) + @TableField(exist = false) + private List dataPermDeptList; + + @RelationManyToMany( + relationMasterIdField = "dataPermId", + relationModelClass = SysDataPermMenu.class) + @TableField(exist = false) + private List dataPermMenuList; + + @RelationManyToMany( + relationMasterIdField = "dataPermId", + relationModelClass = MobileEntryDataPerm.class) + @TableField(exist = false) + private List dataPermMobileEntryList; + + @TableField(exist = false) + private String searchString; + + public void setSearchString(String searchString) { + this.searchString = MyCommonUtil.replaceSqlWildcard(searchString); + } + + @Mapper + public interface SysDataPermModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysDataPermVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "dataPermDeptList", expression = "java(mapToBean(sysDataPermVo.getDataPermDeptList(), apelet.tenantadmin.upms.model.SysDataPermDept.class))") + @Mapping(target = "dataPermMenuList", expression = "java(mapToBean(sysDataPermVo.getDataPermMenuList(), apelet.tenantadmin.upms.model.SysDataPermMenu.class))") + @Mapping(target = "dataPermMobileEntryList", expression = "java(mapToBean(sysDataPermVo.getDataPermMobileEntryList(), apelet.common.mobile.model.MobileEntryDataPerm.class))") + @Override + SysDataPerm toModel(SysDataPermVo sysDataPermVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysDataPerm 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "dataPermDeptList", expression = "java(beanToMap(sysDataPerm.getDataPermDeptList(), false))") + @Mapping(target = "dataPermMenuList", expression = "java(beanToMap(sysDataPerm.getDataPermMenuList(), false))") + @Mapping(target = "dataPermMobileEntryList", expression = "java(beanToMap(sysDataPerm.getDataPermMobileEntryList(), false))") + @Override + SysDataPermVo fromModel(SysDataPerm sysDataPerm); + } + public static final SysDataPermModelMapper INSTANCE = Mappers.getMapper(SysDataPerm.SysDataPermModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermDept.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermDept.java new file mode 100644 index 0000000..02d76c9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermDept.java @@ -0,0 +1,30 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.ToString; + +/** + * 数据权限与部门关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@ToString(of = {"deptId"}) +@TableName(value = "xy_sys_data_perm_dept") +public class SysDataPermDept { + + /** + * 数据权限Id。 + */ + @TableField(value = "data_perm_id") + private Long dataPermId; + + /** + * 关联部门Id。 + */ + @TableField(value = "dept_id") + private Long deptId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermMenu.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermMenu.java new file mode 100644 index 0000000..c300a44 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermMenu.java @@ -0,0 +1,30 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.ToString; + +/** + * 数据权限与菜单关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@ToString(of = {"menuId"}) +@TableName(value = "xy_sys_data_perm_menu") +public class SysDataPermMenu { + + /** + * 数据权限Id。 + */ + @TableField(value = "data_perm_id") + private Long dataPermId; + + /** + * 关联菜单Id。 + */ + @TableField(value = "menu_id") + private Long menuId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermUser.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermUser.java new file mode 100644 index 0000000..10d170a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDataPermUser.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 数据权限与用户关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_data_perm_user") +public class SysDataPermUser { + + /** + * 数据权限Id。 + */ + @TableField(value = "data_perm_id") + private Long dataPermId; + + /** + * 用户Id。 + */ + @TableField(value = "user_id") + private Long userId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDept.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDept.java new file mode 100644 index 0000000..c8b3e78 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDept.java @@ -0,0 +1,75 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.tenantadmin.upms.vo.SysDeptVo; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Date; + +/** + * SysDept实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_dept") +public class SysDept { + + /** + * 部门Id。 + */ + @TableId(value = "dept_id") + private Long deptId; + + /** + * 部门名称。 + */ + private String deptName; + + /** + * 显示顺序。 + */ + private Integer showOrder; + + /** + * 父部门Id。 + */ + private Long parentId; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + private Integer deletedFlag; + + /** + * 创建者Id。 + */ + private Long createUserId; + + /** + * 更新者Id。 + */ + private Long updateUserId; + + /** + * 创建时间。 + */ + private Date createTime; + + /** + * 更新时间。 + */ + private Date updateTime; + + @Mapper + public interface SysDeptModelMapper extends BaseModelMapper { + } + public static final SysDeptModelMapper INSTANCE = Mappers.getMapper(SysDeptModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptPost.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptPost.java new file mode 100644 index 0000000..884edf3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptPost.java @@ -0,0 +1,41 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 部门岗位多对多关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_dept_post") +public class SysDeptPost { + + /** + * 部门岗位Id。 + */ + @TableId(value = "dept_post_id") + private Long deptPostId; + + /** + * 部门Id。 + */ + @TableField(value = "dept_id") + private Long deptId; + + /** + * 岗位Id。 + */ + @TableField(value = "post_id") + private Long postId; + + /** + * 部门岗位显示名称。 + */ + @TableField(value = "post_show_name") + private String postShowName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptRelation.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptRelation.java new file mode 100644 index 0000000..dea14a1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDeptRelation.java @@ -0,0 +1,32 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 部门关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "xy_sys_dept_relation") +public class SysDeptRelation { + + /** + * 上级部门Id。 + */ + @TableField(value = "parent_dept_id") + private Long parentDeptId; + + /** + * 部门Id。 + */ + @TableField(value = "dept_id") + private Long deptId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenu.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenu.java new file mode 100644 index 0000000..0207b86 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenu.java @@ -0,0 +1,145 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.upms.vo.SysMenuVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 菜单实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_menu") +public class SysMenu extends BaseModel { + + /** + * 主键Id。 + */ + @TableId(value = "menu_id") + private Long menuId; + + /** + * 父菜单Id,目录菜单的父菜单为null。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 菜单显示名称。 + */ + @TableField(value = "menu_name") + private String menuName; + + /** + * 菜单类型(0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @TableField(value = "menu_type") + private Integer menuType; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @TableField(value = "form_router_name") + private String formRouterName; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @TableField(value = "online_form_id") + private Long onlineFormId; + + /** + * 在线表单菜单的权限控制类型,具体值可参考SysOnlineMenuPermType常量对象。 + */ + @TableField(value = "online_menu_perm_type") + private Integer onlineMenuPermType; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @TableField(value = "report_page_id") + private Long reportPageId; + + /** + * 仅用于在线表单的流程Id。 + */ + @TableField(value = "online_flow_entry_id") + private Long onlineFlowEntryId; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 菜单图标。 + */ + private String icon; + + /** + * 附加信息。 + */ + @TableField(value = "extra_data") + private String extraData; + + + /** + * 关闭菜单缓存 + */ + @TableField(value = "close_cache") + private Integer closeCache; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationManyToMany( + relationMasterIdField = "menuId", + relationModelClass = SysMenuPermCode.class) + @TableField(exist = false) + private List sysMenuPermCodeList; + + @Mapper + public interface SysMenuModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysMenuVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysMenuPermCodeList", expression = "java(mapToBean(sysMenuVo.getSysMenuPermCodeList(), apelet.tenantadmin.upms.model.SysMenuPermCode.class))") + @Override + SysMenu toModel(SysMenuVo sysMenuVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysMenu 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysMenuPermCodeList", expression = "java(beanToMap(sysMenu.getSysMenuPermCodeList(), false))") + @Override + SysMenuVo fromModel(SysMenu sysMenu); + } + public static final SysMenuModelMapper INSTANCE = Mappers.getMapper(SysMenu.SysMenuModelMapper.class); + /** + * 表单开发。 + */ + @TableField(value = "bill_exploit") + private String billExploit; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenuPermCode.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenuPermCode.java new file mode 100644 index 0000000..5dd8d3d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysMenuPermCode.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 菜单与权限字关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_menu_perm_code") +public class SysMenuPermCode { + + /** + * 关联菜单Id。 + */ + @TableField(value = "menu_id") + private Long menuId; + + /** + * 关联权限字Id。 + */ + @TableField(value = "perm_code_id") + private Long permCodeId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPerm.java new file mode 100644 index 0000000..9b881f2 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPerm.java @@ -0,0 +1,65 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.RelationDict; +import apelet.common.core.base.model.BaseModel; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 权限资源实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_perm") +public class SysPerm extends BaseModel { + + /** + * 权限Id。 + */ + @TableId(value = "perm_id") + private Long permId; + + /** + * 权限所在的权限模块Id。 + */ + @TableField(value = "module_id") + private Long moduleId; + + /** + * 权限名称。 + */ + @TableField(value = "perm_name") + private String permName; + + /** + * 关联的URL。 + */ + private String url; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationDict( + masterIdField = "moduleId", + slaveModelClass = SysPermModule.class, + slaveIdField = "moduleId", + slaveNameField = "moduleName") + @TableField(exist = false) + private Map moduleIdDictMap; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCode.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCode.java new file mode 100644 index 0000000..accd269 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCode.java @@ -0,0 +1,98 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.upms.vo.SysPermCodeVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 权限字实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_perm_code") +public class SysPermCode extends BaseModel { + + /** + * 主键Id。 + */ + @TableId(value = "perm_code_id") + private Long permCodeId; + + /** + * 上级权限字Id。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 权限字标识(一般为有含义的英文字符串)。 + */ + @TableField(value = "perm_code") + private String permCode; + + /** + * 权限类型(0: 表单 1: UI片段 2: 操作)。 + */ + @TableField(value = "perm_code_type") + private Integer permCodeType; + + /** + * 显示名称。 + */ + @TableField(value = "show_name") + private String showName; + + /** + * 显示顺序(数值越小,越靠前)。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationManyToMany( + relationMasterIdField = "permCodeId", + relationModelClass = SysPermCodePerm.class) + @TableField(exist = false) + private List sysPermCodePermList; + + @Mapper + public interface SysPermCodeModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysPermCodeVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysPermCodePermList", expression = "java(mapToBean(sysPermCodeVo.getSysPermCodePermList(), apelet.tenantadmin.upms.model.SysPermCodePerm.class))") + @Override + SysPermCode toModel(SysPermCodeVo sysPermCodeVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysPermCode 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysPermCodePermList", expression = "java(beanToMap(sysPermCode.getSysPermCodePermList(), false))") + @Override + SysPermCodeVo fromModel(SysPermCode sysPermCode); + } + public static final SysPermCodeModelMapper INSTANCE = Mappers.getMapper(SysPermCode.SysPermCodeModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCodePerm.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCodePerm.java new file mode 100644 index 0000000..ca25770 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermCodePerm.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 权限字与权限资源关联实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_perm_code_perm") +public class SysPermCodePerm { + + /** + * 权限字Id。 + */ + @TableField(value = "perm_code_id") + private Long permCodeId; + + /** + * 权限Id。 + */ + @TableField(value = "perm_id") + private Long permId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermModule.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermModule.java new file mode 100644 index 0000000..eebf439 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermModule.java @@ -0,0 +1,60 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.base.model.BaseModel; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 权限模块实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_perm_module") +public class SysPermModule extends BaseModel { + + /** + * 权限模块Id。 + */ + @TableId(value = "module_id") + private Long moduleId; + + /** + * 上级权限模块Id。 + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 权限模块名称。 + */ + @TableField(value = "module_name") + private String moduleName; + + /** + * 权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @TableField(value = "module_type") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @TableField(value = "show_order") + private Integer showOrder; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @TableField(exist = false) + private List sysPermList; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermWhitelist.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermWhitelist.java new file mode 100644 index 0000000..e9397f7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPermWhitelist.java @@ -0,0 +1,35 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 白名单实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_perm_whitelist") +public class SysPermWhitelist { + + /** + * 权限资源的URL。 + */ + @TableId(value = "perm_url") + private String permUrl; + + /** + * 权限资源所属模块名字(通常是Controller的名字)。 + */ + @TableField(value = "module_name") + private String moduleName; + + /** + * 权限的名称。 + */ + @TableField(value = "perm_name") + private String permName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPost.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPost.java new file mode 100644 index 0000000..d789269 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysPost.java @@ -0,0 +1,83 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.upms.vo.SysPostVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * 岗位实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_post") +public class SysPost extends BaseModel { + + /** + * 岗位Id。 + */ + @TableId(value = "post_id") + private Long postId; + + /** + * 岗位名称。 + */ + @TableField(value = "post_name") + private String postName; + + /** + * 岗位层级,数值越小级别越高。 + */ + @TableField(value = "post_level") + private Integer postLevel; + + /** + * 是否领导岗位。 + */ + @TableField(value = "leader_post") + private Boolean leaderPost; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + /** + * postId 的多对多关联表数据对象。 + */ + @TableField(exist = false) + private SysDeptPost sysDeptPost; + + @Mapper + public interface SysPostModelMapper extends BaseModelMapper { + /** + * 转换Vo对象到实体对象。 + * + * @param sysPostVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysDeptPost", expression = "java(mapToBean(sysPostVo.getSysDeptPost(), apelet.tenantadmin.upms.model.SysDeptPost.class))") + @Override + SysPost toModel(SysPostVo sysPostVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysPost 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysDeptPost", expression = "java(beanToMap(sysPost.getSysDeptPost(), false))") + @Override + SysPostVo fromModel(SysPost sysPost); + } + public static final SysPostModelMapper INSTANCE = Mappers.getMapper(SysPostModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRole.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRole.java new file mode 100644 index 0000000..79e6fae --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRole.java @@ -0,0 +1,74 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.RelationManyToMany; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.base.model.BaseModel; +import apelet.tenantadmin.upms.vo.SysRoleVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 角色实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "xy_sys_role") +public class SysRole extends BaseModel { + + /** + * 主键Id。 + */ + @TableId(value = "role_id") + private Long roleId; + + /** + * 角色名称。 + */ + @TableField(value = "role_name") + private String roleName; + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + @TableField(value = "deleted_flag") + private Integer deletedFlag; + + @RelationManyToMany( + relationMasterIdField = "roleId", + relationModelClass = SysRoleMenu.class) + @TableField(exist = false) + private List sysRoleMenuList; + + @Mapper + public interface SysRoleModelMapper extends BaseModelMapper { + /** + * 转换VO对象到实体对象。 + * + * @param sysRoleVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysRoleMenuList", expression = "java(mapToBean(sysRoleVo.getSysRoleMenuList(), apelet.tenantadmin.upms.model.SysRoleMenu.class))") + @Override + SysRole toModel(SysRoleVo sysRoleVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysRole 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysRoleMenuList", expression = "java(beanToMap(sysRole.getSysRoleMenuList(), false))") + @Override + SysRoleVo fromModel(SysRole sysRole); + } + public static final SysRoleModelMapper INSTANCE = Mappers.getMapper(SysRole.SysRoleModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRoleMenu.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRoleMenu.java new file mode 100644 index 0000000..505f43a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysRoleMenu.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色菜单实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_role_menu") +public class SysRoleMenu { + + /** + * 角色Id。 + */ + @TableField(value = "role_id") + private Long roleId; + + /** + * 菜单Id。 + */ + @TableField(value = "menu_id") + private Long menuId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUser.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUser.java new file mode 100644 index 0000000..e1ed6fb --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUser.java @@ -0,0 +1,210 @@ +package apelet.tenantadmin.upms.model; + +import apelet.common.core.annotation.*; +import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.core.upload.UploadStoreTypeEnum; +import apelet.tenantadmin.upms.model.constant.SysUserStatus; +import apelet.tenantadmin.upms.model.constant.SysUserType; +import apelet.tenantadmin.upms.vo.SysUserVo; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * SysUser实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_user") +public class SysUser { + + /** + * 用户Id。 + */ + @TableId(value = "user_id") + private Long userId; + + /** + * 登录用户名。 + */ + private String loginName; + + /** + * 用户密码。 + */ + private String password; + + /** + * 用户部门Id。 + */ + private Long deptId; + + /** + * 用户显示名称。 + */ + private String showName; + + /** + * 用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)。 + */ + private Integer userType; + + /** + * 用户头像的Url。 + */ + @UploadFlagColumn(storeType = UploadStoreTypeEnum.LOCAL_SYSTEM) + private String headImageUrl; + + /** + * 用户状态(0: 正常 1: 锁定)。 + */ + private Integer userStatus; + + /** + * 用户邮箱。 + */ + private String email; + + /** + * 用户手机。 + */ + private String mobile; + + /** + * 创建者Id。 + */ + private Long createUserId; + + /** + * 更新者Id。 + */ + private Long updateUserId; + + /** + * 创建时间。 + */ + private Date createTime; + + /** + * 更新时间。 + */ + private Date updateTime; + + /** + * 云之家id + */ + private String cloudhubId; + + /** + * 钉钉id + */ + private String dingtalkId; + + /** + * eas 用户ID + */ + private String easId; + + + + + /** + * 逻辑删除标记字段(1: 正常 -1: 已删除)。 + */ + @TableLogic + private Integer deletedFlag; + + /** + * createTime 范围过滤起始值(>=)。 + */ + @TableField(exist = false) + private String createTimeStart; + + /** + * createTime 范围过滤结束值(<=)。 + */ + @TableField(exist = false) + private String createTimeEnd; + + /** + * 多对多用户部门岗位数据集合。 + */ + @RelationManyToMany( + relationMasterIdField = "userId", + relationModelClass = SysUserPost.class) + @TableField(exist = false) + private List sysUserPostList; + + /** + * 多对多用户角色数据集合。 + */ + @RelationManyToMany( + relationMasterIdField = "userId", + relationModelClass = SysUserRole.class) + @TableField(exist = false) + private List sysUserRoleList; + + /** + * 多对多用户数据权限数据集合。 + */ + @RelationManyToMany( + relationMasterIdField = "userId", + relationModelClass = SysDataPermUser.class) + @TableField(exist = false) + private List sysDataPermUserList; + + @RelationDict( + masterIdField = "deptId", + slaveModelClass = SysDept.class, + slaveIdField = "deptId", + slaveNameField = "deptName") + @TableField(exist = false) + private Map deptIdDictMap; + + @RelationConstDict( + masterIdField = "userType", + constantDictClass = SysUserType.class) + @TableField(exist = false) + private Map userTypeDictMap; + + @RelationConstDict( + masterIdField = "userStatus", + constantDictClass = SysUserStatus.class) + @TableField(exist = false) + private Map userStatusDictMap; + + @Mapper + public interface SysUserModelMapper extends BaseModelMapper { + /** + * 转换Vo对象到实体对象。 + * + * @param sysUserVo 域对象。 + * @return 实体对象。 + */ + @Mapping(target = "sysUserRoleList", expression = "java(mapToBean(sysUserVo.getSysUserRoleList(), apelet.tenantadmin.upms.model.SysUserRole.class))") + @Mapping(target = "sysUserPostList", expression = "java(mapToBean(sysUserVo.getSysUserPostList(), apelet.tenantadmin.upms.model.SysUserPost.class))") + @Mapping(target = "sysDataPermUserList", expression = "java(mapToBean(sysUserVo.getSysDataPermUserList(), apelet.tenantadmin.upms.model.SysDataPermUser.class))") + @Override + SysUser toModel(SysUserVo sysUserVo); + /** + * 转换实体对象到VO对象。 + * + * @param sysUser 实体对象。 + * @return 域对象。 + */ + @Mapping(target = "sysUserRoleList", expression = "java(beanToMap(sysUser.getSysUserRoleList(), false))") + @Mapping(target = "sysUserPostList", expression = "java(beanToMap(sysUser.getSysUserPostList(), false))") + @Mapping(target = "sysDataPermUserList", expression = "java(beanToMap(sysUser.getSysDataPermUserList(), false))") + @Override + SysUserVo fromModel(SysUser sysUser); + } + public static final SysUserModelMapper INSTANCE = Mappers.getMapper(SysUserModelMapper.class); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserPost.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserPost.java new file mode 100644 index 0000000..fd92882 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserPost.java @@ -0,0 +1,34 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户岗位多对多关系实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_user_post") +public class SysUserPost { + + /** + * 用户Id。 + */ + @TableField(value = "user_id") + private Long userId; + + /** + * 部门岗位Id。 + */ + @TableField(value = "dept_post_id") + private Long deptPostId; + + /** + * 岗位Id。 + */ + @TableField(value = "post_id") + private Long postId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserRole.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserRole.java new file mode 100644 index 0000000..7cf1ca5 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserRole.java @@ -0,0 +1,28 @@ +package apelet.tenantadmin.upms.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户角色实体对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Data +@TableName(value = "xy_sys_user_role") +public class SysUserRole { + + /** + * 用户Id。 + */ + @TableField(value = "user_id") + private Long userId; + + /** + * 角色Id。 + */ + @TableField(value = "role_id") + private Long roleId; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysMenuType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysMenuType.java new file mode 100644 index 0000000..ad28729 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysMenuType.java @@ -0,0 +1,54 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 菜单类型常量对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysMenuType { + + /** + * 目录菜单。 + */ + public static final int TYPE_DIRECTORY = 0; + /** + * 普通菜单。 + */ + public static final int TYPE_MENU = 1; + /** + * 表单片段类型。 + */ + public static final int TYPE_UI_FRAGMENT = 2; + /** + * 按钮类型。 + */ + public static final int TYPE_BUTTON = 3; + + private static final Map DICT_MAP = new HashMap<>(4); + static { + DICT_MAP.put(TYPE_DIRECTORY, "目录菜单"); + DICT_MAP.put(TYPE_MENU, "普通菜单"); + DICT_MAP.put(TYPE_UI_FRAGMENT, "表单片段类型"); + DICT_MAP.put(TYPE_BUTTON, "按钮类型"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysMenuType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysOnlineMenuPermType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysOnlineMenuPermType.java new file mode 100644 index 0000000..e5a9556 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysOnlineMenuPermType.java @@ -0,0 +1,44 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 菜单关联在线表单的控制权限类型。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysOnlineMenuPermType { + + /** + * 查看。 + */ + public static final int TYPE_VIEW = 0; + /** + * 编辑。 + */ + public static final int TYPE_EDIT = 1; + + private static final Map DICT_MAP = new HashMap<>(4); + static { + DICT_MAP.put(TYPE_VIEW, "查看"); + DICT_MAP.put(TYPE_EDIT, "编辑"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysOnlineMenuPermType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermCodeType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermCodeType.java new file mode 100644 index 0000000..4ef92f9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermCodeType.java @@ -0,0 +1,49 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 权限字类型常量对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysPermCodeType { + + /** + * 表单权限字。 + */ + public static final int TYPE_FORM = 0; + /** + * 表单片段布局权限字。 + */ + public static final int TYPE_FRAGMENT = 1; + /** + * 操作权限字。 + */ + public static final int TYPE_OPERATION = 2; + + private static final Map DICT_MAP = new HashMap<>(3); + static { + DICT_MAP.put(TYPE_FORM, "表单权限字"); + DICT_MAP.put(TYPE_FRAGMENT, "表单片段布局权限字"); + DICT_MAP.put(TYPE_OPERATION, "操作权限字"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysPermCodeType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermModuleType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermModuleType.java new file mode 100644 index 0000000..8206f86 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysPermModuleType.java @@ -0,0 +1,44 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 权限资源模块类型常量对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysPermModuleType { + + /** + * 普通模块。 + */ + public static final int TYPE_NORMAL = 0; + /** + * controller接口模块。 + */ + public static final int TYPE_CONTROLLER = 1; + + private static final Map DICT_MAP = new HashMap<>(2); + static { + DICT_MAP.put(TYPE_NORMAL, "普通模块"); + DICT_MAP.put(TYPE_CONTROLLER, "controller接口模块"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysPermModuleType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserStatus.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserStatus.java new file mode 100644 index 0000000..729f080 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserStatus.java @@ -0,0 +1,44 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 用户状态常量字典对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysUserStatus { + + /** + * 正常状态。 + */ + public static final int STATUS_NORMAL = 0; + /** + * 锁定状态。 + */ + public static final int STATUS_LOCKED = 1; + + private static final Map DICT_MAP = new HashMap<>(2); + static { + DICT_MAP.put(STATUS_NORMAL, "正常状态"); + DICT_MAP.put(STATUS_LOCKED, "锁定状态"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysUserStatus() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserType.java new file mode 100644 index 0000000..6f598e7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/constant/SysUserType.java @@ -0,0 +1,49 @@ +package apelet.tenantadmin.upms.model.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 用户类型常量字典对象。 + * + * @author guifc + * @date 2023-08-04 + */ +public final class SysUserType { + + /** + * 管理员。 + */ + public static final int TYPE_ADMIN = 0; + /** + * 系统操作员。 + */ + public static final int TYPE_SYSTEM = 1; + /** + * 普通操作员。 + */ + public static final int TYPE_OPERATOR = 2; + + private static final Map DICT_MAP = new HashMap<>(3); + static { + DICT_MAP.put(TYPE_ADMIN, "管理员"); + DICT_MAP.put(TYPE_SYSTEM, "系统操作员"); + DICT_MAP.put(TYPE_OPERATOR, "普通操作员"); + } + + /** + * 判断参数是否为当前常量字典的合法值。 + * + * @param value 待验证的参数值。 + * @return 合法返回true,否则false。 + */ + public static boolean isValid(Integer value) { + return value != null && DICT_MAP.containsKey(value); + } + + /** + * 私有构造函数,明确标识该常量类的作用。 + */ + private SysUserType() { + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDataPermService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDataPermService.java new file mode 100644 index 0000000..4a53158 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDataPermService.java @@ -0,0 +1,118 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.upms.model.SysDataPerm; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 数据权限数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDataPermService extends IBaseService { + + /** + * 保存新增的数据权限对象。 + * + * @param dataPerm 新增的数据权限对象。 + * @param deptIdSet 关联的部门Id列表。 + * @param menuIdSet 关联的菜单Id列表。 + * @param entryIdSet 关联的移动端入口Id列表。 + * @return 新增后的数据权限对象。 + */ + SysDataPerm saveNew(SysDataPerm dataPerm, Set deptIdSet, Set menuIdSet, Set entryIdSet); + + /** + * 更新数据权限对象。 + * + * @param dataPerm 更新的数据权限对象。 + * @param originalDataPerm 原有的数据权限对象。 + * @param deptIdSet 关联的部门Id列表。 + * @param menuIdSet 关联的菜单Id列表。 + * @param entryIdSet 关联的移动端入口Id列表。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysDataPerm dataPerm, SysDataPerm originalDataPerm, Set deptIdSet, Set menuIdSet, Set entryIdSet); + + /** + * 删除指定数据权限。 + * + * @param dataPermId 数据权限主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long dataPermId); + + /** + * 获取数据权限列表及其关联数据。 + * + * @param filter 数据权限过滤对象。 + * @param orderBy 排序参数。 + * @return 数据权限查询列表。 + */ + List getSysDataPermListWithRelation(SysDataPerm filter, String orderBy); + + /** + * 将指定用户的指定会话的数据权限集合存入缓存。 + * + * @param sessionId 会话Id。 + * @param userId 用户主键Id。 + * @param deptId 用户所属部门主键Id。 + */ + void putDataPermCache(String sessionId, Long userId, Long deptId); + + /** + * 将指定会话的数据权限集合从缓存中移除。 + * + * @param sessionId 会话Id。 + */ + void removeDataPermCache(String sessionId); + + /** + * 获取指定用户Id的数据权限列表。并基于menuId和权限规则类型进行了一级分组。 + * + * @param userId 指定的用户Id。 + * @param deptId 用户所属部门主键Id。 + * @return 合并优化后的数据权限列表。返回格式为,Map>。 + */ + Map> getSysDataPermListByUserId(Long userId, Long deptId); + + /** + * 查询与指定菜单关联的数据权限列表。 + * + * @param menuId 菜单Id。 + * @return 与菜单Id关联的数据权限列表。 + */ + List getSysDataPermListByMenuId(Long menuId); + + /** + * 添加用户和数据权限之间的多对多关联关系。 + * + * @param dataPermId 数据权限Id。 + * @param userIdSet 关联的用户Id列表。 + */ + void addDataPermUserList(Long dataPermId, Set userIdSet); + + /** + * 移除用户和数据权限之间的多对多关联关系。 + * + * @param dataPermId 数据权限主键Id。 + * @param userId 用户主键Id。 + * @return true移除成功,否则false。 + */ + boolean removeDataPermUser(Long dataPermId, Long userId); + + /** + * 验证数据权限对象关联菜单数据是否都合法。 + * + * @param dataPerm 数据权限关对象。 + * @param deptIdListString 与数据权限关联的部门Id列表。 + * @param menuIdListString 与数据权限关联的菜单Id列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData(SysDataPerm dataPerm, String deptIdListString, String menuIdListString); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDeptService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDeptService.java new file mode 100644 index 0000000..294aa48 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysDeptService.java @@ -0,0 +1,199 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.upms.model.SysDept; +import apelet.tenantadmin.upms.model.SysDeptPost; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 部门管理数据操作服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysDeptService extends IBaseService { + + /** + * 保存新增的部门对象。 + * + * @param sysDept 新增的部门对象。 + * @param parentSysDept 上级部门对象。 + * @return 新增后的部门对象。 + */ + SysDept saveNew(SysDept sysDept, SysDept parentSysDept); + + /** + * 更新部门对象。 + * + * @param sysDept 更新的部门对象。 + * @param originalSysDept 原有的部门对象。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysDept sysDept, SysDept originalSysDept); + + /** + * 删除指定数据。 + * + * @param deptId 主键Id。 + * @return 成功返回true,否则false。 + */ + boolean remove(Long deptId); + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysDeptListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysDeptList(SysDept filter, String orderBy); + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysDeptListWithRelation)方法。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysDeptList(String inFilterField, Set inFilterValues, SysDept filter, String orderBy); + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysDeptList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + List getSysDeptListWithRelation(SysDept filter, String orderBy); + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 同时还包含主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysDeptList),以便获取更好的查询性能。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + List getSysDeptListWithRelation( + String inFilterField, Set inFilterValues, SysDept filter, String orderBy); + + /** + * 判断指定对象是否包含下级对象。 + * + * @param deptId 主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long deptId); + + /** + * 判断指定部门Id是否包含用户对象。 + * + * @param deptId 部门主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildrenUser(Long deptId); + + /** + * 批量添加多对多关联关系。 + * + * @param sysDeptPostList 多对多关联表对象集合。 + * @param deptId 主表Id。 + */ + void addSysDeptPostList(List sysDeptPostList, Long deptId); + + /** + * 更新中间表数据。 + * + * @param sysDeptPost 中间表对象。 + * @return 更新成功与否。 + */ + boolean updateSysDeptPost(SysDeptPost sysDeptPost); + + /** + * 移除单条多对多关系。 + * + * @param deptId 主表Id。 + * @param postId 从表Id。 + * @return 成功返回true,否则false。 + */ + boolean removeSysDeptPost(Long deptId, Long postId); + + /** + * 获取中间表数据。 + * + * @param deptId 主表Id。 + * @param postId 从表Id。 + * @return 中间表对象。 + */ + SysDeptPost getSysDeptPost(Long deptId, Long postId); + + /** + * 根据部门岗位Id获取部门岗位关联对象。 + * + * @param deptPostId 部门岗位Id。 + * @return 部门岗位对象。 + */ + SysDeptPost getSysDeptPost(Long deptPostId); + + /** + * 获取指定部门Id的部门岗位多对多关联数据列表,以及关联的部门和岗位数据。 + * + * @param deptId 部门Id。如果参数为空则返回全部数据。 + * @return 部门岗位多对多数据列表。 + */ + List> getSysDeptPostListWithRelationByDeptId(Long deptId); + + /** + * 获取指定部门Id和岗位Id集合的部门岗位多对多关联数据列表。 + * + * @param deptId 部门Id。 + * @param postIdSet 指定的岗位Id集合。 + * @return 部门岗位多对多数据列表。 + */ + List getSysDeptPostList(Long deptId, Set postIdSet); + + /** + * 获取与指定部门Id同级部门和岗位Id集合的部门岗位多对多关联数据列表。 + * + * @param deptId 部门Id。 + * @param postIdSet 指定的岗位Id集合。 + * @return 部门岗位多对多数据列表。 + */ + List getSiblingSysDeptPostList(Long deptId, Set postIdSet); + + /** + * 根据部门Id获取该部门领导岗位的部门岗位Id集合。 + * + * @param deptId 部门Id。 + * @return 部门领导岗位的部门岗位Id集合。 + */ + List getLeaderDeptPostIdList(Long deptId); + + /** + * 根据部门Id获取上级部门领导岗位的部门岗位Id集合。 + * + * @param deptId 部门Id。 + * @return 上级部门领导岗位的部门岗位Id集合。 + */ + List getUpLeaderDeptPostIdList(Long deptId); + + /** + * 根据父主键Id列表,获取当前部门Id及其所有下级部门Id列表。 + * + * @param parentIds 父主键Id列表。 + * @return 获取当前部门Id及其所有下级部门Id列表。 + */ + List getAllChildDeptIdByParentIds(List parentIds); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysMenuService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysMenuService.java new file mode 100644 index 0000000..ba0f1ae --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysMenuService.java @@ -0,0 +1,130 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.upms.model.SysMenu; + +import java.util.*; + +/** + * 菜单数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysMenuService extends IBaseService { + + /** + * 保存新增的菜单对象。 + * + * @param sysMenu 新增的菜单对象。 + * @param permCodeIdSet 权限字Id列表。 + * @return 新增后的菜单对象。 + */ + SysMenu saveNew(SysMenu sysMenu, Set permCodeIdSet); + + /** + * 更新菜单对象。 + * + * @param sysMenu 更新的菜单对象。 + * @param originalSysMenu 原有的菜单对象。 + * @param permCodeIdSet 权限字Id列表。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysMenu sysMenu, SysMenu originalSysMenu, Set permCodeIdSet); + + /** + * 删除指定的菜单。 + * + * @param menu 菜单对象。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(SysMenu menu); + + /** + * 获取指定用户Id的菜单列表,已去重。 + * + * @param userId 用户主键Id。 + * @return 用户关联的菜单列表。 + */ + Collection getMenuListByUserId(Long userId); + + /** + * 查询角色Id列表下的所有菜单和与其关联的权限资源url。 + * + * @param roleIds 角色Id列表。 + * @return 角色Id列表下的所有菜单和与其关联的权限资源url。 + */ + List> getMenuAndPermListByRoleIds(List roleIds); + + /** + * 判断当前菜单是否存在子菜单。 + * + * @param menuId 菜单主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long menuId); + + /** + * 验证菜单对象关联的数据是否都合法。 + * + * @param sysMenu 当前操作的对象。 + * @param originalSysMenu 原有对象。 + * @param permCodeIdListString 逗号分隔的权限Id列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData(SysMenu sysMenu, SysMenu originalSysMenu, String permCodeIdListString); + + /** + * 查询菜单的权限资源地址列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param url 权限资源地址过滤条件。 + * @return 包含从菜单到权限资源的权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail(Long menuId, String url); + + /** + * 查询菜单的用户列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param loginName 登录名。 + * @return 包含从菜单到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail(Long menuId, String loginName); + + /** + * 获取指定类型的所有在线表单的菜单。 + * + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + List getAllOnlineMenuList(Integer menuType); + + /** + * 获取当前用户有权访问的在线表单菜单。 + * + * @param userId 指定的用户。 + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + List getOnlineMenuListByUserId(Long userId, Integer menuType); + + /** + * 获取当前用户有权访问的在线统计表单菜单。 + * + * @param userId 指定的用户。 + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + List getReportMenuListByUserId(Long userId, int menuType); + + + /** + * 根据页面id列表,获取菜单数据列表。 + * + * @param pageIdList 页面id列表 + * @return 菜单数据列表。 + */ + List getMenuListByPageIds(List pageIdList); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermCodeService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermCodeService.java new file mode 100644 index 0000000..8c68566 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermCodeService.java @@ -0,0 +1,94 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.upms.model.SysPermCode; + +import java.util.*; + +/** + * 权限字数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermCodeService extends IBaseService { + + /** + * 获取指定用户的权限字列表,已去重。 + * + * @param userId 用户主键Id。 + * @return 用户关联的权限字列表。 + */ + Collection getPermCodeListByUserId(Long userId); + + /** + * 获取所有权限字数据列表,已去重。 + * + * @return 全部权限字列表。 + */ + Collection getAllPermCodeList(); + + /** + * 保存新增的权限字对象。 + * + * @param sysPermCode 新增的权限字对象。 + * @param permIdSet 权限资源Id列表。 + * @return 新增后的权限字对象。 + */ + SysPermCode saveNew(SysPermCode sysPermCode, Set permIdSet); + + /** + * 更新权限字对象。 + * + * @param sysPermCode 更新的权限字对象。 + * @param originalSysPermCode 原有的权限字对象。 + * @param permIdSet 权限资源Id列表。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysPermCode sysPermCode, SysPermCode originalSysPermCode, Set permIdSet); + + /** + * 删除指定的权限字。 + * + * @param permCodeId 权限字主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long permCodeId); + + /** + * 判断当前权限字是否存在下级权限字对象。 + * + * @param permCodeId 权限字主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long permCodeId); + + /** + * 验证权限字对象关联的数据是否都合法。 + * + * @param sysPermCode 当前操作的对象。 + * @param originalSysPermCode 原有对象。 + * @param permIdListString 逗号分隔的权限资源Id列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData(SysPermCode sysPermCode, SysPermCode originalSysPermCode, String permIdListString); + + /** + * 查询权限字的用户列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param loginName 登录名。 + * @return 包含从权限字到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail(Long permCodeId, String loginName); + + /** + * 查询权限字的角色列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param roleName 角色名。 + * @return 包含从权限字到角色的权限分配路径信息的查询结果列表。 + */ + List> getSysRoleListWithDetail(Long permCodeId, String roleName); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermModuleService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermModuleService.java new file mode 100644 index 0000000..d8a5ec8 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermModuleService.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.upms.model.SysPermModule; + +import java.util.List; + +/** + * 权限资源模块数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermModuleService extends IBaseService { + + /** + * 保存新增的权限资源模块对象。 + * + * @param sysPermModule 新增的权限资源模块对象。 + * @return 新增后的权限资源模块对象。 + */ + SysPermModule saveNew(SysPermModule sysPermModule); + + /** + * 更新权限资源模块对象。 + * + * @param sysPermModule 更新的权限资源模块对象。 + * @param originalSysPermModule 原有的权限资源模块对象。 + * @return 更新成功返回true,否则false + */ + boolean update(SysPermModule sysPermModule, SysPermModule originalSysPermModule); + + /** + * 删除指定的权限资源模块。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long moduleId); + + /** + * 获取权限模块资源及其关联的权限资源列表。 + * + * @return 权限资源模块及其关联的权限资源列表。 + */ + List getPermModuleAndPermList(); + + /** + * 判断是否存在下级权限资源模块。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasChildren(Long moduleId); + + /** + * 判断是否存在权限数据。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + boolean hasModulePerms(Long moduleId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermService.java new file mode 100644 index 0000000..cae5788 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermService.java @@ -0,0 +1,99 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.upms.model.SysPerm; + +import java.util.*; + +/** + * 权限资源数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermService extends IBaseService { + + /** + * 保存新增的权限资源对象。 + * + * @param perm 新增的权限资源对象。 + * @return 新增后的权限资源对象。 + */ + SysPerm saveNew(SysPerm perm); + + /** + * 更新权限资源对象。 + * + * @param perm 更新的权限资源对象。 + * @param originalPerm 原有的权限资源对象。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysPerm perm, SysPerm originalPerm); + + /** + * 删除权限资源。 + * + * @param permId 权限资源主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long permId); + + /** + * 获取权限数据列表。 + * + * @param sysPermFilter 过滤对象。 + * @return 权限列表。 + */ + List getPermListWithRelation(SysPerm sysPermFilter); + + /** + * 将指定用户的指定会话的权限集合存入缓存。 + * + * @param sessionId 会话Id。 + * @param userId 用户主键Id。 + * @param permUrlSet URL集合。 + */ + void putUserSysPermCache(String sessionId, Long userId, Set permUrlSet); + + /** + * 将指定会话的权限集合从缓存中移除。 + * + * @param sessionId 会话Id。 + */ + void removeUserSysPermCache(String sessionId); + + /** + * 获取与指定用户关联的权限资源列表,已去重。 + * + * @param userId 关联的用户主键Id。 + * @return 与指定用户Id关联的权限资源列表。 + */ + Collection getPermListByUserId(Long userId); + + /** + * 查询权限资源地址的用户列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param loginName 登录名。 + * @return 包含从权限资源到用户的完整权限分配路径信息的查询结果列表。 + */ + List> getSysUserListWithDetail(Long permId, String loginName); + + /** + * 查询权限资源地址的角色列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param roleName 角色名。 + * @return 包含从权限资源到角色的权限分配路径信息的查询结果列表。 + */ + List> getSysRoleListWithDetail(Long permId, String roleName); + + /** + * 查询权限资源地址的菜单列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param menuName 菜单名。 + * @return 包含从权限资源到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysMenuListWithDetail(Long permId, String menuName); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermWhitelistService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermWhitelistService.java new file mode 100644 index 0000000..a1c9791 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPermWhitelistService.java @@ -0,0 +1,23 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.upms.model.SysPermWhitelist; + +import java.util.List; + +/** + * 权限资源白名单数据服务接口。 + * 白名单中的权限资源,可以不受权限控制,任何用户皆可访问,一般用于常用的字典数据列表接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPermWhitelistService extends IBaseService { + + /** + * 获取白名单权限资源的列表。 + * + * @return 白名单权限资源地址列表。 + */ + List getWhitelistPermList(); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPostService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPostService.java new file mode 100644 index 0000000..b69bbea --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysPostService.java @@ -0,0 +1,100 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.tenantadmin.upms.model.SysPost; +import apelet.tenantadmin.upms.model.SysUserPost; + +import java.util.List; +import java.util.Set; + +/** + * 岗位管理数据操作服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysPostService extends IBaseService { + + /** + * 保存新增对象。 + * + * @param sysPost 新增对象。 + * @return 返回新增对象。 + */ + SysPost saveNew(SysPost sysPost); + + /** + * 更新数据对象。 + * + * @param sysPost 更新的对象。 + * @param originalSysPost 原有数据对象。 + * @return 成功返回true,否则false。 + */ + boolean update(SysPost sysPost, SysPost originalSysPost); + + /** + * 删除指定数据。 + * + * @param postId 主键Id。 + * @return 成功返回true,否则false。 + */ + boolean remove(Long postId); + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysPostListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysPostList(SysPost filter, String orderBy); + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。 + * 如果仅仅需要获取主表数据,请移步(getSysPostList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysPostListWithRelation(SysPost filter, String orderBy); + + /** + * 在多对多关系中,当前Service的数据表为从表,返回不与指定主表主键Id存在对多对关系的列表。 + * + * @param deptId 主表主键Id。 + * @param filter 从表的过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getNotInSysPostListByDeptId(Long deptId, SysPost filter, String orderBy); + + /** + * 获取指定部门的岗位列表。 + * + * @param deptId 部门Id。 + * @param filter 从表的过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysPostListByDeptId(Long deptId, SysPost filter, String orderBy); + + /** + * 获取指定用户的用户岗位多对多关联数据列表。 + * + * @param userId 用户Id。 + * @return 用户岗位多对多关联数据列表。 + */ + List getSysUserPostListByUserId(Long userId); + + /** + * 判断指定的部门岗位Id集合是否都属于指定的部门Id。 + * + * @param deptPostIdSet 部门岗位Id集合。 + * @param deptId 部门Id。 + * @return 全部是返回true,否则false。 + */ + boolean existAllPrimaryKeys(Set deptPostIdSet, Long deptId); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysRoleService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysRoleService.java new file mode 100644 index 0000000..1390139 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysRoleService.java @@ -0,0 +1,107 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.tenantadmin.upms.model.SysRole; +import apelet.tenantadmin.upms.model.SysUserRole; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 角色数据服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysRoleService extends IBaseService { + + /** + * 保存新增的角色对象。 + * + * @param role 新增的角色对象。 + * @param menuIdSet 菜单Id列表。 + * @return 新增后的角色对象。 + */ + SysRole saveNew(SysRole role, Set menuIdSet); + + /** + * 更新角色对象。 + * + * @param role 更新的角色对象。 + * @param originalRole 原有的角色对象。 + * @param menuIdSet 菜单Id列表。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysRole role, SysRole originalRole, Set menuIdSet); + + /** + * 删除指定角色。 + * + * @param roleId 角色主键Id。 + * @return 删除成功返回true,否则false。 + */ + boolean remove(Long roleId); + + /** + * 获取角色列表。 + * + * @param filter 角色过滤对象。 + * @param orderBy 排序参数。 + * @return 角色列表。 + */ + List getSysRoleList(SysRole filter, String orderBy); + + /** + * 获取用户的用户角色对象列表。 + * + * @param userId 用户Id。 + * @return 用户角色对象列表。 + */ + List getSysUserRoleListByUserId(Long userId); + + /** + * 批量新增用户角色关联。 + * + * @param userRoleList 用户角色关系数据列表。 + */ + void addUserRoleList(List userRoleList); + + /** + * 移除指定用户和指定角色的关联关系。 + * + * @param roleId 角色主键Id。 + * @param userId 用户主键Id。 + * @return 移除成功返回true,否则false。 + */ + boolean removeUserRole(Long roleId, Long userId); + + /** + * 验证角色对象关联的数据是否都合法。 + * + * @param sysRole 当前操作的对象。 + * @param originalSysRole 原有对象。 + * @param menuIdListString 逗号分隔的menuId列表。 + * @return 验证结果。 + */ + CallResult verifyRelatedData(SysRole sysRole, SysRole originalSysRole, String menuIdListString); + + /** + * 查询角色的权限资源地址列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param url url过滤条件。 + * @return 包含从角色到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail(Long roleId, String url); + + /** + * 查询角色的权限字列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从角色到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysPermCodeListWithDetail(Long roleId, String permCode); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java new file mode 100644 index 0000000..1d528e6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java @@ -0,0 +1,256 @@ +package apelet.tenantadmin.upms.service; + +import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.ResponseResult; +import apelet.tenantadmin.upms.model.SysUser; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 用户管理数据操作服务接口。 + * + * @author guifc + * @date 2023-08-04 + */ +public interface SysUserService extends IBaseService { + + /** + * 获取指定登录名的用户对象。 + * + * @param loginName 指定登录用户名。 + * @return 用户对象。 + */ + SysUser getSysUserByLoginName(String loginName); + + /** + * 保存新增的用户对象。 + * + * @param user 新增的用户对象。 + * @param roleIdSet 用户角色Id集合。 + * @param deptPostIdSet 部门岗位Id集合。 + * @param dataPermIdSet 数据权限Id集合。 + * @return 新增后的用户对象。 + */ + SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet); + + /** + * 更新用户对象。 + * + * @param user 更新的用户对象。 + * @param originalUser 原有的用户对象。 + * @param roleIdSet 用户角色Id列表。 + * @param deptPostIdSet 部门岗位Id集合。 + * @param dataPermIdSet 数据权限Id集合。 + * @return 更新成功返回true,否则false。 + */ + boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet); + + /** + * 修改用户密码。 + * @param userId 用户主键Id。 + * @param newPass 新密码。 + * @return 成功返回true,否则false。 + */ + boolean changePassword(Long userId, String newPass); + + /** + * 修改用户头像。 + * + * @param userId 用户主键Id。 + * @param newHeadImage 新的头像信息。 + * @return 成功返回true,否则false。 + */ + boolean changeHeadImage(Long userId, String newHeadImage); + + /** + * 删除指定数据。 + * + * @param userId 主键Id。 + * @return 成功返回true,否则false。 + */ + boolean remove(Long userId); + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysUserListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysUserList(SysUser filter, String orderBy); + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysUserListWithRelation)方法。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + List getSysUserList(String inFilterField, Set inFilterValues, SysUser filter, String orderBy); + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysUserList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + List getSysUserListWithRelation(SysUser filter, String orderBy); + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 同时还包含主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysUserList),以便获取更好的查询性能。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + List getSysUserListWithRelation( + String inFilterField, Set inFilterValues, SysUser filter, String orderBy); + + /** + * 获取指定角色的用户列表。 + * + * @param roleId 角色主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getSysUserListByRoleId(Long roleId, SysUser filter, String orderBy); + + /** + * 获取不属于指定角色的用户列表。 + * + * @param roleId 角色主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getNotInSysUserListByRoleId(Long roleId, SysUser filter, String orderBy); + + /** + * 获取指定数据权限的用户列表。 + * + * @param dataPermId 数据权限主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getSysUserListByDataPermId(Long dataPermId, SysUser filter, String orderBy); + + /** + * 获取不属于指定数据权限的用户列表。 + * + * @param dataPermId 数据权限主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getNotInSysUserListByDataPermId(Long dataPermId, SysUser filter, String orderBy); + + /** + * 获取指定部门岗位的用户列表。 + * + * @param deptPostId 部门岗位主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getSysUserListByDeptPostId(Long deptPostId, SysUser filter, String orderBy); + + /** + * 获取不属于指定部门岗位的用户列表。 + * + * @param deptPostId 部门岗位主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getNotInSysUserListByDeptPostId(Long deptPostId, SysUser filter, String orderBy); + + /** + * 获取指定岗位的用户列表。 + * + * @param postId 岗位主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + List getSysUserListByPostId(Long postId, SysUser filter, String orderBy); + + + /** + * 查询用户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param url url过滤条件。 + * @return 包含从用户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + List> getSysPermListWithDetail(Long userId, String url); + + /** + * 查询用户的权限字列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从用户到权限字的权限分配路径信息的查询结果列表。 + */ + List> getSysPermCodeListWithDetail(Long userId, String permCode); + + /** + * 查询用户的菜单列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从用户到菜单的权限分配路径信息的查询结果列表。 + */ + List> getSysMenuListWithDetail(Long userId, String menuName); + + /** + * 验证用户对象关联的数据是否都合法。 + * + * @param sysUser 当前操作的对象。 + * @param originalSysUser 原有对象。 + * @param roleIds 逗号分隔的角色Id列表字符串。 + * @param deptPostIds 逗号分隔的部门岗位Id列表字符串。 + * @param dataPermIds 逗号分隔的数据权限Id列表字符串。 + * @return 验证结果。 + */ + CallResult verifyRelatedData( + SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds); + + /** + * 根据云之家openId获取当前用户信息 + * + * @param openId 云之家用户openId + * @return + */ + SysUser getUserByHubOpenId(String openId); + /** + * 根据钉钉userId(暂时使用userid,后续可能修改)获取当前用户信息 + * + * @param userId 钉钉用户userId + * @return + */ + SysUser getUserByDingTalkOpenId(String userId); + + + + boolean mySaveBatch(List sysUserList); + + boolean myUpdateBatch(List sysUserList); + + ResponseResult syncEasUSer(); +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDataPermServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDataPermServiceImpl.java new file mode 100644 index 0000000..96fa706 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDataPermServiceImpl.java @@ -0,0 +1,386 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.DataPermRuleType; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.RedisKeyUtil; +import apelet.common.mobile.dao.MobileEntryDataPermMapper; +import apelet.common.mobile.model.MobileEntryDataPerm; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.*; +import apelet.tenantadmin.upms.model.*; +import apelet.tenantadmin.upms.service.*; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RBucket; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 数据权限数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysDataPermService") +public class SysDataPermServiceImpl extends BaseService implements SysDataPermService { + + @Autowired + private SysDataPermMapper sysDataPermMapper; + @Autowired + private SysDataPermDeptMapper sysDataPermDeptMapper; + @Autowired + private SysDataPermUserMapper sysDataPermUserMapper; + @Autowired + private SysDataPermMenuMapper sysDataPermMenuMapper; + @Autowired + private SysUserService sysUserService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysMenuService sysMenuService; + @Autowired + private RedissonClient redissonClient; + @Autowired + private ApplicationConfig applicationConfig; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private MobileEntryDataPermMapper mobileEntryDataPermMapper; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysDataPermMapper; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public SysDataPerm saveNew(SysDataPerm dataPerm, Set deptIdSet, Set menuIdSet, Set entryIdSet) { + dataPerm.setDataPermId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(dataPerm); + dataPerm.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysDataPermMapper.insert(dataPerm); + this.insertRelationData(dataPerm, deptIdSet, menuIdSet, entryIdSet); + return dataPerm; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update( + SysDataPerm dataPerm, SysDataPerm originalDataPerm, Set deptIdSet, Set menuIdSet, Set entryIdSet) { + MyModelUtil.fillCommonsForUpdate(dataPerm, originalDataPerm); + UpdateWrapper uw = this.createUpdateQueryForNullValue(dataPerm, dataPerm.getDataPermId()); + if (sysDataPermMapper.update(dataPerm, uw) != 1) { + return false; + } + SysDataPermDept dataPermDept = new SysDataPermDept(); + dataPermDept.setDataPermId(dataPerm.getDataPermId()); + sysDataPermDeptMapper.delete(new QueryWrapper<>(dataPermDept)); + SysDataPermMenu dataPermMenu = new SysDataPermMenu(); + dataPermMenu.setDataPermId(dataPerm.getDataPermId()); + sysDataPermMenuMapper.delete(new QueryWrapper<>(dataPermMenu)); + MobileEntryDataPerm mobileEntryDataPerm = new MobileEntryDataPerm(); + mobileEntryDataPerm.setDataPermId(dataPerm.getDataPermId()); + mobileEntryDataPermMapper.delete(new QueryWrapper<>(mobileEntryDataPerm)); + this.insertRelationData(dataPerm, deptIdSet, menuIdSet, entryIdSet); + return true; + } + + /** + * 删除指定数据权限。 + * + * @param dataPermId 数据权限主键Id。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long dataPermId) { + if (sysDataPermMapper.deleteById(dataPermId) != 1) { + return false; + } + SysDataPermDept dataPermDept = new SysDataPermDept(); + dataPermDept.setDataPermId(dataPermId); + sysDataPermDeptMapper.delete(new QueryWrapper<>(dataPermDept)); + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setDataPermId(dataPermId); + sysDataPermUserMapper.delete(new QueryWrapper<>(dataPermUser)); + SysDataPermMenu dataPermMenu = new SysDataPermMenu(); + dataPermMenu.setDataPermId(dataPermId); + sysDataPermMenuMapper.delete(new QueryWrapper<>(dataPermMenu)); + MobileEntryDataPerm mobileEntryDataPerm = new MobileEntryDataPerm(); + mobileEntryDataPerm.setDataPermId(dataPermId); + mobileEntryDataPermMapper.delete(new QueryWrapper<>(mobileEntryDataPerm)); + return true; + } + + @Override + public List getSysDataPermListWithRelation(SysDataPerm filter, String orderBy) { + List resultList = sysDataPermMapper.getSysDataPermList(filter, orderBy); + buildRelationForDataList(resultList, MyRelationParam.full(), CollUtil.newHashSet("dataPermDeptList")); + return resultList; + } + + @Override + public void putDataPermCache(String sessionId, Long userId, Long deptId) { + Map> menuDataPermMap = getSysDataPermListByUserId(userId, deptId); + if (menuDataPermMap.size() > 0) { + String dataPermSessionKey = RedisKeyUtil.makeSessionDataPermIdKey(sessionId); + RBucket bucket = redissonClient.getBucket(dataPermSessionKey); + bucket.set(JSON.toJSONString(menuDataPermMap), + applicationConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + } + + /** + * 将指定会话的数据权限集合从缓存中移除。 + * + * @param sessionId 会话Id。 + */ + @Override + public void removeDataPermCache(String sessionId) { + String sessionPermKey = RedisKeyUtil.makeSessionDataPermIdKey(sessionId); + redissonClient.getBucket(sessionPermKey).deleteAsync(); + } + + @Override + public Map> getSysDataPermListByUserId(Long userId, Long deptId) { + List dataPermList = sysDataPermMapper.getSysDataPermListByUserId(userId); + dataPermList.forEach(dataPerm -> { + if (CollUtil.isNotEmpty(dataPerm.getDataPermDeptList())) { + Set deptIdSet = dataPerm.getDataPermDeptList().stream() + .map(SysDataPermDept::getDeptId).collect(Collectors.toSet()); + dataPerm.setDeptIdListString(StrUtil.join(",", deptIdSet)); + } + }); + Map> menuIdMap = new HashMap<>(4); + for (SysDataPerm dataPerm : dataPermList) { + if (CollUtil.isNotEmpty(dataPerm.getDataPermMenuList()) + || CollUtil.isNotEmpty(dataPerm.getDataPermMobileEntryList())) { + for (SysDataPermMenu dataPermMenu : dataPerm.getDataPermMenuList()) { + menuIdMap.computeIfAbsent( + dataPermMenu.getMenuId().toString(), k -> new LinkedList<>()).add(dataPerm); + } + } else { + menuIdMap.computeIfAbsent( + ApplicationConstant.DATA_PERM_ALL_MENU_ID, k -> new LinkedList<>()).add(dataPerm); + } + } + Map> menuResultMap = new HashMap<>(menuIdMap.size()); + for (Map.Entry> entry : menuIdMap.entrySet()) { + Map resultMap = this.mergeAndOptimizeDataPermRule(entry.getValue(), deptId); + menuResultMap.put(entry.getKey(), resultMap); + } + return menuResultMap; + } + + @Override + public List getSysDataPermListByMenuId(Long menuId) { + return sysDataPermMapper.getSysDataPermListByMenuId(menuId); + } + + private Map mergeAndOptimizeDataPermRule(List dataPermList, Long deptId) { + // 为了更方便进行后续的合并优化处理,这里再基于菜单Id和规则类型进行分组。ruleMap的key是规则类型。 + Map> ruleMap = + dataPermList.stream().collect(Collectors.groupingBy(SysDataPerm::getRuleType)); + Map resultMap = new HashMap<>(ruleMap.size()); + // 如有有ALL存在,就可以直接退出了,没有必要在处理后续的规则了。 + if (ruleMap.containsKey(DataPermRuleType.TYPE_ALL)) { + resultMap.put(DataPermRuleType.TYPE_ALL, "null"); + return resultMap; + } + // 这里优先合并最复杂的多部门及子部门场景。 + String deptIds = processMultiDeptAndChildren(ruleMap, deptId); + if (deptIds != null) { + resultMap.put(DataPermRuleType.TYPE_MULTI_DEPT_AND_CHILD_DEPT, deptIds); + } + // 合并当前部门及子部门的优化 + if (ruleMap.get(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT) != null) { + // 需要与仅仅当前部门规则进行合并。 + ruleMap.remove(DataPermRuleType.TYPE_DEPT_ONLY); + resultMap.put(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT, "null"); + } + // 合并自定义部门了。 + deptIds = processMultiDept(ruleMap, deptId); + if (deptIds != null) { + resultMap.put(DataPermRuleType.TYPE_CUSTOM_DEPT_LIST, deptIds); + } + // 最后处理当前部门和当前用户。 + if (ruleMap.get(DataPermRuleType.TYPE_DEPT_ONLY) != null) { + resultMap.put(DataPermRuleType.TYPE_DEPT_ONLY, "null"); + } + if (ruleMap.get(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT_USERS) != null) { + // 合并当前部门用户和当前用户 + ruleMap.remove(DataPermRuleType.TYPE_USER_ONLY); + ruleMap.remove(DataPermRuleType.TYPE_DEPT_USERS); + SysUser filter = new SysUser(); + filter.setDeptId(deptId); + List userList = sysUserService.getSysUserList(filter, null); + Set userIdSet = userList.stream().map(SysUser::getUserId).collect(Collectors.toSet()); + resultMap.put(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT_USERS, CollUtil.join(userIdSet, ",")); + } + if (ruleMap.get(DataPermRuleType.TYPE_DEPT_USERS) != null) { + SysUser filter = new SysUser(); + filter.setDeptId(deptId); + List userList = sysUserService.getListByFilter(filter); + Set userIdSet = userList.stream().map(SysUser::getUserId).collect(Collectors.toSet()); + // 合并仅当前用户 + ruleMap.remove(DataPermRuleType.TYPE_USER_ONLY); + resultMap.put(DataPermRuleType.TYPE_DEPT_USERS, CollUtil.join(userIdSet, ",")); + } + if (ruleMap.get(DataPermRuleType.TYPE_USER_ONLY) != null) { + resultMap.put(DataPermRuleType.TYPE_USER_ONLY, "null"); + } + return resultMap; + } + + private String processMultiDeptAndChildren(Map> ruleMap, Long deptId) { + List parentDeptList = ruleMap.get(DataPermRuleType.TYPE_MULTI_DEPT_AND_CHILD_DEPT); + if (parentDeptList == null) { + return null; + } + Set deptIdSet = new HashSet<>(); + for (SysDataPerm parentDept : parentDeptList) { + deptIdSet.addAll(StrUtil.split(parentDept.getDeptIdListString(), ',') + .stream().map(Long::valueOf).collect(Collectors.toSet())); + } + // 在合并所有的多父部门Id之后,需要判断是否有本部门及子部门的规则。如果有,就继续合并。 + if (ruleMap.containsKey(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT)) { + // 如果多父部门列表中包含当前部门,那么可以直接删除该规则了,如果没包含,就加入到多部门的DEPT_ID的IN LIST中。 + deptIdSet.add(deptId); + ruleMap.remove(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT); + } + // 需要与仅仅当前部门规则进行合并。 + if (ruleMap.containsKey(DataPermRuleType.TYPE_DEPT_ONLY) && deptIdSet.contains(deptId)) { + ruleMap.remove(DataPermRuleType.TYPE_DEPT_ONLY); + } + return StrUtil.join(",", deptIdSet); + } + + private String processMultiDept(Map> ruleMap, Long deptId) { + List customDeptList = ruleMap.get(DataPermRuleType.TYPE_CUSTOM_DEPT_LIST); + if (customDeptList == null) { + return null; + } + Set deptIdSet = new HashSet<>(); + for (SysDataPerm customDept : customDeptList) { + deptIdSet.addAll(StrUtil.split(customDept.getDeptIdListString(), ',') + .stream().map(Long::valueOf).collect(Collectors.toSet())); + } + if (ruleMap.containsKey(DataPermRuleType.TYPE_DEPT_ONLY)) { + deptIdSet.add(deptId); + ruleMap.remove(DataPermRuleType.TYPE_DEPT_ONLY); + } + return StrUtil.join(",", deptIdSet); + } + + /** + * 添加用户和数据权限之间的多对多关联关系。 + * + * @param dataPermId 数据权限Id。 + * @param userIdSet 关联的用户Id列表。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void addDataPermUserList(Long dataPermId, Set userIdSet) { + for (Long userId : userIdSet) { + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setDataPermId(dataPermId); + dataPermUser.setUserId(userId); + sysDataPermUserMapper.insert(dataPermUser); + } + } + + /** + * 移除用户和数据权限之间的多对多关联关系。 + * + * @param dataPermId 数据权限主键Id。 + * @param userId 用户主键Id。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean removeDataPermUser(Long dataPermId, Long userId) { + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setDataPermId(dataPermId); + dataPermUser.setUserId(userId); + return sysDataPermUserMapper.delete(new QueryWrapper<>(dataPermUser)) == 1; + } + + @Override + public CallResult verifyRelatedData(SysDataPerm dataPerm, String deptIdListString, String menuIdListString) { + JSONObject jsonObject = new JSONObject(); + if (dataPerm.getRuleType() == DataPermRuleType.TYPE_MULTI_DEPT_AND_CHILD_DEPT + || dataPerm.getRuleType() == DataPermRuleType.TYPE_CUSTOM_DEPT_LIST) { + if (StrUtil.isBlank(deptIdListString)) { + return CallResult.error("数据验证失败,部门列表不能为空!"); + } + Set deptIdSet = StrUtil.split( + deptIdListString, ",").stream().map(Long::valueOf).collect(Collectors.toSet()); + if (!sysDeptService.existAllPrimaryKeys(deptIdSet)) { + return CallResult.error("数据验证失败,存在不合法的部门数据,请刷新后重试!"); + } + jsonObject.put("deptIdSet", deptIdSet); + } + if (StrUtil.isNotBlank(menuIdListString)) { + Set menuIdSet = StrUtil.split( + menuIdListString, ",").stream().map(Long::valueOf).collect(Collectors.toSet()); + if (!sysMenuService.existAllPrimaryKeys(menuIdSet)) { + return CallResult.error("数据验证失败,存在不合法的菜单数据,请刷新后重试!"); + } + jsonObject.put("menuIdSet", menuIdSet); + } + return CallResult.ok(jsonObject); + } + + private void insertRelationData(SysDataPerm dataPerm, Set deptIdSet, Set menuIdSet, Set entryIdSet) { + if (CollUtil.isNotEmpty(deptIdSet)) { + for (Long deptId : deptIdSet) { + SysDataPermDept dataPermDept = new SysDataPermDept(); + dataPermDept.setDataPermId(dataPerm.getDataPermId()); + dataPermDept.setDeptId(deptId); + sysDataPermDeptMapper.insert(dataPermDept); + } + } + if (CollUtil.isNotEmpty(menuIdSet)) { + for (Long menuId : menuIdSet) { + SysDataPermMenu dataPermMenu = new SysDataPermMenu(); + dataPermMenu.setDataPermId(dataPerm.getDataPermId()); + dataPermMenu.setMenuId(menuId); + sysDataPermMenuMapper.insert(dataPermMenu); + } + } + if (CollUtil.isNotEmpty(entryIdSet)) { + for (Long entryId : entryIdSet) { + MobileEntryDataPerm mobileEntryDataPerm = new MobileEntryDataPerm(); + mobileEntryDataPerm.setDataPermId(dataPerm.getDataPermId()); + mobileEntryDataPerm.setEntryId(entryId); + mobileEntryDataPermMapper.insert(mobileEntryDataPerm); + } + } + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDeptServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..7493696 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,435 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.ext.base.BizWidgetDatasource; +import apelet.common.ext.constant.BizWidgetDatasourceType; +import apelet.common.ext.util.BizWidgetDatasourceExtHelper; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.upms.dao.*; +import apelet.tenantadmin.upms.model.*; +import apelet.tenantadmin.upms.service.SysDeptService; +import apelet.tenantadmin.upms.service.SysUserService; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 部门管理数据操作服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@Service("sysDeptService") +public class SysDeptServiceImpl extends BaseService implements SysDeptService, BizWidgetDatasource { + + @Autowired + private SysDeptMapper sysDeptMapper; + @Autowired + private SysDeptRelationMapper sysDeptRelationMapper; + @Autowired + private SysUserService sysUserService; + @Autowired + private SysDeptPostMapper sysDeptPostMapper; + @Autowired + private SysDataPermDeptMapper sysDataPermDeptMapper; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private BizWidgetDatasourceExtHelper bizWidgetDatasourceExtHelper; + + /** + * 返回当前Service的主表Mapper对象。 + * + * @return 主表Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysDeptMapper; + } + + @PostConstruct + private void registerBizWidgetDatasource() { + bizWidgetDatasourceExtHelper.registerDatasource(BizWidgetDatasourceType.UPMS_DEPT_TYPE, this); + } + + @Override + public MyPageData> getDataList( + String type, Map filter, MyOrderParam orderParam, MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount()); + } + String orderBy = orderParam == null ? null : MyOrderParam.buildOrderBy(orderParam, SysDept.class); + SysDept deptFilter = filter == null ? null : BeanUtil.toBean(filter, SysDept.class); + List deptList = this.getSysDeptList(deptFilter, orderBy); + this.buildRelationForDataList(deptList, MyRelationParam.dictOnly()); + return MyPageUtil.makeResponseData(deptList, BeanUtil::beanToMap); + } + + @Override + public List> getDataListWithInList(String type, String fieldName, List fieldValues) { + List deptList; + if (StrUtil.isBlank(fieldName)) { + deptList = this.getInList(fieldValues.stream().map(Long::valueOf).collect(Collectors.toSet())); + } else { + deptList = this.getInList(fieldName, MyModelUtil.convertToTypeValues(SysDept.class, fieldName, fieldValues)); + } + this.buildRelationForDataList(deptList, MyRelationParam.dictOnly()); + return MyModelUtil.beanToMapList(deptList); + } + + /** + * 保存新增的部门对象。 + * + * @param sysDept 新增的部门对象。 + * @param parentSysDept 上级部门对象。 + * @return 新增后的部门对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysDept saveNew(SysDept sysDept, SysDept parentSysDept) { + sysDept.setDeptId(idGenerator.nextLongId()); + sysDept.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysDept); + sysDeptMapper.insert(sysDept); + // 同步插入部门关联关系数据 + if (parentSysDept == null) { + sysDeptRelationMapper.insert(new SysDeptRelation(sysDept.getDeptId(), sysDept.getDeptId())); + } else { + sysDeptRelationMapper.insertParentList(parentSysDept.getDeptId(), sysDept.getDeptId()); + } + return sysDept; + } + + /** + * 更新部门对象。 + * + * @param sysDept 更新的部门对象。 + * @param originalSysDept 原有的部门对象。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysDept sysDept, SysDept originalSysDept) { + MyModelUtil.fillCommonsForUpdate(sysDept, originalSysDept); + UpdateWrapper uw = this.createUpdateQueryForNullValue(sysDept, sysDept.getDeptId()); + if (sysDeptMapper.update(sysDept, uw) == 0) { + return false; + } + if (ObjectUtil.notEqual(sysDept.getParentId(), originalSysDept.getParentId())) { + this.updateParentRelation(sysDept, originalSysDept); + } + return true; + } + + private void updateParentRelation(SysDept sysDept, SysDept originalSysDept) { + List originalParentIdList = null; + // 1. 因为层级关系变化了,所以要先遍历出,当前部门的原有父部门Id列表。 + if (originalSysDept.getParentId() != null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDeptRelation::getDeptId, sysDept.getDeptId()); + List relationList = sysDeptRelationMapper.selectList(queryWrapper); + originalParentIdList = relationList.stream() + .filter(c -> !c.getParentDeptId().equals(sysDept.getDeptId())) + .map(SysDeptRelation::getParentDeptId).collect(Collectors.toList()); + } + // 2. 毕竟当前部门的上级部门变化了,所以当前部门和他的所有子部门,与当前部门的原有所有上级部门 + // 之间的关联关系就要被移除。 + // 这里先移除当前部门的所有子部门,与当前部门的所有原有上级部门之间的关联关系。 + if (CollUtil.isNotEmpty(originalParentIdList)) { + sysDeptRelationMapper.removeBetweenChildrenAndParents(originalParentIdList, sysDept.getDeptId()); + } + // 这里更进一步,将当前部门Id与其原有所有上级部门Id之间的关联关系删除。 + SysDeptRelation filter = new SysDeptRelation(); + filter.setDeptId(sysDept.getDeptId()); + sysDeptRelationMapper.delete(new QueryWrapper<>(filter)); + // 3. 重新计算当前部门的新上级部门列表。 + List newParentIdList = new LinkedList<>(); + // 这里要重新计算出当前部门所有新的上级部门Id列表。 + if (sysDept.getParentId() != null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDeptRelation::getDeptId, sysDept.getParentId()); + List relationList = sysDeptRelationMapper.selectList(queryWrapper); + newParentIdList = relationList.stream() + .map(SysDeptRelation::getParentDeptId).collect(Collectors.toList()); + } + // 4. 先查询出当前部门的所有下级子部门Id列表。 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDeptRelation::getParentDeptId, sysDept.getDeptId()); + List childRelationList = sysDeptRelationMapper.selectList(queryWrapper); + // 5. 将当前部门及其所有子部门Id与其新的所有上级部门Id之间,建立关联关系。 + List deptRelationList = new LinkedList<>(); + deptRelationList.add(new SysDeptRelation(sysDept.getDeptId(), sysDept.getDeptId())); + for (Long newParentId : newParentIdList) { + deptRelationList.add(new SysDeptRelation(newParentId, sysDept.getDeptId())); + for (SysDeptRelation childDeptRelation : childRelationList) { + deptRelationList.add(new SysDeptRelation(newParentId, childDeptRelation.getDeptId())); + } + } + // 6. 执行批量插入SQL语句,插入当前部门Id及其所有下级子部门Id,与所有新上级部门Id之间的关联关系。 + sysDeptRelationMapper.insertList(deptRelationList); + } + + /** + * 删除指定数据。 + * + * @param deptId 主键Id。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long deptId) { + if (sysDeptMapper.deleteById(deptId) == 0) { + return false; + } + // 这里删除当前部门及其父部门的关联关系。 + // 当前部门和子部门的关系无需在这里删除,因为包含子部门时不能删除父部门。 + SysDeptRelation deptRelation = new SysDeptRelation(); + deptRelation.setDeptId(deptId); + sysDeptRelationMapper.delete(new QueryWrapper<>(deptRelation)); + SysDataPermDept dataPermDept = new SysDataPermDept(); + dataPermDept.setDeptId(deptId); + sysDataPermDeptMapper.delete(new QueryWrapper<>(dataPermDept)); + return true; + } + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysDeptListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysDeptList(SysDept filter, String orderBy) { + return sysDeptMapper.getSysDeptList(null, null, filter, orderBy); + } + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysDeptListWithRelation)方法。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysDeptList( + String inFilterField, Set inFilterValues, SysDept filter, String orderBy) { + String inFilterColumn = MyModelUtil.mapToColumnName(inFilterField, SysDept.class); + return sysDeptMapper.getSysDeptList(inFilterColumn, inFilterValues, filter, orderBy); + } + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。 + * 如果仅仅需要获取主表数据,请移步(getSysDeptList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + @Override + public List getSysDeptListWithRelation(SysDept filter, String orderBy) { + List resultList = sysDeptMapper.getSysDeptList(null, null, filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 同时还包含主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysDeptList),以便获取更好的查询性能。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + @Override + public List getSysDeptListWithRelation( + String inFilterField, Set inFilterValues, SysDept filter, String orderBy) { + String inFilterColumn = MyModelUtil.mapToColumnName(inFilterField, SysDept.class); + List resultList = + sysDeptMapper.getSysDeptList(inFilterColumn, inFilterValues, filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.dictOnly(), batchSize); + return resultList; + } + + /** + * 判断指定对象是否包含下级对象。 + * + * @param deptId 主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long deptId) { + SysDept filter = new SysDept(); + filter.setParentId(deptId); + return getCountByFilter(filter) > 0; + } + + /** + * 判断指定部门Id是否包含用户对象。 + * + * @param deptId 部门主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildrenUser(Long deptId) { + SysUser sysUser = new SysUser(); + sysUser.setDeptId(deptId); + return sysUserService.getCountByFilter(sysUser) > 0; + } + + /** + * 批量添加多对多关联关系。 + * + * @param sysDeptPostList 多对多关联表对象集合。 + * @param deptId 主表Id。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void addSysDeptPostList(List sysDeptPostList, Long deptId) { + for (SysDeptPost sysDeptPost : sysDeptPostList) { + sysDeptPost.setDeptPostId(idGenerator.nextLongId()); + sysDeptPost.setDeptId(deptId); + sysDeptPostMapper.insert(sysDeptPost); + } + } + + /** + * 更新中间表数据。 + * + * @param sysDeptPost 中间表对象。 + * @return 更新成功与否。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean updateSysDeptPost(SysDeptPost sysDeptPost) { + SysDeptPost filter = new SysDeptPost(); + filter.setDeptPostId(sysDeptPost.getDeptPostId()); + filter.setDeptId(sysDeptPost.getDeptId()); + filter.setPostId(sysDeptPost.getPostId()); + UpdateWrapper uw = + BaseService.createUpdateQueryForNullValue(sysDeptPost, SysDeptPost.class); + uw.setEntity(filter); + return sysDeptPostMapper.update(sysDeptPost, uw) > 0; + } + + /** + * 移除单条多对多关系。 + * + * @param deptId 主表Id。 + * @param postId 从表Id。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean removeSysDeptPost(Long deptId, Long postId) { + SysDeptPost filter = new SysDeptPost(); + filter.setDeptId(deptId); + filter.setPostId(postId); + return sysDeptPostMapper.delete(new QueryWrapper<>(filter)) > 0; + } + + /** + * 获取中间表数据。 + * + * @param deptId 主表Id。 + * @param postId 从表Id。 + * @return 中间表对象。 + */ + @Override + public SysDeptPost getSysDeptPost(Long deptId, Long postId) { + SysDeptPost filter = new SysDeptPost(); + filter.setDeptId(deptId); + filter.setPostId(postId); + return sysDeptPostMapper.selectOne(new QueryWrapper<>(filter)); + } + + @Override + public SysDeptPost getSysDeptPost(Long deptPostId) { + return sysDeptPostMapper.selectById(deptPostId); + } + + @Override + public List> getSysDeptPostListWithRelationByDeptId(Long deptId) { + return sysDeptPostMapper.getSysDeptPostListWithRelationByDeptId(deptId); + } + + @Override + public List getSysDeptPostList(Long deptId, Set postIdSet) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDeptPost::getDeptId, deptId); + queryWrapper.in(SysDeptPost::getPostId, postIdSet); + return sysDeptPostMapper.selectList(queryWrapper); + } + + @Override + public List getSiblingSysDeptPostList(Long deptId, Set postIdSet) { + SysDept sysDept = this.getById(deptId); + if (sysDept == null) { + return new LinkedList<>(); + } + List deptList = this.getListByParentId("parentId", sysDept.getParentId()); + Set deptIdSet = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toSet()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysDeptPost::getDeptId, deptIdSet); + queryWrapper.in(SysDeptPost::getPostId, postIdSet); + return sysDeptPostMapper.selectList(queryWrapper); + } + + @Override + public List getLeaderDeptPostIdList(Long deptId) { + List resultList = sysDeptPostMapper.getLeaderDeptPostList(deptId); + return resultList.stream().map(SysDeptPost::getDeptPostId).collect(Collectors.toList()); + } + + @Override + public List getUpLeaderDeptPostIdList(Long deptId) { + SysDept sysDept = this.getById(deptId); + if (sysDept.getParentId() == null) { + return new LinkedList<>(); + } + return this.getLeaderDeptPostIdList(sysDept.getParentId()); + } + + @Override + public List getAllChildDeptIdByParentIds(List parentIds) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysDeptRelation::getParentDeptId, parentIds); + return sysDeptRelationMapper.selectList(queryWrapper) + .stream().map(SysDeptRelation::getDeptId).collect(Collectors.toList()); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysMenuServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..a93d54b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,348 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.util.MyModelUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysMenuMapper; +import apelet.tenantadmin.upms.dao.SysMenuPermCodeMapper; +import apelet.tenantadmin.upms.dao.SysRoleMenuMapper; +import apelet.tenantadmin.upms.model.SysMenu; +import apelet.tenantadmin.upms.model.SysMenuPermCode; +import apelet.tenantadmin.upms.model.SysRoleMenu; +import apelet.tenantadmin.upms.model.constant.SysMenuType; +import apelet.tenantadmin.upms.model.constant.SysOnlineMenuPermType; +import apelet.tenantadmin.upms.service.SysMenuService; +import apelet.tenantadmin.upms.service.SysPermCodeService; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysMenuService") +public class SysMenuServiceImpl extends BaseService implements SysMenuService { + + @Autowired + private SysMenuMapper sysMenuMapper; + @Autowired + private SysRoleMenuMapper sysRoleMenuMapper; + @Autowired + private SysMenuPermCodeMapper sysMenuPermCodeMapper; + @Autowired + private SysPermCodeService sysPermCodeService; + @Autowired + private IdGeneratorWrapper idGenerator; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysMenuMapper; + } + + /** + * 保存新增的菜单对象。 + * + * @param sysMenu 新增的菜单对象。 + * @param permCodeIdSet 权限字Id列表。 + * @return 新增后的菜单对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysMenu saveNew(SysMenu sysMenu, Set permCodeIdSet) { + sysMenu.setMenuId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(sysMenu); + sysMenu.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysMenuMapper.insert(sysMenu); + if (permCodeIdSet != null) { + for (Long permCodeId : permCodeIdSet) { + SysMenuPermCode menuPermCode = new SysMenuPermCode(); + menuPermCode.setMenuId(sysMenu.getMenuId()); + menuPermCode.setPermCodeId(permCodeId); + sysMenuPermCodeMapper.insert(menuPermCode); + } + } + // 判断当前菜单是否为指向在线表单的菜单,并将根据约定,动态插入两个子菜单。 + if (sysMenu.getOnlineFormId() != null) { + SysMenu viewSubMenu = new SysMenu(); + viewSubMenu.setMenuId(idGenerator.nextLongId()); + viewSubMenu.setParentId(sysMenu.getMenuId()); + viewSubMenu.setMenuType(SysMenuType.TYPE_BUTTON); + viewSubMenu.setMenuName("查看"); + viewSubMenu.setShowOrder(0); + viewSubMenu.setOnlineFormId(sysMenu.getOnlineFormId()); + viewSubMenu.setOnlineMenuPermType(SysOnlineMenuPermType.TYPE_VIEW); + viewSubMenu.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(viewSubMenu); + sysMenuMapper.insert(viewSubMenu); + SysMenu editSubMenu = new SysMenu(); + editSubMenu.setMenuId(idGenerator.nextLongId()); + editSubMenu.setParentId(sysMenu.getMenuId()); + editSubMenu.setMenuType(SysMenuType.TYPE_BUTTON); + editSubMenu.setMenuName("编辑"); + editSubMenu.setShowOrder(1); + editSubMenu.setOnlineFormId(sysMenu.getOnlineFormId()); + editSubMenu.setOnlineMenuPermType(SysOnlineMenuPermType.TYPE_EDIT); + editSubMenu.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(editSubMenu); + sysMenuMapper.insert(editSubMenu); + } + return sysMenu; + } + + /** + * 更新菜单对象。 + * + * @param sysMenu 更新的菜单对象。 + * @param originalSysMenu 原有的菜单对象。 + * @param permCodeIdSet 权限字Id列表。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysMenu sysMenu, SysMenu originalSysMenu, Set permCodeIdSet) { + MyModelUtil.fillCommonsForUpdate(sysMenu, originalSysMenu); + sysMenu.setMenuType(originalSysMenu.getMenuType()); + UpdateWrapper uw = this.createUpdateQueryForNullValue(sysMenu, sysMenu.getMenuId()); + if (sysMenuMapper.update(sysMenu, uw) != 1) { + return false; + } + SysMenuPermCode deletedMenuPermCode = new SysMenuPermCode(); + deletedMenuPermCode.setMenuId(sysMenu.getMenuId()); + sysMenuPermCodeMapper.delete(new QueryWrapper<>(deletedMenuPermCode)); + if (permCodeIdSet != null) { + for (Long permCodeId : permCodeIdSet) { + SysMenuPermCode menuPermCode = new SysMenuPermCode(); + menuPermCode.setMenuId(sysMenu.getMenuId()); + menuPermCode.setPermCodeId(permCodeId); + sysMenuPermCodeMapper.insert(menuPermCode); + } + } + // 如果当前菜单的在线表单Id变化了,就需要同步更新他的内置子菜单也同步更新。 + if (ObjectUtil.notEqual(originalSysMenu.getOnlineFormId(), sysMenu.getOnlineFormId())) { + SysMenu onlineSubMenu = new SysMenu(); + onlineSubMenu.setOnlineFormId(sysMenu.getOnlineFormId()); + sysMenuMapper.update(onlineSubMenu, + new QueryWrapper().lambda().eq(SysMenu::getParentId, sysMenu.getMenuId())); + } + return true; + } + + /** + * 删除指定的菜单。 + * + * @param menu 菜单对象。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(SysMenu menu) { + Long menuId = menu.getMenuId(); + if (sysMenuMapper.delete(new LambdaQueryWrapper().eq(SysMenu::getMenuId, menuId)) != 1) { + return false; + } + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setMenuId(menuId); + sysRoleMenuMapper.delete(new QueryWrapper<>(roleMenu)); + SysMenuPermCode menuPermCode = new SysMenuPermCode(); + menuPermCode.setMenuId(menuId); + sysMenuPermCodeMapper.delete(new QueryWrapper<>(menuPermCode)); + // 如果为指向在线表单的菜单,则连同删除子菜单 + if (menu.getOnlineFormId() != null) { + SysMenu filter = new SysMenu(); + filter.setParentId(menuId); + List childMenus = sysMenuMapper.selectList(new QueryWrapper<>(filter)); + sysMenuMapper.delete(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); + if (CollUtil.isNotEmpty(childMenus)) { + List childMenuIds = childMenus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.in(SysRoleMenu::getMenuId, childMenuIds); + sysRoleMenuMapper.delete(qw); + LambdaQueryWrapper qw2 = new LambdaQueryWrapper<>(); + qw2.in(SysMenuPermCode::getMenuId, childMenuIds); + sysMenuPermCodeMapper.delete(qw2); + } + } + return true; + } + + /** + * 获取指定用户Id的菜单列表,已去重。 + * + * @param userId 用户主键Id。 + * @return 用户关联的菜单列表。 + */ + @Override + public Collection getMenuListByUserId(Long userId) { + List menuList = sysMenuMapper.getMenuListByUserId(userId); + LinkedHashMap menuMap = new LinkedHashMap<>(); + for (SysMenu menu : menuList) { + menuMap.put(menu.getMenuId(), menu); + } + return menuMap.values(); + } + + @Override + public List> getMenuAndPermListByRoleIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return new LinkedList<>(); + } + return sysMenuMapper.getMenuAndPermListByRoleIds(roleIds); + } + + /** + * 判断当前菜单是否存在子菜单。 + * + * @param menuId 菜单主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long menuId) { + SysMenu menu = new SysMenu(); + menu.setParentId(menuId); + return this.getCountByFilter(menu) > 0; + } + + /** + * 验证菜单对象关联的数据是否都合法。 + * + * @param sysMenu 当前操作的对象。 + * @param originalSysMenu 原有对象。 + * @param permCodeIdListString 逗号分隔的权限Id列表。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData(SysMenu sysMenu, SysMenu originalSysMenu, String permCodeIdListString) { + // menu、ui fragment和button类型的menu不能没有parentId + if (sysMenu.getParentId() == null && sysMenu.getMenuType() != SysMenuType.TYPE_DIRECTORY) { + return CallResult.error("数据验证失败,当前类型菜单项的上级菜单不能为空!"); + } + if (this.needToVerify(sysMenu, originalSysMenu, SysMenu::getParentId)) { + String errorMessage = checkErrorOfNonDirectoryMenu(sysMenu); + if (errorMessage != null) { + return CallResult.error(errorMessage); + } + } + JSONObject jsonObject = null; + if (StrUtil.isNotBlank(permCodeIdListString)) { + Set permCodeIdSet = Arrays.stream( + permCodeIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysPermCodeService.existAllPrimaryKeys(permCodeIdSet)) { + return CallResult.error("数据验证失败,存在不合法的权限字,请刷新后重试!"); + } + jsonObject = new JSONObject(); + jsonObject.put("permCodeIdSet", permCodeIdSet); + } + return CallResult.ok(jsonObject); + } + + /** + * 查询菜单的权限资源地址列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param url 权限资源地址过滤条件。 + * @return 包含从菜单到权限资源的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysPermListWithDetail(Long menuId, String url) { + return sysMenuMapper.getSysPermListWithDetail(menuId, url); + } + + /** + * 查询菜单的用户列表。同时返回详细的分配路径。 + * + * @param menuId 菜单Id。 + * @param loginName 登录名。 + * @return 包含从菜单到用户的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysUserListWithDetail(Long menuId, String loginName) { + return sysMenuMapper.getSysUserListWithDetail(menuId, loginName); + } + + /** + * 获取指定类型的所有在线表单的菜单。 + * + * @param menuType 菜单类型,NULL则返回全部类型。 + * @return 在线表单关联的菜单列表。 + */ + @Override + public List getAllOnlineMenuList(Integer menuType) { + LambdaQueryWrapper queryWrapper = + new QueryWrapper().lambda().isNotNull(SysMenu::getOnlineFormId); + if (menuType != null) { + queryWrapper.eq(SysMenu::getMenuType, menuType); + } + return sysMenuMapper.selectList(queryWrapper); + } + + @Override + public List getOnlineMenuListByUserId(Long userId, Integer menuType) { + return sysMenuMapper.getOnlineMenuListByUserId(userId, menuType); + } + + @Override + public List getReportMenuListByUserId(Long userId, int menuType) { + return sysMenuMapper.getReportMenuListByUserId(userId, menuType); + } + + @Override + public List getMenuListByPageIds(List pageIdList) { + return sysMenuMapper.getMenuListByPageIds(pageIdList); + } + + private String checkErrorOfNonDirectoryMenu(SysMenu sysMenu) { + // 判断父节点是否存在 + SysMenu parentSysMenu = getById(sysMenu.getParentId()); + if (parentSysMenu == null) { + return "数据验证失败,关联的上级菜单并不存在,请刷新后重试!"; + } + // 逐个判断每种类型的菜单,他的父菜单的合法性,先从目录类型和菜单类型开始 + if (sysMenu.getMenuType() == SysMenuType.TYPE_DIRECTORY + || sysMenu.getMenuType() == SysMenuType.TYPE_MENU) { + // 他们的上级只能是目录 + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_DIRECTORY) { + return "数据验证失败,当前类型菜单项的上级菜单只能是目录类型!"; + } + } else if (sysMenu.getMenuType() == SysMenuType.TYPE_UI_FRAGMENT) { + // ui fragment的上级只能是menu类型 + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_MENU) { + return "数据验证失败,当前类型菜单项的上级菜单只能是菜单类型和按钮类型!"; + } + } else if (sysMenu.getMenuType() == SysMenuType.TYPE_BUTTON) { + // button的上级只能是menu和ui fragment + if (parentSysMenu.getMenuType() != SysMenuType.TYPE_MENU + && parentSysMenu.getMenuType() != SysMenuType.TYPE_UI_FRAGMENT) { + return "数据验证失败,当前类型菜单项的上级菜单只能是菜单类型和UI片段类型!"; + } + } else { + return "数据验证失败,不支持的菜单类型!"; + } + return null; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermCodeServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermCodeServiceImpl.java new file mode 100644 index 0000000..fa825a9 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermCodeServiceImpl.java @@ -0,0 +1,228 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.util.MyModelUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysMenuPermCodeMapper; +import apelet.tenantadmin.upms.dao.SysPermCodeMapper; +import apelet.tenantadmin.upms.dao.SysPermCodePermMapper; +import apelet.tenantadmin.upms.model.SysMenuPermCode; +import apelet.tenantadmin.upms.model.SysPermCode; +import apelet.tenantadmin.upms.model.SysPermCodePerm; +import apelet.tenantadmin.upms.service.SysPermCodeService; +import apelet.tenantadmin.upms.service.SysPermService; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 权限字数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysPermCodeService") +public class SysPermCodeServiceImpl extends BaseService implements SysPermCodeService { + + @Autowired + private SysPermCodeMapper sysPermCodeMapper; + @Autowired + private SysPermCodePermMapper sysPermCodePermMapper; + @Autowired + private SysMenuPermCodeMapper sysMenuPermCodeMapper; + @Autowired + private SysPermService sysPermService; + @Autowired + private IdGeneratorWrapper idGenerator; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysPermCodeMapper; + } + + /** + * 获取指定用户的权限字列表,已去重。 + * + * @param userId 用户主键Id。 + * @return 用户关联的权限字列表。 + */ + @Override + public Collection getPermCodeListByUserId(Long userId) { + List permCodeList = sysPermCodeMapper.getPermCodeListByUserId(userId); + return new HashSet<>(permCodeList); + } + + /** + * 获取所有权限字数据列表,已去重。 + * + * @return 全部权限字列表。 + */ + @Override + public Collection getAllPermCodeList() { + List permCodeList = this.getAllList(); + return permCodeList.stream().map(SysPermCode::getPermCode).collect(Collectors.toSet()); + } + + /** + * 保存新增的权限字对象。 + * + * @param sysPermCode 新增的权限字对象。 + * @param permIdSet 权限资源Id列表。 + * @return 新增后的权限字对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysPermCode saveNew(SysPermCode sysPermCode, Set permIdSet) { + sysPermCode.setPermCodeId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(sysPermCode); + sysPermCode.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysPermCodeMapper.insert(sysPermCode); + if (permIdSet != null) { + for (Long permId : permIdSet) { + SysPermCodePerm permCodePerm = new SysPermCodePerm(); + permCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + permCodePerm.setPermId(permId); + sysPermCodePermMapper.insert(permCodePerm); + } + } + return sysPermCode; + } + + /** + * 更新权限字对象。 + * + * @param sysPermCode 更新的权限字对象。 + * @param originalSysPermCode 原有的权限字对象。 + * @param permIdSet 权限资源Id列表。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysPermCode sysPermCode, SysPermCode originalSysPermCode, Set permIdSet) { + MyModelUtil.fillCommonsForUpdate(sysPermCode, originalSysPermCode); + sysPermCode.setParentId(originalSysPermCode.getParentId()); + UpdateWrapper uw = + this.createUpdateQueryForNullValue(sysPermCode, sysPermCode.getPermCodeId()); + if (sysPermCodeMapper.update(sysPermCode, uw) != 1) { + return false; + } + SysPermCodePerm deletedPermCodePerm = new SysPermCodePerm(); + deletedPermCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + sysPermCodePermMapper.delete(new QueryWrapper<>(deletedPermCodePerm)); + if (permIdSet != null) { + for (Long permId : permIdSet) { + SysPermCodePerm permCodePerm = new SysPermCodePerm(); + permCodePerm.setPermCodeId(sysPermCode.getPermCodeId()); + permCodePerm.setPermId(permId); + sysPermCodePermMapper.insert(permCodePerm); + } + } + return true; + } + + /** + * 删除指定的权限字。 + * + * @param permCodeId 权限字主键Id。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long permCodeId) { + if (sysPermCodeMapper.deleteById(permCodeId) != 1) { + return false; + } + SysMenuPermCode menuPermCode = new SysMenuPermCode(); + menuPermCode.setPermCodeId(permCodeId); + sysMenuPermCodeMapper.delete(new QueryWrapper<>(menuPermCode)); + SysPermCodePerm permCodePerm = new SysPermCodePerm(); + permCodePerm.setPermCodeId(permCodeId); + sysPermCodePermMapper.delete(new QueryWrapper<>(permCodePerm)); + return true; + } + + /** + * 判断当前权限字是否存在下级权限字对象。 + * + * @param permCodeId 权限字主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long permCodeId) { + SysPermCode permCode = new SysPermCode(); + permCode.setParentId(permCodeId); + return this.getCountByFilter(permCode) > 0; + } + + /** + * 验证权限字对象关联的数据是否都合法。 + * + * @param sysPermCode 当前操作的对象。 + * @param originalSysPermCode 原有对象。 + * @param permIdListString 逗号分隔的权限资源Id列表。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData( + SysPermCode sysPermCode, SysPermCode originalSysPermCode, String permIdListString) { + if (this.needToVerify(sysPermCode, originalSysPermCode, SysPermCode::getParentId) + && getById(sysPermCode.getParentId()) == null) { + return CallResult.error("数据验证失败,关联的上级权限字并不存在,请刷新后重试!"); + } + JSONObject jsonObject = null; + if (StringUtils.isNotBlank(permIdListString)) { + Set permIdSet = Arrays.stream( + permIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysPermService.existAllPrimaryKeys(permIdSet)) { + return CallResult.error("数据验证失败,存在不合法的权限资源,请刷新后重试!"); + } + jsonObject = new JSONObject(); + jsonObject.put("permIdSet", permIdSet); + } + return CallResult.ok(jsonObject); + } + + /** + * 查询权限字的用户列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param loginName 登录名。 + * @return 包含从权限字到用户的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysUserListWithDetail(Long permCodeId, String loginName) { + return sysPermCodeMapper.getSysUserListWithDetail(permCodeId, loginName); + } + + /** + * 查询权限字的角色列表。同时返回详细的分配路径。 + * + * @param permCodeId 权限字Id。 + * @param roleName 角色名。 + * @return 包含从权限字到角色的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysRoleListWithDetail(Long permCodeId, String roleName) { + return sysPermCodeMapper.getSysRoleListWithDetail(permCodeId, roleName); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermModuleServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermModuleServiceImpl.java new file mode 100644 index 0000000..d057761 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermModuleServiceImpl.java @@ -0,0 +1,127 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.util.MyModelUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysPermModuleMapper; +import apelet.tenantadmin.upms.model.SysPerm; +import apelet.tenantadmin.upms.model.SysPermModule; +import apelet.tenantadmin.upms.service.SysPermModuleService; +import apelet.tenantadmin.upms.service.SysPermService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 权限资源模块数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysPermModuleService") +public class SysPermModuleServiceImpl extends BaseService implements SysPermModuleService { + + @Autowired + private SysPermModuleMapper sysPermModuleMapper; + @Autowired + private SysPermService sysPermService; + @Autowired + private IdGeneratorWrapper idGenerator; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysPermModuleMapper; + } + + /** + * 保存新增的权限资源模块对象。 + * + * @param sysPermModule 新增的权限资源模块对象。 + * @return 新增后的权限资源模块对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysPermModule saveNew(SysPermModule sysPermModule) { + sysPermModule.setModuleId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(sysPermModule); + sysPermModule.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysPermModuleMapper.insert(sysPermModule); + return sysPermModule; + } + + /** + * 更新权限资源模块对象。 + * + * @param sysPermModule 更新的权限资源模块对象。 + * @param originalSysPermModule 原有的权限资源模块对象。 + * @return 更新成功返回true,否则false + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysPermModule sysPermModule, SysPermModule originalSysPermModule) { + MyModelUtil.fillCommonsForUpdate(sysPermModule, originalSysPermModule); + return sysPermModuleMapper.updateById(sysPermModule) != 0; + } + + /** + * 删除指定的权限资源模块。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long moduleId) { + return sysPermModuleMapper.deleteById(moduleId) == 1; + } + + /** + * 获取权限模块资源及其关联的权限资源列表。 + * + * @return 权限资源模块及其关联的权限资源列表。 + */ + @Override + public List getPermModuleAndPermList() { + return sysPermModuleMapper.getPermModuleAndPermList(); + } + + /** + * 判断是否存在下级权限资源模块。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasChildren(Long moduleId) { + SysPermModule permModule = new SysPermModule(); + permModule.setParentId(moduleId); + return this.getCountByFilter(permModule) > 0; + } + + /** + * 判断是否存在权限数据。 + * + * @param moduleId 权限资源模块主键Id。 + * @return 存在返回true,否则false。 + */ + @Override + public boolean hasModulePerms(Long moduleId) { + SysPerm filter = new SysPerm(); + filter.setModuleId(moduleId); + return sysPermService.getCountByFilter(filter) > 0; + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermServiceImpl.java new file mode 100644 index 0000000..cab1921 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermServiceImpl.java @@ -0,0 +1,226 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.CallResult; +import apelet.common.core.object.MyRelationParam; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.RedisKeyUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.ApplicationConfig; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysPermCodePermMapper; +import apelet.tenantadmin.upms.dao.SysPermMapper; +import apelet.tenantadmin.upms.model.SysPerm; +import apelet.tenantadmin.upms.model.SysPermCodePerm; +import apelet.tenantadmin.upms.model.SysPermModule; +import apelet.tenantadmin.upms.service.SysPermModuleService; +import apelet.tenantadmin.upms.service.SysPermService; +import apelet.tenantadmin.upms.service.SysUserService; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RSet; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 权限资源数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysPermService") +public class SysPermServiceImpl extends BaseService implements SysPermService { + + @Autowired + private SysPermMapper sysPermMapper; + @Autowired + private SysPermCodePermMapper sysPermCodePermMapper; + @Autowired + private SysPermModuleService sysPermModuleService; + @Autowired + private SysUserService sysUserService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private RedissonClient redissonClient; + @Autowired + private ApplicationConfig applicationConfig; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysPermMapper; + } + + /** + * 保存新增的权限资源对象。 + * + * @param perm 新增的权限资源对象。 + * @return 新增后的权限资源对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysPerm saveNew(SysPerm perm) { + perm.setPermId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(perm); + perm.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysPermMapper.insert(perm); + return perm; + } + + /** + * 更新权限资源对象。 + * + * @param perm 更新的权限资源对象。 + * @param originalPerm 原有的权限资源对象。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysPerm perm, SysPerm originalPerm) { + MyModelUtil.fillCommonsForUpdate(perm, originalPerm); + return sysPermMapper.updateById(perm) != 0; + } + + /** + * 删除权限资源。 + * + * @param permId 权限资源主键Id。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long permId) { + if (sysPermMapper.deleteById(permId) != 1) { + return false; + } + SysPermCodePerm permCodePerm = new SysPermCodePerm(); + permCodePerm.setPermId(permId); + sysPermCodePermMapper.delete(new QueryWrapper<>(permCodePerm)); + return true; + } + + /** + * 获取权限数据列表。 + * + * @param sysPermFilter 过滤对象。 + * @return 权限列表。 + */ + @Override + public List getPermListWithRelation(SysPerm sysPermFilter) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.orderByAsc(this.safeMapToColumnName("showOrder")); + queryWrapper.eq(ObjectUtil.isNotNull(sysPermFilter.getModuleId()), + this.safeMapToColumnName("moduleId"), sysPermFilter.getModuleId()); + queryWrapper.like(ObjectUtil.isNotNull(sysPermFilter.getUrl()), + this.safeMapToColumnName("url"), "%" + sysPermFilter.getUrl() + "%"); + List permList = sysPermMapper.selectList(queryWrapper); + // 这里因为权限只有字典数据,所以仅仅做字典关联。 + this.buildRelationForDataList(permList, MyRelationParam.dictOnly()); + return permList; + } + + @Override + public void putUserSysPermCache(String sessionId, Long userId, Set permUrlSet) { + if (CollUtil.isEmpty(permUrlSet)) { + return; + } + String sessionPermKey = RedisKeyUtil.makeSessionPermIdKey(sessionId); + RSet redisPermSet = redissonClient.getSet(sessionPermKey); + redisPermSet.addAll(permUrlSet); + redisPermSet.expire(applicationConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + + /** + * 将指定会话的权限集合从缓存中移除。 + * + * @param sessionId 会话Id。 + */ + @Override + public void removeUserSysPermCache(String sessionId) { + String sessionPermKey = RedisKeyUtil.makeSessionPermIdKey(sessionId); + redissonClient.getSet(sessionPermKey).deleteAsync(); + } + + /** + * 获取与指定用户关联的权限资源列表,已去重。 + * + * @param userId 关联的用户主键Id。 + * @return 与指定用户Id关联的权限资源列表。 + */ + @Override + public Collection getPermListByUserId(Long userId) { + List urlList = sysPermMapper.getPermListByUserId(userId); + return new HashSet<>(urlList); + } + + /** + * 验证权限资源对象关联的数据是否都合法。 + * + * @param sysPerm 当前操作的对象。 + * @param originalSysPerm 原有对象。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData(SysPerm sysPerm, SysPerm originalSysPerm) { + if (this.needToVerify(sysPerm, originalSysPerm, SysPerm::getModuleId)) { + SysPermModule permModule = sysPermModuleService.getById(sysPerm.getModuleId()); + if (permModule == null) { + return CallResult.error("数据验证失败,关联的权限模块Id并不存在,请刷新后重试!"); + } + } + return CallResult.ok(); + } + + /** + * 查询权限资源地址的用户列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param loginName 登录名。 + * @return 包含从权限资源到用户的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysUserListWithDetail(Long permId, String loginName) { + return sysPermMapper.getSysUserListWithDetail(permId, loginName); + } + + /** + * 查询权限资源地址的角色列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param roleName 角色名。 + * @return 包含从权限资源到角色的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysRoleListWithDetail(Long permId, String roleName) { + return sysPermMapper.getSysRoleListWithDetail(permId, roleName); + } + + /** + * 查询权限资源地址的菜单列表。同时返回详细的分配路径。 + * + * @param permId 权限资源Id。 + * @param menuName 菜单名。 + * @return 包含从权限资源到菜单的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysMenuListWithDetail(Long permId, String menuName) { + return sysPermMapper.getSysMenuListWithDetail(permId, menuName); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermWhitelistServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermWhitelistServiceImpl.java new file mode 100644 index 0000000..04f2729 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPermWhitelistServiceImpl.java @@ -0,0 +1,55 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysPermWhitelistMapper; +import apelet.tenantadmin.upms.model.SysPermWhitelist; +import apelet.tenantadmin.upms.service.SysPermWhitelistService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 权限资源白名单数据服务类。 + * 白名单中的权限资源,可以不受权限控制,任何用户皆可访问,一般用于常用的字典数据列表接口。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysPermWhitelistService") +public class SysPermWhitelistServiceImpl extends BaseService implements SysPermWhitelistService { + + @Autowired + private SysPermWhitelistMapper sysPermWhitelistMapper; + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysPermWhitelistMapper; + } + + /** + * 获取白名单权限资源的列表。 + * + * @return 白名单权限资源地址列表。 + */ + @Override + public List getWhitelistPermList() { + List dataList = this.getAllList(); + Function getterFunc = SysPermWhitelist::getPermUrl; + return dataList.stream() + .filter(x -> getterFunc.apply(x) != null).map(getterFunc).collect(Collectors.toList()); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPostServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..9e23278 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysPostServiceImpl.java @@ -0,0 +1,242 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.ext.base.BizWidgetDatasource; +import apelet.common.ext.constant.BizWidgetDatasourceType; +import apelet.common.ext.util.BizWidgetDatasourceExtHelper; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysDeptPostMapper; +import apelet.tenantadmin.upms.dao.SysPostMapper; +import apelet.tenantadmin.upms.dao.SysUserPostMapper; +import apelet.tenantadmin.upms.model.SysDeptPost; +import apelet.tenantadmin.upms.model.SysPost; +import apelet.tenantadmin.upms.model.SysUserPost; +import apelet.tenantadmin.upms.service.SysDeptService; +import apelet.tenantadmin.upms.service.SysPostService; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 岗位管理数据操作服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysPostService") +public class SysPostServiceImpl extends BaseService implements SysPostService, BizWidgetDatasource { + + @Autowired + private SysPostMapper sysPostMapper; + @Autowired + private SysUserPostMapper sysUserPostMapper; + @Autowired + private SysDeptPostMapper sysDeptPostMapper; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private BizWidgetDatasourceExtHelper bizWidgetDatasourceExtHelper; + + /** + * 返回当前Service的主表Mapper对象。 + * + * @return 主表Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysPostMapper; + } + + @PostConstruct + private void registerBizWidgetDatasource() { + bizWidgetDatasourceExtHelper.registerDatasource(BizWidgetDatasourceType.UPMS_POST_TYPE, this); + bizWidgetDatasourceExtHelper.registerDatasource(BizWidgetDatasourceType.UPMS_DEPT_POST_TYPE, this); + } + + @Override + public MyPageData> getDataList( + String type, Map filter, MyOrderParam orderParam, MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount()); + } + String orderBy = orderParam == null ? null : MyOrderParam.buildOrderBy(orderParam, SysPost.class); + SysPost postFilter = filter == null ? null : BeanUtil.toBean(filter, SysPost.class); + if (StrUtil.equals(type, BizWidgetDatasourceType.UPMS_POST_TYPE)) { + List postList = this.getSysPostList(postFilter, orderBy); + return MyPageUtil.makeResponseData(postList, BeanUtil::beanToMap); + } + Assert.notNull(filter, "filter can't be NULL."); + Long deptId = (Long) filter.get("deptId"); + List> dataList = sysDeptService.getSysDeptPostListWithRelationByDeptId(deptId); + return MyPageUtil.makeResponseData(dataList); + } + + @Override + public List> getDataListWithInList(String type, String fieldName, List fieldValues) { + List postList; + if (StrUtil.isBlank(fieldName)) { + postList = this.getInList(fieldValues.stream().map(Long::valueOf).collect(Collectors.toSet())); + } else { + postList = this.getInList(fieldName, MyModelUtil.convertToTypeValues(SysPost.class, fieldName, fieldValues)); + } + return MyModelUtil.beanToMapList(postList); + } + + /** + * 保存新增对象。 + * + * @param sysPost 新增对象。 + * @return 返回新增对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysPost saveNew(SysPost sysPost) { + sysPost.setPostId(idGenerator.nextLongId()); + sysPost.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(sysPost); + MyModelUtil.setDefaultValue(sysPost, "leaderPost", false); + sysPostMapper.insert(sysPost); + return sysPost; + } + + /** + * 更新数据对象。 + * + * @param sysPost 更新的对象。 + * @param originalSysPost 原有数据对象。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysPost sysPost, SysPost originalSysPost) { + MyModelUtil.fillCommonsForUpdate(sysPost, originalSysPost); + // 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。 + UpdateWrapper uw = this.createUpdateQueryForNullValue(sysPost, sysPost.getPostId()); + return sysPostMapper.update(sysPost, uw) == 1; + } + + /** + * 删除指定数据。 + * + * @param postId 主键Id。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long postId) { + if (sysPostMapper.deleteById(postId) != 1) { + return false; + } + // 开始删除多对多父表的关联 + SysUserPost sysUserPost = new SysUserPost(); + sysUserPost.setPostId(postId); + sysUserPostMapper.delete(new QueryWrapper<>(sysUserPost)); + SysDeptPost sysDeptPost = new SysDeptPost(); + sysDeptPost.setPostId(postId); + sysDeptPostMapper.delete(new QueryWrapper<>(sysDeptPost)); + return true; + } + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysPostListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysPostList(SysPost filter, String orderBy) { + return sysPostMapper.getSysPostList(filter, orderBy); + } + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。 + * 如果仅仅需要获取主表数据,请移步(getSysPostList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysPostListWithRelation(SysPost filter, String orderBy) { + List resultList = sysPostMapper.getSysPostList(filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + /** + * 在多对多关系中,当前Service的数据表为从表,返回不与指定主表主键Id存在对多对关系的列表。 + * + * @param deptId 主表主键Id。 + * @param filter 从表的过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getNotInSysPostListByDeptId(Long deptId, SysPost filter, String orderBy) { + List resultList = sysPostMapper.getNotInSysPostListByDeptId(deptId, filter, orderBy); + this.buildRelationForDataList(resultList, MyRelationParam.dictOnly()); + return resultList; + } + + /** + * 获取指定部门的岗位列表。 + * + * @param deptId 部门Id。 + * @param filter 从表的过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysPostListByDeptId(Long deptId, SysPost filter, String orderBy) { + List resultList = sysPostMapper.getSysPostListByDeptId(deptId, filter, orderBy); + this.buildRelationForDataList(resultList, MyRelationParam.dictOnly()); + return resultList; + } + + @Override + public List getSysUserPostListByUserId(Long userId) { + SysUserPost filter = new SysUserPost(); + filter.setUserId(userId); + return sysUserPostMapper.selectList(new QueryWrapper<>(filter)); + } + + @Override + public boolean existAllPrimaryKeys(Set deptPostIdSet, Long deptId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDeptPost::getDeptId, deptId); + queryWrapper.in(SysDeptPost::getDeptPostId, deptPostIdSet); + return sysDeptPostMapper.selectCount(queryWrapper) == deptPostIdSet.size(); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysRoleServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..c969253 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,269 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.core.annotation.MyDataSource; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.ext.base.BizWidgetDatasource; +import apelet.common.ext.constant.BizWidgetDatasourceType; +import apelet.common.ext.util.BizWidgetDatasourceExtHelper; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.config.DataSourceType; +import apelet.tenantadmin.upms.dao.SysRoleMapper; +import apelet.tenantadmin.upms.dao.SysRoleMenuMapper; +import apelet.tenantadmin.upms.dao.SysUserRoleMapper; +import apelet.tenantadmin.upms.model.SysRole; +import apelet.tenantadmin.upms.model.SysRoleMenu; +import apelet.tenantadmin.upms.model.SysUserRole; +import apelet.tenantadmin.upms.service.SysMenuService; +import apelet.tenantadmin.upms.service.SysRoleService; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 角色数据服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@MyDataSource(DataSourceType.TENANT_ADMIN) +@Service("sysRoleService") +public class SysRoleServiceImpl extends BaseService implements SysRoleService, BizWidgetDatasource { + + @Autowired + private SysRoleMapper sysRoleMapper; + @Autowired + private SysRoleMenuMapper sysRoleMenuMapper; + @Autowired + private SysUserRoleMapper sysUserRoleMapper; + @Autowired + private SysMenuService sysMenuService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private BizWidgetDatasourceExtHelper bizWidgetDatasourceExtHelper; + + @PostConstruct + private void registerBizWidgetDatasource() { + bizWidgetDatasourceExtHelper.registerDatasource(BizWidgetDatasourceType.UPMS_ROLE_TYPE, this); + } + + @Override + public MyPageData> getDataList( + String type, Map filter, MyOrderParam orderParam, MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount()); + } + String orderBy = orderParam == null ? null : MyOrderParam.buildOrderBy(orderParam, SysRole.class); + SysRole roleFilter = filter == null ? null : BeanUtil.toBean(filter, SysRole.class); + List roleList = this.getSysRoleList(roleFilter, orderBy); + return MyPageUtil.makeResponseData(roleList, BeanUtil::beanToMap); + } + + @Override + public List> getDataListWithInList(String type, String fieldName, List fieldValues) { + List roleList; + if (StrUtil.isBlank(fieldName)) { + roleList = this.getInList(fieldValues.stream().map(Long::valueOf).collect(Collectors.toSet())); + } else { + roleList = this.getInList(fieldName, MyModelUtil.convertToTypeValues(SysRole.class, fieldName, fieldValues)); + } + return MyModelUtil.beanToMapList(roleList); + } + + /** + * 返回主对象的Mapper对象。 + * + * @return 主对象的Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysRoleMapper; + } + + /** + * 保存新增的角色对象。 + * + * @param role 新增的角色对象。 + * @param menuIdSet 菜单Id列表。 + * @return 新增后的角色对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysRole saveNew(SysRole role, Set menuIdSet) { + role.setRoleId(idGenerator.nextLongId()); + MyModelUtil.fillCommonsForInsert(role); + role.setDeletedFlag(GlobalDeletedFlag.NORMAL); + sysRoleMapper.insert(role); + if (menuIdSet != null) { + for (Long menuId : menuIdSet) { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(role.getRoleId()); + roleMenu.setMenuId(menuId); + sysRoleMenuMapper.insert(roleMenu); + } + } + return role; + } + + /** + * 更新角色对象。 + * + * @param role 更新的角色对象。 + * @param originalRole 原有的角色对象。 + * @param menuIdSet 菜单Id列表。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysRole role, SysRole originalRole, Set menuIdSet) { + MyModelUtil.fillCommonsForUpdate(role, originalRole); + if (sysRoleMapper.updateById(role) != 1) { + return false; + } + SysRoleMenu deletedRoleMenu = new SysRoleMenu(); + deletedRoleMenu.setRoleId(role.getRoleId()); + sysRoleMenuMapper.delete(new QueryWrapper<>(deletedRoleMenu)); + if (menuIdSet != null) { + for (Long menuId : menuIdSet) { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(role.getRoleId()); + roleMenu.setMenuId(menuId); + sysRoleMenuMapper.insert(roleMenu); + } + } + return true; + } + + /** + * 删除指定角色。 + * + * @param roleId 角色主键Id。 + * @return 删除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long roleId) { + if (sysRoleMapper.deleteById(roleId) != 1) { + return false; + } + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(roleId); + sysRoleMenuMapper.delete(new QueryWrapper<>(roleMenu)); + SysUserRole userRole = new SysUserRole(); + userRole.setRoleId(roleId); + sysUserRoleMapper.delete(new QueryWrapper<>(userRole)); + return true; + } + + /** + * 获取角色列表。 + * + * @param filter 角色过滤对象。 + * @param orderBy 排序参数。 + * @return 角色列表。 + */ + @Override + public List getSysRoleList(SysRole filter, String orderBy) { + return sysRoleMapper.getSysRoleList(filter, orderBy); + } + + @Override + public List getSysUserRoleListByUserId(Long userId) { + SysUserRole filter = new SysUserRole(); + filter.setUserId(userId); + return sysUserRoleMapper.selectList(new QueryWrapper<>(filter)); + } + + /** + * 批量新增用户角色关联。 + * + * @param userRoleList 用户角色关系数据列表。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void addUserRoleList(List userRoleList) { + for (SysUserRole userRole : userRoleList) { + sysUserRoleMapper.insert(userRole); + } + } + + /** + * 移除指定用户和指定角色的关联关系。 + * + * @param roleId 角色主键Id。 + * @param userId 用户主键Id。 + * @return 移除成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean removeUserRole(Long roleId, Long userId) { + SysUserRole userRole = new SysUserRole(); + userRole.setRoleId(roleId); + userRole.setUserId(userId); + return sysUserRoleMapper.delete(new QueryWrapper<>(userRole)) == 1; + } + + /** + * 验证角色对象关联的数据是否都合法。 + * + * @param sysRole 当前操作的对象。 + * @param originalSysRole 原有对象。 + * @param menuIdListString 逗号分隔的menuId列表。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData(SysRole sysRole, SysRole originalSysRole, String menuIdListString) { + JSONObject jsonObject = null; + if (StringUtils.isNotBlank(menuIdListString)) { + Set menuIdSet = Arrays.stream( + menuIdListString.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysMenuService.existAllPrimaryKeys(menuIdSet)) { + return CallResult.error("数据验证失败,存在不合法的菜单权限,请刷新后重试!"); + } + jsonObject = new JSONObject(); + jsonObject.put("menuIdSet", menuIdSet); + } + return CallResult.ok(jsonObject); + } + + /** + * 查询角色的权限资源地址列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param url url过滤条件。 + * @return 包含从角色到权限资源的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysPermListWithDetail(Long roleId, String url) { + return sysRoleMapper.getSysPermListWithDetail(roleId, url); + } + + /** + * 查询角色的权限字列表。同时返回详细的分配路径。 + * + * @param roleId 角色Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从角色到权限字的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysPermCodeListWithDetail(Long roleId, String permCode) { + return sysRoleMapper.getSysPermCodeListWithDetail(roleId, permCode); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..c56f557 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java @@ -0,0 +1,760 @@ +package apelet.tenantadmin.upms.service.impl; + +import apelet.common.botp.db.util.DbUtil; +import apelet.common.core.base.dao.BaseDaoMapper; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.constant.GlobalDeletedFlag; +import apelet.common.core.constant.UserFilterGroup; +import apelet.common.core.exception.MyRuntimeException; +import apelet.common.core.object.*; +import apelet.common.core.util.MyModelUtil; +import apelet.common.core.util.MyPageUtil; +import apelet.common.eas.service.impl.EASSyncService; +import apelet.common.ext.base.BizWidgetDatasource; +import apelet.common.ext.constant.BizWidgetDatasourceType; +import apelet.common.ext.util.BizWidgetDatasourceExtHelper; +import apelet.common.generator.utils.OrmGenDataSourceUtil; +import apelet.common.sequence.wrapper.IdGeneratorWrapper; +import apelet.tenantadmin.upms.controller.SysUserController; +import apelet.tenantadmin.upms.dao.*; +import apelet.tenantadmin.upms.model.*; +import apelet.tenantadmin.upms.model.constant.SysUserStatus; +import apelet.tenantadmin.upms.service.*; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 用户管理数据操作服务类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@Service("sysUserService") +public class SysUserServiceImpl extends BaseService implements SysUserService, BizWidgetDatasource { + + @Autowired + private SysUserMapper sysUserMapper; + @Autowired + private SysUserRoleMapper sysUserRoleMapper; + @Autowired + private SysUserPostMapper sysUserPostMapper; + @Autowired + private SysPostService sysPostService; + @Autowired + private SysRoleService sysRoleService; + @Autowired + private SysDataPermService sysDataPermService; + @Autowired + private SysDataPermUserMapper sysDataPermUserMapper; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private IdGeneratorWrapper idGenerator; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private BizWidgetDatasourceExtHelper bizWidgetDatasourceExtHelper; + @Autowired + private EASSyncService easSyncService; + @Autowired + private RedissonClient redissonClient; + + /** + * 返回当前Service的主表Mapper对象。 + * + * @return 主表Mapper对象。 + */ + @Override + protected BaseDaoMapper mapper() { + return sysUserMapper; + } + + @PostConstruct + private void registerBizWidgetDatasource() { + bizWidgetDatasourceExtHelper.registerDatasource(BizWidgetDatasourceType.UPMS_USER_TYPE, this); + } + + @Override + public MyPageData> getDataList( + String type, Map filter, MyOrderParam orderParam, MyPageParam pageParam) { + if (pageParam != null) { + PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize(), pageParam.getCount()); + } + List userList = null; + String orderBy = MyOrderParam.buildOrderBy(orderParam, SysUser.class, false); + SysUser userFilter = BeanUtil.toBean(filter, SysUser.class); + if (filter != null) { + Object group = filter.get("USER_FILTER_GROUP"); + if (group != null) { + JSONObject filterGroupJson = JSON.parseObject(group.toString()); + String groupType = filterGroupJson.getString("type"); + String values = filterGroupJson.getString("values"); + if (UserFilterGroup.USER.equals(groupType)) { + List loginNames = StrUtil.splitTrim(values, ","); + userList = sysUserMapper.getSysUserListByLoginNames(loginNames, userFilter, orderBy); + } else { + Set groupIds = StrUtil.splitTrim(values, ",") + .stream().map(Long::valueOf).collect(Collectors.toSet()); + userList = this.getUserListByGroupIds(groupType, groupIds, userFilter, orderBy); + } + } + } + if (userList == null) { + userList = this.getSysUserList(userFilter, orderBy); + } + this.buildRelationForDataList(userList, MyRelationParam.dictOnly()); + return MyPageUtil.makeResponseData(userList, BeanUtil::beanToMap); + } + + private List getUserListByGroupIds(String groupType, Set groupIds, SysUser filter, String orderBy) { + if (groupType.equals(UserFilterGroup.DEPT)) { + return sysUserMapper.getSysUserListByDeptIds(groupIds, filter, orderBy); + } + List userIds = null; + switch (groupType) { + case UserFilterGroup.ROLE: + userIds = sysUserMapper.getUserIdListByRoleIds(groupIds, filter, orderBy); + break; + case UserFilterGroup.POST: + userIds = sysUserMapper.getUserIdListByPostIds(groupIds, filter, orderBy); + break; + case UserFilterGroup.DEPT_POST: + userIds = sysUserMapper.getUserIdListByDeptPostIds(groupIds, filter, orderBy); + break; + default: + break; + } + if (CollUtil.isEmpty(userIds)) { + return null; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysUser::getUserId, userIds); + if (StrUtil.isNotBlank(orderBy)) { + queryWrapper.last(" ORDER BY " + orderBy); + } + return sysUserMapper.selectList(queryWrapper); + } + + @Override + public List> getDataListWithInList(String type, String fieldName, List fieldValues) { + List userList; + if (StrUtil.isBlank(fieldName)) { + userList = this.getInList(fieldValues.stream().map(Long::valueOf).collect(Collectors.toSet())); + } else { + userList = this.getInList(fieldName, MyModelUtil.convertToTypeValues(SysUser.class, fieldName, fieldValues)); + } + this.buildRelationForDataList(userList, MyRelationParam.dictOnly()); + return MyModelUtil.beanToMapList(userList); + } + + /** + * 获取指定登录名的用户对象。 + * + * @param loginName 指定登录用户名。 + * @return 用户对象。 + */ + @Override + public SysUser getSysUserByLoginName(String loginName) { + SysUser filter = new SysUser(); + filter.setLoginName(loginName); + return sysUserMapper.selectOne(new QueryWrapper<>(filter)); + } + + /** + * 保存新增的用户对象。 + * + * @param user 新增的用户对象。 + * @param roleIdSet 用户角色Id集合。 + * @param deptPostIdSet 部门岗位Id集合。 + * @param dataPermIdSet 数据权限Id集合。 + * @return 新增后的用户对象。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet) { + user.setUserId(idGenerator.nextLongId()); + user.setPassword(passwordEncoder.encode(user.getPassword())); + user.setUserStatus(SysUserStatus.STATUS_NORMAL); + user.setDeletedFlag(GlobalDeletedFlag.NORMAL); + MyModelUtil.fillCommonsForInsert(user); + sysUserMapper.insert(user); + if (CollUtil.isNotEmpty(deptPostIdSet)) { + for (Long deptPostId : deptPostIdSet) { + SysDeptPost deptPost = sysDeptService.getSysDeptPost(deptPostId); + SysUserPost userPost = new SysUserPost(); + userPost.setUserId(user.getUserId()); + userPost.setDeptPostId(deptPostId); + userPost.setPostId(deptPost.getPostId()); + sysUserPostMapper.insert(userPost); + } + } + if (CollUtil.isNotEmpty(roleIdSet)) { + for (Long roleId : roleIdSet) { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(user.getUserId()); + userRole.setRoleId(roleId); + sysUserRoleMapper.insert(userRole); + } + } + if (CollUtil.isNotEmpty(dataPermIdSet)) { + for (Long dataPermId : dataPermIdSet) { + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setDataPermId(dataPermId); + dataPermUser.setUserId(user.getUserId()); + sysDataPermUserMapper.insert(dataPermUser); + } + } + return user; + } + + /** + * 更新用户对象。 + * + * @param user 更新的用户对象。 + * @param originalUser 原有的用户对象。 + * @param roleIdSet 用户角色Id列表。 + * @param deptPostIdSet 部门岗位Id集合。 + * @param dataPermIdSet 数据权限Id集合。 + * @return 更新成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet) { + user.setLoginName(originalUser.getLoginName()); + user.setPassword(originalUser.getPassword()); + MyModelUtil.fillCommonsForUpdate(user, originalUser); + UpdateWrapper uw = this.createUpdateQueryForNullValue(user, user.getUserId()); + if (sysUserMapper.update(user, uw) != 1) { + return false; + } + // 先删除原有的User-Post关联关系,再重新插入新的关联关系 + SysUserPost deletedUserPost = new SysUserPost(); + deletedUserPost.setUserId(user.getUserId()); + sysUserPostMapper.delete(new QueryWrapper<>(deletedUserPost)); + if (CollUtil.isNotEmpty(deptPostIdSet)) { + for (Long deptPostId : deptPostIdSet) { + SysDeptPost deptPost = sysDeptService.getSysDeptPost(deptPostId); + SysUserPost userPost = new SysUserPost(); + userPost.setUserId(user.getUserId()); + userPost.setDeptPostId(deptPostId); + userPost.setPostId(deptPost.getPostId()); + sysUserPostMapper.insert(userPost); + } + } + // 先删除原有的User-Role关联关系,再重新插入新的关联关系 + SysUserRole deletedUserRole = new SysUserRole(); + deletedUserRole.setUserId(user.getUserId()); + sysUserRoleMapper.delete(new QueryWrapper<>(deletedUserRole)); + if (CollUtil.isNotEmpty(roleIdSet)) { + for (Long roleId : roleIdSet) { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(user.getUserId()); + userRole.setRoleId(roleId); + sysUserRoleMapper.insert(userRole); + } + } + // 先删除原有的DataPerm-User关联关系,在重新插入新的关联关系 + SysDataPermUser deletedDataPermUser = new SysDataPermUser(); + deletedDataPermUser.setUserId(user.getUserId()); + sysDataPermUserMapper.delete(new QueryWrapper<>(deletedDataPermUser)); + if (CollUtil.isNotEmpty(dataPermIdSet)) { + for (Long dataPermId : dataPermIdSet) { + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setDataPermId(dataPermId); + dataPermUser.setUserId(user.getUserId()); + sysDataPermUserMapper.insert(dataPermUser); + } + } + return true; + } + + /** + * 修改用户密码。 + * + * @param userId 用户主键Id。 + * @param newPass 新密码。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean changePassword(Long userId, String newPass) { + SysUser updatedUser = new SysUser(); + updatedUser.setUserId(userId); + updatedUser.setPassword(passwordEncoder.encode(newPass)); + return sysUserMapper.updateById(updatedUser) == 1; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean changeHeadImage(Long userId, String newHeadImage) { + SysUser updatedUser = new SysUser(); + updatedUser.setUserId(userId); + updatedUser.setHeadImageUrl(newHeadImage); + return sysUserMapper.updateById(updatedUser) == 1; + } + + /** + * 删除指定数据。 + * + * @param userId 主键Id。 + * @return 成功返回true,否则false。 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean remove(Long userId) { + if (sysUserMapper.deleteById(userId) == 0) { + return false; + } + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(userId); + sysUserRoleMapper.delete(new QueryWrapper<>(userRole)); + SysUserPost userPost = new SysUserPost(); + userPost.setUserId(userId); + sysUserPostMapper.delete(new QueryWrapper<>(userPost)); + SysDataPermUser dataPermUser = new SysDataPermUser(); + dataPermUser.setUserId(userId); + sysDataPermUserMapper.delete(new QueryWrapper<>(dataPermUser)); + return true; + } + + /** + * 获取单表查询结果。由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysUserListWithRelation)方法。 + * + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysUserList(SysUser filter, String orderBy) { + return sysUserMapper.getSysUserList(null, null, filter, orderBy); + } + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 由于没有关联数据查询,因此在仅仅获取单表数据的场景下,效率更高。 + * 如果需要同时获取关联数据,请移步(getSysUserListWithRelation)方法。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 过滤对象。 + * @param orderBy 排序参数。 + * @return 查询结果集。 + */ + @Override + public List getSysUserList( + String inFilterField, Set inFilterValues, SysUser filter, String orderBy) { + String inFilterColumn = MyModelUtil.mapToColumnName(inFilterField, SysUser.class); + return sysUserMapper.getSysUserList(inFilterColumn, inFilterValues, filter, orderBy); + } + + /** + * 获取主表的查询结果,以及主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 该查询会涉及到一对一从表的关联过滤,或一对多从表的嵌套关联过滤,因此性能不如单表过滤。 + * 如果仅仅需要获取主表数据,请移步(getSysUserList),以便获取更好的查询性能。 + * + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + @Override + public List getSysUserListWithRelation(SysUser filter, String orderBy) { + List resultList = sysUserMapper.getSysUserList(null, null, filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + /** + * 获取主表的查询结果,查询条件中包括主表过滤对象和指定字段的(in list)过滤。 + * 同时还包含主表关联的字典数据和一对一从表数据,以及一对一从表的字典数据。 + * 如果仅仅需要获取主表数据,请移步(getSysUserList),以便获取更好的查询性能。 + * + * @param inFilterField (In-list)指定的字段(Java成员字段,而非数据列名)。 + * @param inFilterValues inFilterField指定字段的(In-list)数据列表。 + * @param filter 主表过滤对象。 + * @param orderBy 排序对象。 + * @return 查询结果集。 + */ + @Override + public List getSysUserListWithRelation( + String inFilterField, Set inFilterValues, SysUser filter, String orderBy) { + String inFilterColumn = MyModelUtil.mapToColumnName(inFilterField, SysUser.class); + List resultList = + sysUserMapper.getSysUserList(inFilterColumn, inFilterValues, filter, orderBy); + // 在缺省生成的代码中,如果查询结果resultList不是Page对象,说明没有分页,那么就很可能是数据导出接口调用了当前方法。 + // 为了避免一次性的大量数据关联,规避因此而造成的系统运行性能冲击,这里手动进行了分批次读取,开发者可按需修改该值。 + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.dictOnly(), batchSize); + return resultList; + } + + /** + * 获取指定角色的用户列表。 + * + * @param roleId 角色主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + @Override + public List getSysUserListByRoleId(Long roleId, SysUser filter, String orderBy) { + return sysUserMapper.getSysUserListByRoleId(roleId, filter, orderBy); + } + + /** + * 获取不属于指定角色的用户列表。 + * + * @param roleId 角色主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + @Override + public List getNotInSysUserListByRoleId(Long roleId, SysUser filter, String orderBy) { + return sysUserMapper.getNotInSysUserListByRoleId(roleId, filter, orderBy); + } + + /** + * 获取指定数据权限的用户列表。 + * + * @param dataPermId 数据权限主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + @Override + public List getSysUserListByDataPermId(Long dataPermId, SysUser filter, String orderBy) { + return sysUserMapper.getSysUserListByDataPermId(dataPermId, filter, orderBy); + } + + /** + * 获取不属于指定数据权限的用户列表。 + * + * @param dataPermId 数据权限主键Id。 + * @param filter 用户过滤对象。 + * @param orderBy 排序参数。 + * @return 用户列表。 + */ + @Override + public List getNotInSysUserListByDataPermId(Long dataPermId, SysUser filter, String orderBy) { + return sysUserMapper.getNotInSysUserListByDataPermId(dataPermId, filter, orderBy); + } + + @Override + public List getSysUserListByDeptPostId(Long deptPostId, SysUser filter, String orderBy) { + return sysUserMapper.getSysUserListByDeptPostId(deptPostId, filter, orderBy); + } + + @Override + public List getNotInSysUserListByDeptPostId(Long deptPostId, SysUser filter, String orderBy) { + return sysUserMapper.getNotInSysUserListByDeptPostId(deptPostId, filter, orderBy); + } + + @Override + public List getSysUserListByPostId(Long postId, SysUser filter, String orderBy) { + return sysUserMapper.getSysUserListByPostId(postId, filter, orderBy); + } + + /** + * 查询用户的权限资源地址列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param url url过滤条件。 + * @return 包含从用户到权限资源的完整权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysPermListWithDetail(Long userId, String url) { + return sysUserMapper.getSysPermListWithDetail(userId, url); + } + + /** + * 查询用户的权限字列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param permCode 权限字名称过滤条件。 + * @return 包含从用户到权限字的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysPermCodeListWithDetail(Long userId, String permCode) { + return sysUserMapper.getSysPermCodeListWithDetail(userId, permCode); + } + + /** + * 查询用户的菜单列表。同时返回详细的分配路径。 + * + * @param userId 用户Id。 + * @param menuName 菜单名称过滤条件。 + * @return 包含从用户到菜单的权限分配路径信息的查询结果列表。 + */ + @Override + public List> getSysMenuListWithDetail(Long userId, String menuName) { + return sysUserMapper.getSysMenuListWithDetail(userId, menuName); + } + + /** + * 验证用户对象关联的数据是否都合法。 + * + * @param sysUser 当前操作的对象。 + * @param originalSysUser 原有对象。 + * @param roleIds 逗号分隔的角色Id列表字符串。 + * @param deptPostIds 逗号分隔的部门岗位Id列表字符串。 + * @param dataPermIds 逗号分隔的数据权限Id列表字符串。 + * @return 验证结果。 + */ + @Override + public CallResult verifyRelatedData( + SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds) { + JSONObject jsonObject = new JSONObject(); + if (StrUtil.isBlank(deptPostIds)) { + return CallResult.error("数据验证失败,用户的部门岗位数据不能为空!"); + } + Set deptPostIdSet = + Arrays.stream(deptPostIds.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysPostService.existAllPrimaryKeys(deptPostIdSet, sysUser.getDeptId())) { + return CallResult.error("数据验证失败,存在不合法的用户岗位,请刷新后重试!"); + } + jsonObject.put("deptPostIdSet", deptPostIdSet); + if (StrUtil.isBlank(roleIds)) { + return CallResult.error("数据验证失败,用户的角色数据不能为空!"); + } + Set roleIdSet = Arrays.stream( + roleIds.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysRoleService.existAllPrimaryKeys(roleIdSet)) { + return CallResult.error("数据验证失败,存在不合法的用户角色,请刷新后重试!"); + } + jsonObject.put("roleIdSet", roleIdSet); + if (StrUtil.isBlank(dataPermIds)) { + return CallResult.error("数据验证失败,用户的数据权限不能为空!"); + } + Set dataPermIdSet = Arrays.stream( + dataPermIds.split(",")).map(Long::valueOf).collect(Collectors.toSet()); + if (!sysDataPermService.existAllPrimaryKeys(dataPermIdSet)) { + return CallResult.error("数据验证失败,存在不合法的数据权限,请刷新后重试!"); + } + jsonObject.put("dataPermIdSet", dataPermIdSet); + if (this.needToVerify(sysUser, originalSysUser, SysUser::getDeptId) + && !sysDeptService.existId(sysUser.getDeptId())) { + return CallResult.error("数据验证失败,关联的用户部门Id并不存在,请刷新后重试!"); + } + return CallResult.ok(jsonObject); + } + + /** + * 根据云之家openId获取当前用户信息 + * + * @param openId 云之家用户openId + * @return + */ + @Override + public SysUser getUserByHubOpenId(String openId) { + return sysUserMapper.getUserByHubOpenId(openId); + } + + /** + * 根据钉钉userId(暂时使用userid,后续可能修改)获取当前用户信息 + * + * @param userId 钉钉用户userId + * @return + */ + @Override + public SysUser getUserByDingTalkOpenId(String userId) { + return sysUserMapper.getUserByDingTalkOpenId(userId); + } + + + @Override + public boolean mySaveBatch(List sysUserList) { + sysUserMapper.insertList(sysUserList); + return true; + } + + @Override + public boolean myUpdateBatch(List sysUserList) { + + return true; + } + + + private static final String LOCK_KEY = "lock:syncEasUser"; + + + @Override + public ResponseResult syncEasUSer() { + RLock lock = redissonClient.getLock(LOCK_KEY); + while (true) { + try { + // 尝试获取锁,等待时间为10秒,锁定时间为5秒 + if (lock.tryLock(5, 10, TimeUnit.MINUTES)) { + try { + JSONObject param = new JSONObject(); + param.put("method", "getEasPerson"); + JSONObject jsonObject = easSyncService.getEasData(param); + if (!jsonObject.containsKey("data")) { + throw new MyRuntimeException("返回数据错误"); + } + JSONObject data = jsonObject.getJSONObject("data"); + JSONArray array = data.getJSONArray("rows"); + log.warn("======================syncEasUser======================array == > {}", array.size()); + List users = new ArrayList<>(); + String password = passwordEncoder.encode("qwe!@#123ASD"); + for (Object o : array) { + JSONObject object = (JSONObject) o; + SysUser sysUser = new SysUser(); + sysUser.setUserId(idGenerator.nextLongId()); + sysUser.setEasId(object.getString("id") == null ? "0" : object.getString("id")); + sysUser.setShowName(object.getString("personName") == null ? "" : object.getString("personName")); + sysUser.setDingtalkId(object.getString("dd_user") == null ? "" : object.getString("dd_user")); + sysUser.setCloudhubId(object.getString("openId") == null ? "0" : object.getString("openId")); + sysUser.setUserStatus(object.getInteger("status").equals(1) ? 0 : 1); + sysUser.setLoginName(object.getString("personNumber") == null ? "" : object.getString("personNumber")); + sysUser.setDeptId(0L); + sysUser.setPassword(password); + sysUser.setUserType(0); + sysUser.setDeletedFlag(1); + sysUser.setCreateUserId(0L); + sysUser.setCreateTime(new Date()); + sysUser.setUpdateUserId(0L); + sysUser.setUpdateTime(new Date()); + users.add(sysUser); + } + List list = list(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getEasId, SysUser::getLoginName, SysUser::getCloudhubId, + SysUser::getUserStatus, SysUser::getShowName) + ); + // 正式环境 + Map userMap = new HashMap<>(); + list.forEach(f -> userMap.put(f.getEasId() + f.getCloudhubId(), f)); + ArrayList save = new ArrayList<>(); + ArrayList update = new ArrayList<>(); + for (SysUser user : users) { + SysUser sysUser = userMap.get(user.getEasId() + user.getCloudhubId()); + if (sysUser != null) { + if (user.getLoginName().equals(sysUser.getLoginName()) && + user.getCloudhubId().equals(sysUser.getCloudhubId()) && + user.getUserStatus().equals(sysUser.getUserStatus()) && + user.getShowName().equals(sysUser.getShowName()) + ) { + continue; + } + user.setUpdateTime(new Date()); + user.setUserId(sysUser.getUserId()); + update.add(user); + } else { + sysUser = userMap.get(user.getEasId() + 0); + if (sysUser != null) { + user.setUpdateTime(new Date()); + user.setUserId(sysUser.getUserId()); + update.add(user); + }else{ + save.add(user); + } + } + } + + int sized = save.size(); + log.warn("save 保存 数据大小 = {}", sized); + if(save.isEmpty() && update.isEmpty()){ + return ResponseResult.success("同步eas用户, 没有需要同步的数据"); + } + if(!save.isEmpty()){ + int mo = 3000; + int num = sized / mo; + ExecutorService executorService = Executors.newFixedThreadPool(30); + int start; + int end; + log.warn(" 开始 多线程 size {}", num); + for (int i = 0; i <= num; i++) { + if (i == 0) { + start = i; + end = sized > mo ? ((i + 1) * mo) - 1 : sized; + } else if (i == num) { + start = (i * mo); + end = sized; + } else { + start = (i * mo); + end = ((i + 1) * mo) - 1; + } + try { + log.warn(" 开始 多线程 提交任务 start :{} , end :{}", start, end); + List sysUsers = save.subList(start, end); + Runnable task = new Task(sysUsers); + executorService.submit(task); + } catch (Exception e) { + log.error("start = " + start + " --- end =" + end); + } + } + executorService.shutdown(); + } + if(!update.isEmpty()){ + update.forEach(this::updateById); + } + return ResponseResult.success("同步数据完成"); + } catch (Exception e) { + e.printStackTrace(); + log.error("e {} ", e.getMessage()); + } finally { + lock.unlock(); + break; // 退出循环,因为任务已经完成 + } + } else { + throw new MyRuntimeException("当前正在同步Eas用户, 请稍等..."); + } + } catch (InterruptedException e) { + lock.unlock(); + e.printStackTrace(); + break; // 退出循环,因为发生了中断异常 + } + } + return ResponseResult.success("同步数据失败"); + } + + class Task implements Runnable { + private final List userList; + + public Task(List sysUsers) { + this.userList = sysUsers; + } + + @Override + public void run() { + log.warn("Task is running on thread ID: {}", Thread.currentThread().getId()); + log.warn("Task is running on thread Name : {}", Thread.currentThread().getName()); + try { + mySaveBatch(userList); + } catch (Exception e) { + log.error("Task is running on thread ID ,{} thread Name : {} , error info: {}", Thread.currentThread().getId() + , Thread.currentThread().getName(), e.getMessage()); + } + log.warn("Task is running on thread completed"); + } + } + + +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/CaptchaCacheServiceRedisImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/CaptchaCacheServiceRedisImpl.java new file mode 100644 index 0000000..a2ae2c6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/CaptchaCacheServiceRedisImpl.java @@ -0,0 +1,48 @@ +package apelet.tenantadmin.upms.util; + +import com.anji.captcha.service.CaptchaCacheService; +import org.redisson.api.RAtomicLong; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.concurrent.TimeUnit; + +/** + * Aj验证码 redis实现 + */ +public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService { + + @Autowired + private RedissonClient redissonClient; + + @Override + public String type() { + return "redis"; + } + + @Override + public void set(String key, String value, long expiresInSeconds) { + redissonClient.getBucket(key).set(value, expiresInSeconds, TimeUnit.SECONDS); + } + + @Override + public boolean exists(String key) { + return redissonClient.getBucket(key).isExists(); + } + + @Override + public void delete(String key) { + redissonClient.getBucket(key).delete(); + } + + @Override + public String get(String key) { + return (String) redissonClient.getBucket(key).get(); + } + + @Override + public Long increment(String key, long val) { + RAtomicLong atomicLong = redissonClient.getAtomicLong(key); + return atomicLong.addAndGet(val); + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/FlowIdentityExtHelper.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/FlowIdentityExtHelper.java new file mode 100644 index 0000000..360d361 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/util/FlowIdentityExtHelper.java @@ -0,0 +1,244 @@ +package apelet.tenantadmin.upms.util; + +import apelet.common.flow.util.BaseFlowIdentityExtHelper; +import apelet.common.flow.util.FlowCustomExtFactory; +import apelet.common.flow.vo.FlowUserInfoVo; +import apelet.tenantadmin.upms.model.SysDept; +import apelet.tenantadmin.upms.model.SysDeptPost; +import apelet.tenantadmin.upms.model.SysUser; +import apelet.tenantadmin.upms.model.constant.SysUserStatus; +import apelet.tenantadmin.upms.service.SysDeptService; +import apelet.tenantadmin.upms.service.SysUserService; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 为流程提供所需的用户身份相关的等扩展信息的帮助类。 + * + * @author guifc + * @date 2023-08-04 + */ +@Slf4j +@Component +public class FlowIdentityExtHelper implements BaseFlowIdentityExtHelper { + + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysUserService sysUserService; + @Autowired + private FlowCustomExtFactory flowCustomExtFactory; + + @PostConstruct + public void doRegister() { + flowCustomExtFactory.registerFlowIdentityExtHelper(this); + } + + @Override + public Long getLeaderDeptPostId(Long deptId) { + List deptPostIdList = sysDeptService.getLeaderDeptPostIdList(deptId); + return CollUtil.isEmpty(deptPostIdList) ? null : deptPostIdList.get(0); + } + + @Override + public Long getUpLeaderDeptPostId(Long deptId) { + List deptPostIdList = sysDeptService.getUpLeaderDeptPostIdList(deptId); + return CollUtil.isEmpty(deptPostIdList) ? null : deptPostIdList.get(0); + } + + @Override + public Map getDeptPostIdMap(Long deptId, Set postIdSet) { + Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + List deptPostList = sysDeptService.getSysDeptPostList(deptId, postIdSet2); + if (CollUtil.isEmpty(deptPostList)) { + return null; + } + Map resultMap = new HashMap<>(deptPostList.size()); + deptPostList.forEach(sysDeptPost -> + resultMap.put(sysDeptPost.getPostId().toString(), sysDeptPost.getDeptPostId().toString())); + return resultMap; + } + + @Override + public Map getSiblingDeptPostIdMap(Long deptId, Set postIdSet) { + Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + List deptPostList = sysDeptService.getSiblingSysDeptPostList(deptId, postIdSet2); + if (CollUtil.isEmpty(deptPostList)) { + return null; + } + Map resultMap = new HashMap<>(deptPostList.size()); + for (SysDeptPost deptPost : deptPostList) { + String deptPostId = resultMap.get(deptPost.getPostId().toString()); + if (deptPostId != null) { + deptPostId = deptPostId + "," + deptPost.getDeptPostId(); + } else { + deptPostId = deptPost.getDeptPostId().toString(); + } + resultMap.put(deptPost.getPostId().toString(), deptPostId); + } + return resultMap; + } + + @Override + public Map getUpDeptPostIdMap(Long deptId, Set postIdSet) { + SysDept sysDept = sysDeptService.getById(deptId); + if (sysDept == null || sysDept.getParentId() == null) { + return null; + } + return getDeptPostIdMap(sysDept.getParentId(), postIdSet); + } + + @Override + public Set getUsernameListByRoleIds(Set roleIdSet) { + Set usernameSet = new HashSet<>(); + Set roleIdSet2 = roleIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long roleId : roleIdSet2) { + List userList = sysUserService.getSysUserListByRoleId(roleId, filter, null); + this.extractAndAppendUsernameList(usernameSet, userList); + } + return usernameSet; + } + + @Override + public List getUserInfoListByRoleIds(Set roleIdSet) { + List resultList = new LinkedList<>(); + Set roleIdSet2 = roleIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long roleId : roleIdSet2) { + List userList = sysUserService.getSysUserListByRoleId(roleId, filter, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class)); + } + } + return resultList; + } + + @Override + public Set getUsernameListByDeptIds(Set deptIdSet) { + Set usernameSet = new HashSet<>(); + Set deptIdSet2 = deptIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + for (Long deptId : deptIdSet2) { + SysUser filter = new SysUser(); + filter.setDeptId(deptId); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + List userList = sysUserService.getSysUserList(filter, null); + this.extractAndAppendUsernameList(usernameSet, userList); + } + return usernameSet; + } + + @Override + public List getUserInfoListByDeptIds(Set deptIdSet) { + List resultList = new LinkedList<>(); + Set deptIdSet2 = deptIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + for (Long deptId : deptIdSet2) { + SysUser filter = new SysUser(); + filter.setDeptId(deptId); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + List userList = sysUserService.getSysUserList(filter, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class)); + } + } + return resultList; + } + + @Override + public Set getUsernameListByPostIds(Set postIdSet) { + Set usernameSet = new HashSet<>(); + Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long postId : postIdSet2) { + List userList = sysUserService.getSysUserListByPostId(postId, filter, null); + this.extractAndAppendUsernameList(usernameSet, userList); + } + return usernameSet; + } + + @Override + public List getUserInfoListByPostIds(Set postIdSet) { + List resultList = new LinkedList<>(); + Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long postId : postIdSet2) { + List userList = sysUserService.getSysUserListByPostId(postId, filter, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class)); + } + } + return resultList; + } + + @Override + public Set getUsernameListByDeptPostIds(Set deptPostIdSet) { + Set usernameSet = new HashSet<>(); + Set deptPostIdSet2 = deptPostIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long deptPostId : deptPostIdSet2) { + List userList = sysUserService.getSysUserListByDeptPostId(deptPostId, filter, null); + this.extractAndAppendUsernameList(usernameSet, userList); + } + return usernameSet; + } + + @Override + public List getUserInfoListByDeptPostIds(Set deptPostIdSet) { + List resultList = new LinkedList<>(); + Set deptPostIdSet2 = deptPostIdSet.stream().map(Long::valueOf).collect(Collectors.toSet()); + SysUser filter = new SysUser(); + filter.setUserStatus(SysUserStatus.STATUS_NORMAL); + for (Long deptPostId : deptPostIdSet2) { + List userList = sysUserService.getSysUserListByDeptPostId(deptPostId, filter, null); + if (CollUtil.isNotEmpty(userList)) { + resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class)); + } + } + return resultList; + } + + @Override + public List getUserInfoListByUsernameSet(Set usernameSet) { + List resultList = null; + List userList = sysUserService.getInList("loginName", usernameSet); + if (CollUtil.isNotEmpty(userList)) { + resultList = BeanUtil.copyToList(userList, FlowUserInfoVo.class); + } + return resultList; + } + + @Override + public Boolean supprtDataPerm() { + return true; + } + + @Override + public Map mapUserShowNameByLoginName(Set loginNameSet) { + if (CollUtil.isEmpty(loginNameSet)) { + return new HashMap<>(1); + } + Map resultMap = new HashMap<>(loginNameSet.size()); + List userList = sysUserService.getInList("loginName", loginNameSet); + userList.forEach(user -> resultMap.put(user.getLoginName(), user.getShowName())); + return resultMap; + } + + private void extractAndAppendUsernameList(Set resultUsernameList, List userList) { + List usernameList = userList.stream().map(SysUser::getLoginName).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(usernameList)) { + resultUsernameList.addAll(usernameList); + } + } +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermDeptVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermDeptVo.java new file mode 100644 index 0000000..ffccc9d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermDeptVo.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.upms.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 数据权限与部门关联VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限与部门关联VO") +@Data +public class SysDataPermDeptVo { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id") + private Long dataPermId; + + /** + * 关联部门Id。 + */ + @Schema(description = "关联部门Id") + private Long deptId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermMenuVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermMenuVo.java new file mode 100644 index 0000000..f25104e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermMenuVo.java @@ -0,0 +1,27 @@ +package apelet.tenantadmin.upms.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 数据权限与菜单关联VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限与菜单关联VO") +@Data +public class SysDataPermMenuVo { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id") + private Long dataPermId; + + /** + * 关联菜单Id。 + */ + @Schema(description = "关联菜单Id") + private Long menuId; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermVo.java new file mode 100644 index 0000000..f913fd1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDataPermVo.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 数据权限VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "数据权限VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysDataPermVo extends BaseVo { + + /** + * 数据权限Id。 + */ + @Schema(description = "数据权限Id") + private Long dataPermId; + + /** + * 显示名称。 + */ + @Schema(description = "显示名称") + private String dataPermName; + + /** + * 数据权限规则类型(0: 全部可见 1: 只看自己 2: 只看本部门 3: 本部门及子部门 4: 多部门及子部门 5: 自定义部门列表)。 + */ + @Schema(description = "数据权限规则类型") + private Integer ruleType; + + /** + * 部门Id列表(逗号分隔)。 + */ + @Schema(description = "部门Id列表") + private String deptIdListString; + + /** + * 数据权限与部门关联对象列表。 + */ + @Schema(description = "数据权限与部门关联对象列表") + private List> dataPermDeptList; + + /** + * 数据权限与菜单关联对象列表。 + */ + @Schema(description = "数据权限与菜单关联对象列表") + private List> dataPermMenuList; + + /** + * 数据权限与移动端入口关联对象列表。 + */ + @Schema(description = "数据权限与移动端入口关联对象列表") + private List> dataPermMobileEntryList; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptPostVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptPostVo.java new file mode 100644 index 0000000..ab21522 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptPostVo.java @@ -0,0 +1,39 @@ +package apelet.tenantadmin.upms.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 部门岗位VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "部门岗位VO") +@Data +public class SysDeptPostVo { + + /** + * 部门岗位Id。 + */ + @Schema(description = "部门岗位Id") + private Long deptPostId; + + /** + * 部门Id。 + */ + @Schema(description = "部门Id") + private Long deptId; + + /** + * 岗位Id。 + */ + @Schema(description = "岗位Id") + private Long postId; + + /** + * 部门岗位显示名称。 + */ + @Schema(description = "部门岗位显示名称") + private String postShowName; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptVo.java new file mode 100644 index 0000000..623cb2e --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptVo.java @@ -0,0 +1,65 @@ +package apelet.tenantadmin.upms.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +/** + * SysDeptVO视图对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "SysDeptVO视图对象") +@Data +public class SysDeptVo { + + /** + * 部门Id。 + */ + @Schema(description = "部门Id") + private Long deptId; + + /** + * 部门名称。 + */ + @Schema(description = "部门名称") + private String deptName; + + /** + * 显示顺序。 + */ + @Schema(description = "显示顺序") + private Integer showOrder; + + /** + * 父部门Id。 + */ + @Schema(description = "父部门Id") + private Long parentId; + + /** + * 创建者Id。 + */ + @Schema(description = "创建者Id") + private Long createUserId; + + /** + * 更新者Id。 + */ + @Schema(description = "更新者Id") + private Long updateUserId; + + /** + * 创建时间。 + */ + @Schema(description = "创建时间") + private Date createTime; + + /** + * 更新时间。 + */ + @Schema(description = "更新时间") + private Date updateTime; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysMenuVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysMenuVo.java new file mode 100644 index 0000000..a5257c1 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysMenuVo.java @@ -0,0 +1,103 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 菜单VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "菜单VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysMenuVo extends BaseVo { + + /** + * 菜单Id。 + */ + @Schema(description = "菜单Id") + private Long menuId; + + /** + * 父菜单Id,目录菜单的父菜单为null + */ + @Schema(description = "父菜单Id") + private Long parentId; + + /** + * 菜单显示名称。 + */ + @Schema(description = "菜单显示名称") + private String menuName; + + /** + * 菜单类型 (0: 目录 1: 菜单 2: 按钮 3: UI片段)。 + */ + @Schema(description = "菜单类型") + private Integer menuType; + + /** + * 前端表单路由名称,仅用于menu_type为1的菜单类型。 + */ + @Schema(description = "前端表单路由名称") + private String formRouterName; + + /** + * 在线表单主键Id,仅用于在线表单绑定的菜单。 + */ + @Schema(description = "在线表单主键Id") + private Long onlineFormId; + + /** + * 在线表单菜单的权限控制类型,具体值可参考SysOnlineMenuPermType常量对象。 + */ + @Schema(description = "在线表单菜单的权限控制类型") + private Integer onlineMenuPermType; + + /** + * 统计页面主键Id,仅用于统计页面绑定的菜单。 + */ + @Schema(description = "统计页面主键Id") + private Long reportPageId; + + /** + * 仅用于在线表单的流程Id。 + */ + @Schema(description = "仅用于在线表单的流程Id") + private Long onlineFlowEntryId; + + /** + * 菜单显示顺序 (值越小,排序越靠前)。 + */ + @Schema(description = "菜单显示顺序") + private Integer showOrder; + + /** + * 菜单图标。 + */ + @Schema(description = "菜单显示顺序") + private String icon; + + /** + * 附加信息。 + */ + @Schema(description = "附加信息") + private String extraData; + + @Schema(description = "关闭菜单缓存") + private Integer closeCache; + + + /** + * 菜单与权限字关联对象列表。 + */ + @Schema(description = "菜单与权限字关联对象列表") + private List> sysMenuPermCodeList; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermCodeVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermCodeVo.java new file mode 100644 index 0000000..7f252a7 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermCodeVo.java @@ -0,0 +1,63 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 权限字VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限字VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysPermCodeVo extends BaseVo { + + /** + * 权限字Id。 + */ + @Schema(description = "权限字Id") + private Long permCodeId; + + /** + * 权限字标识(一般为有含义的英文字符串)。 + */ + @Schema(description = "权限字标识") + private String permCode; + + /** + * 上级权限字Id。 + */ + @Schema(description = "上级权限字Id") + private Long parentId; + + /** + * 权限字类型(0: 表单 1: UI片段 2: 操作)。 + */ + @Schema(description = "权限字类型") + private Integer permCodeType; + + /** + * 显示名称。 + */ + @Schema(description = "显示名称") + private String showName; + + /** + * 显示顺序(数值越小,越靠前)。 + */ + @Schema(description = "显示顺序") + private Integer showOrder; + + /** + * 权限字与权限资源关联对象列表。 + */ + @Schema(description = "权限字与权限资源关联对象列表") + private List> sysPermCodePermList; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermModuleVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermModuleVo.java new file mode 100644 index 0000000..b26d33d --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermModuleVo.java @@ -0,0 +1,56 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 权限资源模块VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限资源模块VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysPermModuleVo extends BaseVo { + + /** + * 权限模块Id。 + */ + @Schema(description = "权限模块Id") + private Long moduleId; + + /** + * 权限模块名称。 + */ + @Schema(description = "权限模块名称") + private String moduleName; + + /** + * 上级权限模块Id。 + */ + @Schema(description = "上级权限模块Id") + private Long parentId; + + /** + * 权限模块类型(0: 普通模块 1: Controller模块)。 + */ + @Schema(description = "权限模块类型") + private Integer moduleType; + + /** + * 权限模块在当前层级下的顺序,由小到大。 + */ + @Schema(description = "显示顺序") + private Integer showOrder; + + /** + * 权限资源对象列表。 + */ + @Schema(description = "权限资源对象列表") + private List sysPermList; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermVo.java new file mode 100644 index 0000000..2ce8231 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPermVo.java @@ -0,0 +1,62 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 权限资源VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "权限资源VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysPermVo extends BaseVo { + + /** + * 权限资源Id。 + */ + @Schema(description = "权限资源Id") + private Long permId; + + /** + * 权限资源名称。 + */ + @Schema(description = "权限资源名称") + private String permName; + + /** + * shiro格式的权限字,如(upms:sysUser:add)。 + */ + @Schema(description = "权限字") + private String permCode; + + /** + * 权限所在的权限模块Id。 + */ + @Schema(description = "权限所在的权限模块Id") + private Long moduleId; + + /** + * 关联的URL。 + */ + @Schema(description = "关联的URL") + private String url; + + /** + * 权限在当前模块下的顺序,由小到大。 + */ + @Schema(description = "显示顺序") + private Integer showOrder; + + /** + * 模块Id的字典关联数据。 + */ + @Schema(description = "模块Id的字典关联数据") + private Map moduleIdDictMap; +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPostVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPostVo.java new file mode 100644 index 0000000..ec9f5eb --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysPostVo.java @@ -0,0 +1,50 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +/** + * 岗位VO对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "岗位VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysPostVo extends BaseVo { + + /** + * 岗位Id。 + */ + @Schema(description = "岗位Id") + private Long postId; + + /** + * 岗位名称。 + */ + @Schema(description = "岗位名称") + private String postName; + + /** + * 岗位层级,数值越小级别越高。 + */ + @Schema(description = "岗位层级,数值越小级别越高") + private Integer postLevel; + + /** + * 是否领导岗位。 + */ + @Schema(description = "是否领导岗位") + private Boolean leaderPost; + + /** + * postId 的多对多关联表数据对象,数据对应类型为SysDeptPostVo。 + */ + @Schema(description = "postId 的多对多关联表数据对象") + private Map sysDeptPost; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysRoleVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysRoleVo.java new file mode 100644 index 0000000..4efc79c --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysRoleVo.java @@ -0,0 +1,39 @@ +package apelet.tenantadmin.upms.vo; + +import apelet.common.core.base.vo.BaseVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; + +/** + * 角色VO。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "角色VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SysRoleVo extends BaseVo { + + /** + * 角色Id。 + */ + @Schema(description = "角色Id") + private Long roleId; + + /** + * 角色名称。 + */ + @Schema(description = "角色名称") + private String roleName; + + /** + * 角色与菜单关联对象列表。 + */ + @Schema(description = "角色与菜单关联对象列表") + private List> sysRoleMenuList; +} diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysUserVo.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysUserVo.java new file mode 100644 index 0000000..25c11ea --- /dev/null +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysUserVo.java @@ -0,0 +1,133 @@ +package apelet.tenantadmin.upms.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * SysUserVO视图对象。 + * + * @author guifc + * @date 2023-08-04 + */ +@Schema(description = "SysUserVO视图对象") +@Data +public class SysUserVo { + + /** + * 用户Id。 + */ + @Schema(description = "用户Id") + private Long userId; + + /** + * 登录用户名。 + */ + @Schema(description = "登录用户名") + private String loginName; + + /** + * 用户部门Id。 + */ + @Schema(description = "用户部门Id") + private Long deptId; + + /** + * 用户显示名称。 + */ + @Schema(description = "用户显示名称") + private String showName; + + /** + * 用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)。 + */ + @Schema(description = "用户类型(0: 管理员 1: 系统管理用户 2: 系统业务用户)") + private Integer userType; + + /** + * 用户头像的Url。 + */ + @Schema(description = "用户头像的Url") + private String headImageUrl; + + /** + * 用户状态(0: 正常 1: 锁定)。 + */ + @Schema(description = "用户状态(0: 正常 1: 锁定)") + private Integer userStatus; + + /** + * 用户邮箱。 + */ + @Schema(description = "用户邮箱") + private String email; + + /** + * 用户手机。 + */ + @Schema(description = "用户手机") + private String mobile; + + /** + * 创建者Id。 + */ + @Schema(description = "创建者Id") + private Long createUserId; + + /** + * 更新者Id。 + */ + @Schema(description = "更新者Id") + private Long updateUserId; + + /** + * 创建时间。 + */ + @Schema(description = "创建时间") + private Date createTime; + + /** + * 更新时间。 + */ + @Schema(description = "更新时间") + private Date updateTime; + + /** + * 多对多用户岗位数据集合。 + */ + @Schema(description = "多对多用户岗位数据集合") + private List> sysUserPostList; + + /** + * 多对多用户角色数据集合。 + */ + @Schema(description = "多对多用户角色数据集合") + private List> sysUserRoleList; + + /** + * 多对多用户数据权限数据集合。 + */ + @Schema(description = "多对多用户数据权限数据集合") + private List> sysDataPermUserList; + + /** + * deptId 字典关联数据。 + */ + @Schema(description = "deptId 字典关联数据") + private Map deptIdDictMap; + + /** + * userType 常量字典关联数据。 + */ + @Schema(description = "userType 常量字典关联数据") + private Map userTypeDictMap; + + /** + * userStatus 常量字典关联数据。 + */ + @Schema(description = "userStatus 常量字典关联数据") + private Map userStatusDictMap; +} diff --git a/application-tenant/tenant-admin/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService b/application-tenant/tenant-admin/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService new file mode 100644 index 0000000..d3dee9b --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService @@ -0,0 +1 @@ +apelet.tenantadmin.upms.util.CaptchaCacheServiceRedisImpl \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/resources/application-config.yml b/application-tenant/tenant-admin/src/main/resources/application-config.yml new file mode 100644 index 0000000..158d786 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/application-config.yml @@ -0,0 +1,161 @@ +server: + tomcat: + uri-encoding: UTF-8 + threads: + max: 100 + min-spare: 10 + servlet: + encoding: + force: true + charset: UTF-8 + enabled: true + +logging: + config: classpath:logback-spring.xml + +spring: + servlet: + multipart: + max-file-size: 50MB + max-request-size: 50MB + mvc: + converters: + preferred-json-mapper: fastjson + freemarker: + template-loader-path: classpath:/template/ + cache: false + charset: UTF-8 + check-template-location: true + content-type: text/html + expose-request-attributes: false + expose-session-attributes: false + request-context-attribute: request + suffix: .ftl + cloud: + sentinel: + transport: + # sentinel控制台地址。 + # 由于8080端口容易与其他服务端口冲突,可以在启动sentinel控制台时动态修改,如: + # java -Dserver.port=8858 -jar sentinel-dashboard-$VERSION.jar + # 改为8858后,下面的配置端口也需要改为:localhost:8858。 + dashboard: localhost:8858 + +# feign 配置 +feign: + sentinel: + enabled: false + httpclient: + enabled: true + max-connections: 200 + max-connections-per-route: 50 + client: + config: + default: + # 输出feignclient的日志,缺省值为none,可选值为none/basic/headers/full + # 注意:需要把feignclient类所在包的日志级别设置为debug时才生效。如: + # logging: + # level: + # com.demo.multi: info + # com.demo.multi.coursepaperinterface.client: debug + loggerLevel: full + connectTimeout: 5000 + readTimeout: 10000 + compression: + request: + enabled: true + response: + enabled: true + +common-core: + # 可选值为 mysql / postgresql / oracle / dm8 / kingbase + databaseType: mysql + +swagger: + # 当enabled为false的时候,则可禁用swagger。 + enabled: true + # 工程的基础包名。 + basePackage: apelet + # 工程服务的基础包名。 + serviceBasePackage: + title: 小猿微服务工程 + description: 小猿微服务工程详情 + version: 1.0 +knife4j: + # 打开knife4j增强模式。 + enable: true + +aliyun: + oss: + enabled: false + expireSeconds: 1000 + # 下面几项均需在申请阿里云OSS后,根据自己的实际情况进行配置。 + endpoint: + accessKey: + secretKey: + bucketName: + +minio: + enabled: false + endpoint: + accessKey: + secretKey: + bucketName: + +# 暴露监控端点 +management: + metrics: + tags: + application: ${spring.application.name} + endpoints: + web: + exposure: + include: '*' + jmx: + exposure: + include: '*' + endpoint: + # 与中间件相关的健康详情也会被展示 + health: + show-details: always + configprops: + # 在/actuator/configprops中,所有包含password的配置,将用 * 隐藏。 + # 如果不想隐藏任何配置项的值,可以直接使用如下被注释的空值。 + # keys-to-sanitize: + keys-to-sanitize: password + server: + base-path: "/" + +# 存储session数据的Redis,所有服务均需要,因此放到公共配置中。 +# 根据实际情况,该Redis也可以用于存储其他数据。 +common-redis: + # redisson的配置。每个服务可以自己的配置文件中覆盖此选项。 + redisson: + # 如果该值为false,系统将不会创建RedissionClient的bean。 + enabled: true + # mode的可用值为,single/cluster/sentinel/master-slave + mode: single + # single: 单机模式 + # address: redis://localhost:6379 + # cluster: 集群模式 + # 每个节点逗号分隔,同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + # sentinel: + # 每个节点逗号分隔,同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + # master-slave: + # 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + address: redis://192.168.100.25:6379 + # 链接超时,单位毫秒。 + timeout: 6000 + # 单位毫秒。分布式锁的超时检测时长。 + # 如果一次锁内操作超该毫秒数,或在释放锁之前异常退出,Redis会在该时长之后主动删除该锁使用的key。 + lockWatchdogTimeout: 60000 + # redis 密码,空可以不填。 + password: + database: 0 + pool: + # 连接池数量。 + poolSize: 20 + # 连接池中最小空闲数量。 + minIdle: 5 diff --git a/application-tenant/tenant-admin/src/main/resources/bootstrap.yml b/application-tenant/tenant-admin/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..7b52d96 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/bootstrap.yml @@ -0,0 +1,12 @@ +spring: + application: + name: tenant-admin + profiles: + active: @profile.name@ + main: + allow-bean-definition-overriding: true + allow-circular-references: true + config: + import: + - classpath:tenant-admin-dev.yml + - classpath:application-config.yml # 引入共享配置 \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg1.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg1.png new file mode 100644 index 0000000..028073a Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg1.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg10.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg10.png new file mode 100644 index 0000000..022aabf Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg10.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg11.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg11.png new file mode 100644 index 0000000..914908e Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg11.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg12.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg12.png new file mode 100644 index 0000000..f0f3ce5 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg12.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg13.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg13.png new file mode 100644 index 0000000..c5697f3 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg13.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg14.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg14.png new file mode 100644 index 0000000..e29e7a3 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg14.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg15.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg15.png new file mode 100644 index 0000000..2425f41 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg15.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg16.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg16.png new file mode 100644 index 0000000..c1730ad Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg16.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg17.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg17.png new file mode 100644 index 0000000..10ea85b Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg17.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg18.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg18.png new file mode 100644 index 0000000..a63f0ac Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg18.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg19.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg19.png new file mode 100644 index 0000000..fe9c4cc Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg19.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg2.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg2.png new file mode 100644 index 0000000..7167036 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg2.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg20.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg20.png new file mode 100644 index 0000000..7db9563 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg20.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg3.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg3.png new file mode 100644 index 0000000..e932d2c Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg3.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg4.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg4.png new file mode 100644 index 0000000..63b2005 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg4.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg5.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg5.png new file mode 100644 index 0000000..4281ad4 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg5.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg6.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg6.png new file mode 100644 index 0000000..ac544e3 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg6.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg7.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg7.png new file mode 100644 index 0000000..c6df4f7 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg7.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg8.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg8.png new file mode 100644 index 0000000..3b07dcd Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg8.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg9.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg9.png new file mode 100644 index 0000000..4b9844a Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/original/bg9.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/1.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/1.png new file mode 100644 index 0000000..1905026 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/1.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/10.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/10.png new file mode 100644 index 0000000..e651cf4 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/10.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/11.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/11.png new file mode 100644 index 0000000..5a33509 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/11.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/2.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/2.png new file mode 100644 index 0000000..b1482d4 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/2.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/3.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/3.png new file mode 100644 index 0000000..cdbb0b1 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/3.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/4.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/4.png new file mode 100644 index 0000000..bc69c96 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/4.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/5.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/5.png new file mode 100644 index 0000000..0080a54 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/5.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/6.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/6.png new file mode 100644 index 0000000..b07c3b4 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/6.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/7.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/7.png new file mode 100644 index 0000000..3be019a Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/7.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/8.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/8.png new file mode 100644 index 0000000..96d3c76 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/8.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/9.png b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/9.png new file mode 100644 index 0000000..524c940 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/jigsaw/slidingBlock/9.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg1.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg1.png new file mode 100644 index 0000000..51573a0 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg1.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg2.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg2.png new file mode 100644 index 0000000..909dc39 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg2.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg3.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg3.png new file mode 100644 index 0000000..59bc59c Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg3.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg4.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg4.png new file mode 100644 index 0000000..c856f4d Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg4.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg5.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg5.png new file mode 100644 index 0000000..4594fcf Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg5.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg6.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg6.png new file mode 100644 index 0000000..0f28d82 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg6.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg7.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg7.png new file mode 100644 index 0000000..1e04492 Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg7.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg8.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg8.png new file mode 100644 index 0000000..f11545f Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg8.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg9.png b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg9.png new file mode 100644 index 0000000..2f3a86d Binary files /dev/null and b/application-tenant/tenant-admin/src/main/resources/images/pic-click/bg9.png differ diff --git a/application-tenant/tenant-admin/src/main/resources/logback-spring.xml b/application-tenant/tenant-admin/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..68533c6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/logback-spring.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + ${LOG_PATTERN} + + + + + + + + ${LOG_HOME}/${LOG_NAME}.log + true + + + ${LOG_HOME}/${LOG_NAME}-%d{yyyy-MM-dd}-%i.log + + + 31 + + + 20MB + + + + + + ${LOG_PATTERN_EX} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/BillController.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/BillController.java.ftl new file mode 100644 index 0000000..84fc01a --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/BillController.java.ftl @@ -0,0 +1,154 @@ +package ${package.Controller}; + +import apelet.common.botp.dto.ConvertRelationDto; +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.MyOrderParam; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyPageUtil; +import apelet.common.core.object.ConvertRelationDto; +import apelet.tenantadmin.ape.model.ApeBase; +import apelet.tenantadmin.ape.model.service.IApeBaseService; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +<#if restControllerStyle> +import org.springframework.web.bind.annotation.RestController; +<#else> +import org.springframework.stereotype.Controller; + + +<#if superControllerClass??> +import apelet.common.botp.controller.BillBaseController; + + +/** +*

+ * ${table.comment!} 前端控制器 + *

+* +* @author ${author} +* @since ${date} +*/ +<#if restControllerStyle> +@RestController +<#else> + @Controller + + +@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}") +<#if kotlin> +class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() +<#else> +<#if superControllerClass??> +public class ${table.controllerName} extends BillBaseController<${entity},${entityVo},Long> { + + @Autowired + private ${table.serviceName} ${table.serviceName}; + + @Override + protected IBillBaseService<${entity}, Long> billservice() { + return (IBillBaseService<${entity}, Long>) iApeBaseService; + } + + + /** + * 列表接口 + * @param m + * @param orderParam + * @param pageParam + * @param traceParamDto + * @return + */ + @Override + @PostMapping(value = "/listBill") + @ApiOperation(value = "listBill", notes = "单据列表") + public ResponseResult listBill(@MyRequestBody ${entity} ${entity}, + @MyRequestBody MyOrderParam orderParam, + @MyRequestBody MyPageParam pageParam, + @MyRequestBody TraceParamDto traceParamDto) { + return super.listBill(${entity},, orderParam, pageParam, traceParamDto); + } + + /** + * 单据保存接口,包括保存单据分录 + * @param m + * @param crDto + * @return + */ + @Override + @PostMapping(value = "/saveBill") + @ApiOperation(value = "saveBill", notes = "保存单据") + public ResponseResult saveBill(@MyRequestBody ${entity} ${entity}, @MyRequestBody + List crDto) { + return super.saveBill(${entity}, crDto); + } + + /** + * 单据详情 + * @param id + * @return + */ + @Override + @GetMapping(value = "/detailBill") + @ApiOperation(value = "detailBill", notes = "查询单据详情") + public ResponseResult detailBill(Long id) { + return super.detailBill(id); + } + + /** + * 单据提交接口 ,包括保存单据分录 + * @param processDefinitionKey 流程标识 + * @param m 单据实体 + * @param crDto 关联关系 + * @return + */ + @Override + @PostMapping(value = "/submitBill") + @ApiOperation(value = "submitBill", notes = "提交单据") + public ResponseResult submitBill(String processDefinitionKey, @MyRequestBody ${entity} ${entity}, + @MyRequestBody List crDto) { + return super.submitBill(processDefinitionKey, ${entity}, crDto); + } + + /** + * 单据提交接口 + * @param processDefinitionKey 流程标识 + * @param id 单据id + * @return + */ + @Override + @GetMapping (value = "/submitBillKey") + @ApiOperation(value = "submitBillKey", notes = "提交单据") + public ResponseResult submitBillKey(String processDefinitionKey, Long id) { + return super.submitBillKey(processDefinitionKey, id); + } + + /** + * 删除单据,包括分录数据关联删除 + * @param ids id逗号隔开 + * @return + */ + @Override + @GetMapping(value = "/removeBillById") + @ApiOperation(value = "removeBillById", notes = "删除单据") + public ResponseResult removeBillById(String ids) { + return super.removeBillById(ids); + } + +<#else> +public class ${table.controllerName} { + + @Autowired + private ${table.serviceName} ${table.serviceName}; + + + } + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/controller.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/controller.java.ftl new file mode 100644 index 0000000..9d3cf42 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/controller.java.ftl @@ -0,0 +1,54 @@ +package ${package.Controller}; + + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.MyOrderParam; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyPageUtil; +import apelet.tenantadmin.ape.model.ApeBase; +import apelet.tenantadmin.ape.model.service.IApeBaseService; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +<#if restControllerStyle> +import org.springframework.web.bind.annotation.RestController; +<#else> +import org.springframework.stereotype.Controller; + +<#if superControllerClassPackage??> +import ${superControllerClassPackage}; + + +/** + *

+ * ${table.comment!} 前端控制器 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if restControllerStyle> +@RestController +<#else> +@Controller + +@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}") +<#if kotlin> +class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() +<#else> +<#if superControllerClass??> +public class ${table.controllerName} extends ${superControllerClass} { +<#else> +public class ${table.controllerName} { + + +} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/controllertest.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/controllertest.java.ftl new file mode 100644 index 0000000..8b739bd --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/controllertest.java.ftl @@ -0,0 +1,143 @@ +package ${package.Controller}; + + +import apelet.common.core.annotation.MyRequestBody; +import apelet.common.core.constant.ErrorCodeEnum; +import apelet.common.core.object.MyOrderParam; +import apelet.common.core.object.MyPageData; +import apelet.common.core.object.MyPageParam; +import apelet.common.core.object.ResponseResult; +import apelet.common.core.util.MyPageUtil; +import apelet.tenantadmin.ape.model.ApeBase; +import apelet.tenantadmin.ape.model.service.IApeBaseService; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +<#if restControllerStyle> +import org.springframework.web.bind.annotation.RestController; +<#else> +import org.springframework.stereotype.Controller; + +<#if superControllerClassPackage??> +import ${superControllerClassPackage}; + + +/** + *

+ * ${table.comment!} 前端控制器 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if restControllerStyle> +@RestController +<#else> +@Controller + +@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}") +<#if kotlin> +class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() +<#else> +<#if superControllerClass??> +public class ${table.controllerName} extends ${superControllerClass} { +<#else> +public class ${table.controllerName} { + + @Autowired + private ${table.serviceName} ${table.serviceName}; + + + /** + * ${table.controllerName}列表(分页) + * + * @param query 查询参数对象 + * @return + */ + @GetMapping("/page") + public Result page(${table.controllerName}Query query) { + log.info("${table.controllerName}Controller.page 参数为:" + JsonUtil.toJsonFromObject(query)); + return Result.success(this.${table.controllerName}Service.page(query)); + } + + + /** + * ${table.controllerName}列表(不分页) + * + * @param query 查询参数对象 + * @return + */ + @GetMapping("/list") + public Result list(${table.controllerName}Query query) { + log.info("${table.controllerName}Controller.list 参数为:" + JsonUtil.toJsonFromObject(query)); + return Result.success(this.${table.controllerName}Service.list(query)); + } + +/** +* 插入${table.comment} +* +* @param dto 待插入的${table.comment} +* @return +*/ + @PostMapping("/insert") + public Result insert(@RequestBody @Validated(Insert.class) ${table.controllerName}Dto dto) { + log.info("${table.controllerName}Controller.insert 参数为:" + JsonUtil.toJsonFromObject(dto)); + return Result.success(this.${table.controllerName}Service.save(dto)); + } + + +/** +* 根据 id 获取${table.controllerName} +* +* @param id +* @return +*/ +@GetMapping("/selectById") +public Result selectById(@RequestParam("id") Integer id) { + Assert.notNull(id, "id 不能为空"); + + log.info("${table.controllerName}Controller.selectById 参数为:" + id); + return Result.success(this.${table.controllerName}Service.getById(id)); +} + + +/** +* 根据 id 更新${table.controllerName} +* +* @param dto +* @return +*/ +@PostMapping("/updateById") +public Result updateById(@RequestBody @Validated(Update.class) ${table.controllerName}Dto dto) { + Assert.notNull(dto.getId(), "id 不能为空"); + + log.info("${table.controllerName}Controller.updateById 参数为:" + JsonUtil.toJsonFromObject(dto)); + return Result.success(this.${table.controllerName}Service.updateById(dto)); +} + + +/** +* 根据 id 删除${table.controllerName} +* +* @param id +* @return +*/ +@PostMapping("/deleteById") +public Result deleteById(@RequestParam("id") Integer id) { + Assert.notNull(id, "id 不能为空"); + + log.info("${table.controllerName}Controller.deleteById 参数为:" + id); + return Result.success(this.${table.controllerName}Service.removeById(id)); +} + + + + + +} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/dto.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/dto.java.ftl new file mode 100644 index 0000000..b536baf --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/dto.java.ftl @@ -0,0 +1,17 @@ +package ${package.Entity}; + + +/** + *

+ * 测试自定义DTO模板 ${table.comment!} + *

+ * + * 测试注入 ${cfg.abc} + */ + + +// 自定义的查询内容 +<#list table.fields as field> + 自定义 abc 内容: ${field.customMap.abc} + 自定义 def 内容: ${field.customMap.def} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/dtotest.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/dtotest.java.ftl new file mode 100644 index 0000000..b029fae --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/dtotest.java.ftl @@ -0,0 +1,25 @@ +package ${package.Entity}; + +import ${package}.${entity}#if(${moduleName} != "").${moduleName}#end.${className}; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + *

+ * 测试自定义DTO模板 ${table.comment!} + *

+ * + * 测试注入 ${cfg.abc} + */ + + +/** +* @author ${author} +* @date ${date} +* @desc ${table.comment} dto +*/ +@Data +@Accessors(chain = true) +public class ${table.dtoName}Dto extends ${table.dtoName} implements Dto { + +} diff --git a/application-tenant/tenant-admin/src/main/resources/templates/entity.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/entity.java.ftl new file mode 100644 index 0000000..965211f --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/entity.java.ftl @@ -0,0 +1,158 @@ +package ${package.Entity}; + +<#list table.importPackages as pkg> +import ${pkg}; + +<#if springdoc> +import io.swagger.v3.oas.annotations.media.Schema; +<#elseif swagger> +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +<#if entityLombokModel> +import lombok.Data; + <#if chainModel> +import lombok.experimental.Accessors; + + + +/** + *

+ * ${table.comment!} + *

+ * + * @author ${author} + * @since ${date} + */ +<#if entityLombokModel> +@Data + <#if chainModel> +@Accessors(chain = true) + + +<#if table.convert> +@TableName("${schemaName}${table.name}") + +<#if springdoc> +@Schema(name = "${entity}", description = "${table.comment!}") +<#elseif swagger> +@ApiModel(value = "${entity}对象", description = "${table.comment!}") + +<#if superEntityClass??> +public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}> { +<#elseif activeRecord> +public class ${entity} extends Model<${entity}> { +<#elseif entitySerialVersionUID> +public class ${entity} implements Serializable { +<#else> +public class ${entity} { + +<#if entitySerialVersionUID> + + private static final long serialVersionUID = 1L; + +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.fields as field> + <#if field.keyFlag> + <#assign keyPropertyName="${field.propertyName}"/> + + + <#if field.comment!?length gt 0> + <#if springdoc> + @Schema(description = "${field.comment}") + <#elseif swagger> + @ApiModelProperty("${field.comment}") + <#else> + /** + * ${field.comment} + */ + + + <#if field.keyFlag> + <#-- 主键 --> + <#if field.keyIdentityFlag> + @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO) + <#elseif idType??> + @TableId(value = "${field.annotationColumnName}", type = IdType.${idType}) + <#elseif field.convert> + @TableId("${field.annotationColumnName}") + + <#-- 普通字段 --> + <#elseif field.fill??> + <#-- ----- 存在字段填充设置 -----> + <#if field.convert> + @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill}) + <#else> + @TableField(fill = FieldFill.${field.fill}) + + <#elseif field.convert> + @TableField("${field.annotationColumnName}") + + <#-- 乐观锁注解 --> + <#if field.versionField> + @Version + + <#-- 逻辑删除注解 --> + <#if field.logicDeleteField> + @TableLogic + + private ${field.propertyType} ${field.propertyName}; + +<#------------ END 字段循环遍历 ----------> +<#if !entityLombokModel> + <#list table.fields as field> + <#if field.propertyType == "boolean"> + <#assign getprefix="is"/> + <#else> + <#assign getprefix="get"/> + + + public ${field.propertyType} ${getprefix}${field.capitalName}() { + return ${field.propertyName}; + } + + <#if chainModel> + public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + <#else> + public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + + this.${field.propertyName} = ${field.propertyName}; + <#if chainModel> + return this; + + } + + +<#if entityColumnConstant> + <#list table.fields as field> + + public static final String ${field.name?upper_case} = "${field.name}"; + + +<#if activeRecord> + + @Override + public Serializable pkVal() { + <#if keyPropertyName??> + return this.${keyPropertyName}; + <#else> + return null; + + } + +<#if !entityLombokModel> + + @Override + public String toString() { + return "${entity}{" + + <#list table.fields as field> + <#if field_index==0> + "${field.propertyName} = " + ${field.propertyName} + + <#else> + ", ${field.propertyName} = " + ${field.propertyName} + + + + "}"; + } + +} diff --git a/application-tenant/tenant-admin/src/main/resources/templates/entity.kt.ftl b/application-tenant/tenant-admin/src/main/resources/templates/entity.kt.ftl new file mode 100644 index 0000000..47261c0 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/entity.kt.ftl @@ -0,0 +1,122 @@ +package ${package.Entity} + +<#list table.importPackages as pkg> +import ${pkg} + +<#if springdoc> +import io.swagger.v3.oas.annotations.media.Schema; +<#elseif swagger> +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + + +/** + *

+ * ${table.comment} + *

+ * + * @author ${author} + * @since ${date} + */ +<#if table.convert> +@TableName("${schemaName}${table.name}") + +<#if springdoc> +@Schema(name = "${entity}", description = "$!{table.comment}") +<#elseif swagger> +@ApiModel(value = "${entity}对象", description = "${table.comment!}") + +<#if superEntityClass??> +class ${entity} : ${superEntityClass}<#if activeRecord><${entity}>() { +<#elseif activeRecord> +class ${entity} : Model<${entity}>() { +<#elseif entitySerialVersionUID> +class ${entity} : Serializable { +<#else> +class ${entity} { + + +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.fields as field> +<#if field.keyFlag> + <#assign keyPropertyName="${field.propertyName}"/> + +<#if field.comment!?length gt 0> + <#if springdoc> + @Schema(description = "${field.comment}") + <#elseif swagger> + @ApiModelProperty("${field.comment}") + <#else> + /** + * ${field.comment} + */ + + +<#if field.keyFlag> +<#-- 主键 --> +<#if field.keyIdentityFlag> + @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO) +<#elseif idType ??> + @TableId(value = "${field.annotationColumnName}", type = IdType.${idType}) +<#elseif field.convert> + @TableId("${field.annotationColumnName}") + +<#-- 普通字段 --> +<#elseif field.fill??> +<#-- ----- 存在字段填充设置 -----> +<#if field.convert> + @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill}) +<#else> + @TableField(fill = FieldFill.${field.fill}) + +<#elseif field.convert> + @TableField("${field.annotationColumnName}") + +<#-- 乐观锁注解 --> +<#if field.versionField> + @Version + +<#-- 逻辑删除注解 --> +<#if field.logicDeleteField> + @TableLogic + + <#if field.propertyType == "Integer"> + var ${field.propertyName}: Int? = null + <#else> + var ${field.propertyName}: ${field.propertyType}? = null + + + +<#-- ---------- END 字段循环遍历 ----------> +<#if entityColumnConstant> + companion object { +<#list table.fields as field> + + const val ${field.name?upper_case} : String = "${field.name}" + + + } + + +<#if activeRecord> + override fun pkVal(): Serializable? { +<#if keyPropertyName??> + return ${keyPropertyName} +<#else> + return null + + } + + + override fun toString(): String { + return "${entity}{" + +<#list table.fields as field> +<#if field_index==0> + "${field.propertyName}=" + ${field.propertyName} + +<#else> + ", ${field.propertyName}=" + ${field.propertyName} + + + + "}" + } +} diff --git a/application-tenant/tenant-admin/src/main/resources/templates/entity1.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/entity1.java.ftl new file mode 100644 index 0000000..c957615 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/entity1.java.ftl @@ -0,0 +1,162 @@ +package ${package.Entity}; + +<#list table.importPackages as pkg> +import ${pkg}; + +<#if swagger> +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +<#if entityLombokModel> +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import apelet.common.core.annotation.RelationOneToMany; + +/** + *

+ 自定义模板 + * ${table.comment!} +<#-- 自定义属性注入abc=${cfg.abc}--> + *

+ * + * @author ${author} + * @since ${date} + */ +<#if entityLombokModel> +@Data + <#if superEntityClass??> +@EqualsAndHashCode(callSuper = true) + <#else> +@EqualsAndHashCode(callSuper = false) + +@Accessors(chain = true) + +<#if table.convert> +@TableName("${table.name}") + +<#if swagger> +@ApiModel(value="${entity}对象", description="${table.comment!}") + +<#if superEntityClass??> +public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}> { +<#elseif activeRecord> +public class ${entity} extends Model<${entity}> { +<#else> +public class ${entity} implements Serializable { + + + private static final long serialVersionUID = 1L; +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.fields as field> + <#if field.keyFlag> + <#assign keyPropertyName="${field.propertyName}"/> + + + <#if field.comment!?length gt 0> + <#if swagger> + @ApiModelProperty(value = "${field.comment}") + <#else> + /** + * ${field.comment} + */ + + + <#if field.keyFlag> + <#-- 主键 --> + <#if field.keyIdentityFlag> + @TableId(value = "${field.name}", type = IdType.AUTO) + <#elseif idType??> + @TableId(value = "${field.name}", type = IdType.${idType}) + <#elseif field.convert> + @TableId("${field.name}") + + <#-- 普通字段 --> + <#elseif field.fill??> + <#-- ----- 存在字段填充设置 -----> + <#if field.convert> + @TableField(value = "${field.name}", fill = FieldFill.${field.fill}) + <#else> + @TableField(fill = FieldFill.${field.fill}) + + <#elseif field.convert> + @TableField("${field.name}") + +<#-- 乐观锁注解 --> + <#if (versionFieldName!"") == field.name> + @Version + +<#-- 逻辑删除注解 --> + <#if (logicDeleteFieldName!"") == field.name> + @TableLogic + + private ${field.propertyType} ${field.propertyName}; + +<#------------ END 字段循环遍历 ----------> + +<#-- 分录对象--> +<#if childList??> + <#list childList as item> + + @RelationOneToMany(masterIdField = "id", slaveModelClass = ${item}.class, slaveIdField = "parentId") + private List<${item}> list; + + + +<#if !entityLombokModel> + <#list table.fields as field> + <#if field.propertyType == "boolean"> + <#assign getprefix="is"/> + <#else> + <#assign getprefix="get"/> + + public ${field.propertyType} ${getprefix}${field.capitalName}() { + return ${field.propertyName}; + } + + <#if entityBuilderModel> + public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + <#else> + public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + + this.${field.propertyName} = ${field.propertyName}; + <#if entityBuilderModel> + return this; + + } + + + +<#if entityColumnConstant> + <#list table.fields as field> + public static final String ${field.name?upper_case} = "${field.name}"; + + + +<#if activeRecord> + @Override + protected Serializable pkVal() { + <#if keyPropertyName??> + return this.${keyPropertyName}; + <#else> + return null; + + } + + +<#if !entityLombokModel> + @Override + public String toString() { + return "${entity}{" + + <#list table.fields as field> + <#if field_index==0> + "${field.propertyName}=" + ${field.propertyName} + + <#else> + ", ${field.propertyName}=" + ${field.propertyName} + + + + "}"; + } + +} diff --git a/application-tenant/tenant-admin/src/main/resources/templates/entityVo.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/entityVo.java.ftl new file mode 100644 index 0000000..5986daa --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/entityVo.java.ftl @@ -0,0 +1,42 @@ +package ${package.Entity}; + +<#list table.importPackages as pkg> +import ${pkg}; + + +<#if swagger> +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + + +/** +*

+ 自定义模板 + * ${table.comment!} + <#-- 自定义属性注入abc=${cfg.abc}--> + *

+* +* @author ${author} +* @since ${date} +*/ +@Data +<#if swagger> +@ApiModel(value = "${entity}对象", description = "${table.comment!}") + +public class ${entity}Vo implements Serializable { +private static final long serialVersionUID = 1L; +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.fields as field> + + @ApiModelProperty("${field.comment}") + private ${field.propertyType} ${field.propertyName}; + +<#------------ END 字段循环遍历 ----------> + +<#-- 分录对象--> +<#if childList??> +<#list childList as child> + private List<${child}> ${child}List; + + +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/resources/templates/from.vue.ftl b/application-tenant/tenant-admin/src/main/resources/templates/from.vue.ftl new file mode 100644 index 0000000..e69de29 diff --git a/application-tenant/tenant-admin/src/main/resources/templates/list.vue.ftl b/application-tenant/tenant-admin/src/main/resources/templates/list.vue.ftl new file mode 100644 index 0000000..52917e4 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/list.vue.ftl @@ -0,0 +1,535 @@ + + + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/mapper.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/mapper.java.ftl new file mode 100644 index 0000000..320a683 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/mapper.java.ftl @@ -0,0 +1,26 @@ +package ${package.Mapper}; + +import ${package.Entity}.${entity}; +import ${superMapperClassPackage}; +<#if mapperAnnotationClass??> +import ${mapperAnnotationClass.name}; + + +/** + *

+ * ${table.comment!} Mapper 接口 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if mapperAnnotationClass??> +@${mapperAnnotationClass.simpleName} + +<#if kotlin> +interface ${table.mapperName} : ${superMapperClass}<${entity}> +<#else> +public interface ${table.mapperName} extends BaseDaoMapper<${entity}> { + +} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/mapper.xml.ftl b/application-tenant/tenant-admin/src/main/resources/templates/mapper.xml.ftl new file mode 100644 index 0000000..b60acec --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/mapper.xml.ftl @@ -0,0 +1,6 @@ + + + + + + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/service.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/service.java.ftl new file mode 100644 index 0000000..e3232f3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/service.java.ftl @@ -0,0 +1,20 @@ +package ${package.Service}; + +import ${package.Entity}.${entity}; +import ${superServiceClassPackage}; + +/** + *

+ * ${table.comment!} 服务类 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if kotlin> +interface ${table.serviceName} : ${superServiceClass}<${entity}> +<#else> +public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { + +} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/serviceImpl.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/serviceImpl.java.ftl new file mode 100644 index 0000000..8368702 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/serviceImpl.java.ftl @@ -0,0 +1,181 @@ +package ${package.ServiceImpl}; + +import apelet.common.core.annotation.MyDataSource; +import lombok.extern.slf4j.Slf4j; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.object.TokenData; +import cn.hutool.core.util.* +import ${package.Entity}.${entity}; +import ${package.Mapper}.${table.mapperName}; +<#if table.serviceInterface> +import ${package.Service}.${table.serviceName}; + +import ${superServiceImplClassPackage}; +import org.springframework.stereotype.Service; + +/** + *

+ * ${table.comment!} 服务实现类 + *

+ * + * @author ${author} + * @since ${date} + */ +@Service +@Slf4j +<#--<#if kotlin>--> +<#--open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>()<#if table.serviceInterface>, ${table.serviceName}--> +<#-- {}--> +<#--<#else>--> +<#--public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}><#if table.serviceInterface> implements ${table.serviceName}--> +<#-- {}--> +<#----> +public class ${table.serviceImplName} extends BaseService<${entity}, Long> implements ${table.serviceName} { + @Autowired + private ${table.mapperName} ${table.mapperName}; + + @Override + protected BaseDaoMapper<${entity}> mapper() { return baseMapper; } + + + String path="C:\\导出路径后期模版修改"; + + /** + * 查询列表 + */ + @Override + public List<${entity}> get${entity}ListWithRelation(${entity} filter, String buildOrderBy) { + LambdaQueryWrapper<${entity}> queryWrapper = new LambdaQueryWrapper<>(filter); + if (StrUtil.isNotBlank(buildOrderBy)) { + queryWrapper.last(" ORDER BY " + buildOrderBy); + } + List<${entity}> resultList = baseMapper.selectList(queryWrapper); + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + /** + * 新增 + */ + @Override + public Boolean addAll(${entity} base) { + //设置主数据基本信息 + addOrUpdateBaseData(base,null); + boolean save =this.save(base); + //如果有分录 +<#-- Long id = base.getId();--> +<#-- if (null==id)return false;--> +<#-- //设置分录数据基本信息,添加--> +<#-- return addChild(base,id);--> + return save; + } + /** + * 修改 + */ + @Override + public Boolean editAll(${entity} base) { + Long id = base.getId(); + if (null==id)return false; + //设置主数据基本信息 + boolean update =addOrUpdateBaseData(apeBase,null); + this.updateById(apeBase); +<#-- //删除分录数据--> +<#-- removeChild(id);--> +<#-- //设置分录数据基本信息,重新添加--> +<#-- return addChild(apeBase,id);--> + return update; + } + + /** + * 删除 + */ + @Override + public Boolean removeAllById(Long id) { + boolean remove = this.removeById(id); + //有分录情况删除 +<#-- if (remove){--> +<#-- removeChild(id);--> +<#-- }--> + return remove; + } + @Override + public void exportExcel(JSONObject data, HttpServletResponse response){ + JSONArray ids = data.getJSONArray("ids"); + List<${entity}> dataList=new ArrayList<>(); + if (ObjectUtil.isNotEmpty(ids)){ + dataList.addAll(this.listByIds(ids.toJavaList(Long.class))); + }else { + dataList.addAll(this.list()); + } +<#-- //如果有分录--> +<#-- for (${entity} base : dataList) {--> +<#-- base.setChild1(child1Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild1::getMaterId,apeBase.getId())));--> +<#-- base.setChild2(child2Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild2::getMaterId,apeBase.getId())));--> +<#-- base.setChild3(child3Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild3::getMaterId,apeBase.getId())));--> +<#-- }--> + ApeEasyPoiUtil.exportExcel(response,dataList,${entity}.class,path,"excel导出的名称","excel导出的sheet名称"); + } + /** + * 导入 + */ + @Override + public void importExcel(HttpServletRequest request){ + List<${entity}> list = ApeEasyPoiUtil.importExcel(request, ApeBase.class); + for (ApeBase apeBase : list) { + addAll(apeBase); + } + } + + + + + + +<#-- /**--> +<#-- * 如果有分录情况下且需要参数映射就需要这样命名方法--> +<#-- */--> +<#-- @Override--> +<#-- public List getChild1WithRelation(Long id) {--> +<#-- return child1Service.getChild1WithRelation(id);--> +<#-- }--> + + +<#-- /**--> +<#-- * 如果有分录且需要条件--> +<#-- */--> +<#-- private Boolean addChild(ApeBase apeBase,Long id){--> +<#-- List child = apeBase.getChild1();--> +<#-- if (ObjectUtil.isNotEmpty(child)){--> +<#-- for (child one : child) {--> +<#-- addOrUpdateBaseData(one,id);--> +<#-- childMapper.insert(one);--> +<#-- }--> +<#-- }--> +<#-- return true;--> +<#-- }--> + + +<#-- /**--> +<#-- * 有分录的情况下删除分录的方法--> +<#-- */--> +<#-- private void removeChild(Long id){--> +<#-- child1Service1.remove(new LambdaQueryWrapper().eq(child::getMaterId,id));--> +<#-- }--> + /** + * 设置基本参数方法 + */ + private void addOrUpdateBaseData(T t,Long id){ + if (t instanceof ${entity}){ + ${entity} data = (${entity}) t; + data.setDeletedFlag(GlobalDeletedFlag.NORMAL); + data.setUpdateUserId(TokenData.takeFromRequest().getUserId()); + data.setUpdateTime(new Date()); + if (ObjectUtil.isEmpty(data.getCreateTime())){ + data.setCreateUserId(TokenData.takeFromRequest().getUserId()); + data.setCreateTime(new Date()); + } + } + } +} + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/serviceImpltest.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/serviceImpltest.java.ftl new file mode 100644 index 0000000..ad8bb42 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/serviceImpltest.java.ftl @@ -0,0 +1,182 @@ +package ${package.ServiceImpl}; + +import apelet.common.core.annotation.MyDataSource; +import lombok.extern.slf4j.Slf4j; +import apelet.common.core.base.service.BaseService; +import apelet.common.core.object.TokenData; +import cn.hutool.core.util.* +import ${package.Entity}.${entity}; +import ${package.Mapper}.${table.mapperName}; +<#if table.serviceInterface> +import ${package.Service}.${table.serviceName}; + +import ${superServiceImplClassPackage}; +import org.springframework.stereotype.Service; + +/** + *

+ * ${table.comment!} 服务实现类 + *

+ * + * @author ${author} + * @since ${date} + */ +@Service +@Slf4j +<#--<#if kotlin>--> +<#--open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>()<#if table.serviceInterface>, ${table.serviceName}--> +<#-- {}--> +<#--<#else>--> +<#--public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}><#if table.serviceInterface> implements ${table.serviceName}--> +<#-- {}--> +<#----> +<#if superControllerClass??> +public class ${table.serviceImplName} extends BillBaseService<${entity}, Long> implements ${table.serviceName}{ +<#else> + public class ${table.serviceImplName} extends BaseService<${entity}, Long> implements ${table.serviceName} { + + @Autowired + private ${table.mapperName} ${table.mapperName}; + + @Override + protected BaseDaoMapper<${entity}> mapper() { return baseMapper; } + + + /** + * 查询列表 + */ + @Override + public List<${entity}> get${entity}ListWithRelation(${entity} filter, String buildOrderBy) { + LambdaQueryWrapper<${entity}> queryWrapper = new LambdaQueryWrapper<>(filter); + if (StrUtil.isNotBlank(buildOrderBy)) { + queryWrapper.last(" ORDER BY " + buildOrderBy); + } + List<${entity}> resultList = baseMapper.selectList(queryWrapper); + int batchSize = resultList instanceof Page ? 0 : 1000; + this.buildRelationForDataList(resultList, MyRelationParam.normal(), batchSize); + return resultList; + } + + /** + * 新增 + */ + @Override + public Boolean addAll(${entity} base) { + //设置主数据基本信息 + addOrUpdateBaseData(base,null); + boolean save =this.save(base); + //如果有分录 + <#-- Long id = base.getId();--> + <#-- if (null==id)return false;--> + <#-- //设置分录数据基本信息,添加--> + <#-- return addChild(base,id);--> + return save; + } + /** + * 修改 + */ + @Override + public Boolean editAll(${entity} base) { + Long id = base.getId(); + if (null==id)return false; + //设置主数据基本信息 + boolean update =addOrUpdateBaseData(apeBase,null); + this.updateById(apeBase); + <#-- //删除分录数据--> + <#-- removeChild(id);--> + <#-- //设置分录数据基本信息,重新添加--> + <#-- return addChild(apeBase,id);--> + return update; + } + + /** + * 删除 + */ + @Override + public Boolean removeAllById(Long id) { + boolean remove = this.removeById(id); + //有分录情况删除 + <#-- if (remove){--> + <#-- removeChild(id);--> + <#-- }--> + return remove; + } + @Override + public void exportExcel(JSONObject data, HttpServletResponse response){ + JSONArray ids = data.getJSONArray("ids"); + List<${entity}> dataList=new ArrayList<>(); + if (ObjectUtil.isNotEmpty(ids)){ + dataList.addAll(this.listByIds(ids.toJavaList(Long.class))); + }else { + dataList.addAll(this.list()); + } + <#-- //如果有分录--> + <#-- for (${entity} base : dataList) {--> + <#-- base.setChild1(child1Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild1::getMaterId,apeBase.getId())));--> + <#-- base.setChild2(child2Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild2::getMaterId,apeBase.getId())));--> + <#-- base.setChild3(child3Mapper.selectList(new LambdaQueryWrapper<${entity}>().eq(ApeBaseChild3::getMaterId,apeBase.getId())));--> + <#-- }--> + ApeEasyPoiUtil.exportExcel(response,dataList,${entity}.class,path,"excel导出的名称","excel导出的sheet名称"); + } + /** + * 导入 + */ + @Override + public void importExcel(HttpServletRequest request){ + List<${entity}> list = ApeEasyPoiUtil.importExcel(request, ApeBase.class); + for (ApeBase apeBase : list) { + addAll(apeBase); + } + } + + + + + + +<#-- /**--> +<#-- * 如果有分录情况下且需要参数映射就需要这样命名方法--> +<#-- */--> +<#-- @Override--> +<#-- public List getChild1WithRelation(Long id) {--> +<#-- return child1Service.getChild1WithRelation(id);--> +<#-- }--> + + +<#-- /**--> +<#-- * 如果有分录且需要条件--> +<#-- */--> +<#-- private Boolean addChild(ApeBase apeBase,Long id){--> +<#-- List child = apeBase.getChild1();--> +<#-- if (ObjectUtil.isNotEmpty(child)){--> +<#-- for (child one : child) {--> +<#-- addOrUpdateBaseData(one,id);--> +<#-- childMapper.insert(one);--> +<#-- }--> +<#-- }--> +<#-- return true;--> +<#-- }--> + + +<#-- /**--> +<#-- * 有分录的情况下删除分录的方法--> +<#-- */--> +<#-- private void removeChild(Long id){--> +<#-- child1Service1.remove(new LambdaQueryWrapper().eq(child::getMaterId,id));--> +<#-- }--> + /** + * 设置基本参数方法 + */ + private void addOrUpdateBaseData(T t,Long id){ + if (t instanceof ${entity}){ + ${entity} data = (${entity}) t; + data.setDeletedFlag(GlobalDeletedFlag.NORMAL); + data.setUpdateUserId(TokenData.takeFromRequest().getUserId()); + data.setUpdateTime(new Date()); + if (ObjectUtil.isEmpty(data.getCreateTime())){ + data.setCreateUserId(TokenData.takeFromRequest().getUserId()); + data.setCreateTime(new Date()); + } + } + } +} \ No newline at end of file diff --git a/application-tenant/tenant-admin/src/main/resources/templates/servicetest.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/servicetest.java.ftl new file mode 100644 index 0000000..d7e0ac3 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/servicetest.java.ftl @@ -0,0 +1,27 @@ +package ${package.Service}; + +import ${package.Entity}.${entity}; +import ${superServiceClassPackage}; + +/** +*

+ * ${table.comment!} 服务类 + *

+* +* @author ${author} +* @since ${date} +*/ +<#if kotlin> + interface ${table.serviceName} : ${superServiceClass}<${entity}> +<#else> + public interface I${table.serviceName} extends IBaseService<${entity},Long> { + + Boolean removeAllById(Long id); + + Boolean editAll(${entity} ${entity}); + + Boolean addAll(${entity} ${entity}); + + List<${entity}> get${entity}ListWithRelation(${entity} ${entity}Filter, String buildOrderBy); + } + diff --git a/application-tenant/tenant-admin/src/main/resources/templates/vo.java.ftl b/application-tenant/tenant-admin/src/main/resources/templates/vo.java.ftl new file mode 100644 index 0000000..416cdd6 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/templates/vo.java.ftl @@ -0,0 +1,16 @@ +package ${package.Entity}; + + +/** + *

+ * 测试自定义VO模板 ${table.comment!} + *

+ * + * 测试注入 ${cfg.abc} + */ + + +// 自定义的查询内容 +<#list table.fields as field> + 字段: ${field.propertyName} + diff --git a/application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml b/application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml new file mode 100644 index 0000000..f1d3f95 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml @@ -0,0 +1,307 @@ +logging: + level: + # 这里设置的日志级别优先于logback-spring.xml文件Loggers中的日志级别。 + apelet: info + config: classpath:logback-spring.xml + +server: + port: 8075 + +application: + # Jwt令牌加密的签名值。该值的长度要超过10个字符(过短会报错)。 + tokenSigningKey: tenantadmin-signing-key-signing-key + # Jwt令牌在Http Header中的键名称。 + tokenHeaderKey: Authorization + # Jwt令牌刷新后在Http Header中的键名称。 + refreshedTokenHeaderKey: RefreshedToken + # Jwt令牌过期时间(毫秒)。 + expiration: 72000000 + # 初始化密码。 + defaultUserPassword: 123456 + # 缺省的文件上传根目录。 + uploadFileBaseDir: ./zz-resource/upload-files/tenant-admin + # 跨域的IP(http://192.168.10.10:8086)白名单列表,多个IP之间逗号分隔(* 表示全部信任,空白表示禁用跨域信任)。 + credentialIpList: "*" + # 同步租户公共管理数据到消息队列的主题,再由同步消费者服务将数据同步到目标数据库中。 + tenantSyncTopic: TenantSync + # Session会话和用户和权限在Redis中的过期时间(秒)。 + sessionExpiredSeconds: 86400 + # Session的数据权限信息在Redis中的过期时间(秒)。 + dataPermExpiredSeconds: 86460 + # 是否排他登录。 + excludeLogin: false +common-eas: + urlPrefix: /tenantadmin/eas +common-ext: + urlPrefix: /tenantadmin/commonext +common-mobile: + # 注意不要以反斜杠(/)结尾。 + urlPrefix: /tenantadmin/mobile + # 在线表单业务数据上传资源路径 + uploadFileBaseDir: ./zz-resource/upload-files/mobile +common-online: + # 注意不要以反斜杠(/)结尾。 + urlPrefix: /tenantadmin/online + # 打印接口的路径,不要以反斜杠(/)结尾。 + printUrlPath: /tenantadmin/report/reportPrint/print + # 在线表单业务数据上传资源路径 + uploadFileBaseDir: ./zz-resource/upload-files/online + # 如果为false,在线表单模块中所有Controller接口将不能使用。 + operationEnabled: true + # 1: minio 2: aliyun-oss 3: qcloud-cos。 + distributeStoreType: 2 + # 调用render接口时候,是否打开一级缓存加速。 + enableRenderCache: false + # 脱敏字段的掩码字符,只能为单个字符。 + maskChar: '*' + # 下面的url列表,请保持反斜杠(/)结尾。 + viewUrlList: + - ${common-online.urlPrefix}/onlineOperation/viewByDatasourceId/ + - ${common-online.urlPrefix}/onlineOperation/viewByOneToManyRelationId/ + - ${common-online.urlPrefix}/onlineOperation/listByDatasourceId/ + - ${common-online.urlPrefix}/onlineOperation/listByOneToManyRelationId/ + - ${common-online.urlPrefix}/onlineOperation/exportByDatasourceId/ + - ${common-online.urlPrefix}/onlineOperation/exportByOneToManyRelationId/ + - ${common-online.urlPrefix}/onlineOperation/downloadDatasource/ + - ${common-online.urlPrefix}/onlineOperation/downloadOneToManyRelation/ + - ${common-online.urlPrefix}/onlineOperation/print/ + editUrlList: + - ${common-online.urlPrefix}/onlineOperation/addDatasource/ + - ${common-online.urlPrefix}/onlineOperation/addOneToManyRelation/ + - ${common-online.urlPrefix}/onlineOperation/updateDatasource/ + - ${common-online.urlPrefix}/onlineOperation/updateOneToManyRelation/ + - ${common-online.urlPrefix}/onlineOperation/deleteDatasource/ + - ${common-online.urlPrefix}/onlineOperation/deleteOneToManyRelation/ + - ${common-online.urlPrefix}/onlineOperation/deleteBatchDatasource/ + - ${common-online.urlPrefix}/onlineOperation/deleteBatchOneToManyRelation/ + - ${common-online.urlPrefix}/onlineOperation/uploadDatasource/ + - ${common-online.urlPrefix}/onlineOperation/uploadOneToManyRelation/ + +common-flow: + # 请慎重修改urlPrefix的缺省配置,注意不要以反斜杠(/)结尾。如必须修改其他路径,请同步修改数据库脚本。 + urlPrefix: /tenantadmin/flow + # 如果为false,流程模块的所有Controller中的接口将不能使用。 + operationEnabled: true + +common-report: + # 注意不要以反斜杠(/)结尾。 + urlPrefix: /tenantadmin/report + # 如果为false,报表模块的所有Controller中的接口将不能使用。 + operationEnabled: true + # 该配置项仅当打印模板中,打印图片字段时,才会需要。 + # 这里的url配置只是一个示例,并不能保证开箱即用,代码示例和说明可参考common-report模块 + # example包内的ReportExampleController中的代码和详细说明。 + imageDownloadUrl: "http://localhost:8083/tenantadmin/report/example/downloadDirectly" + # 该配置用于报表部分的数据权限过滤功能。 + # 当前数据权限需要获取指定部门Ids的所有下级子部门Ids的时候,会调用该接口。 + dataPermAllChildrenDeptIdUrl: "http://localhost:8083/tenantadmin/upms/sysDept/listAllChildDeptIdByParentIds" + # 当前服务是否为可视化后台服务。 + isVisualization: false + # 下面的url列表,请保持反斜杠(/)结尾。 + viewUrlList: + - ${common-report.urlPrefix}/reportOperation/listData/ + +datasync: + producer: + # upms服务作为生产者,需要打开该选项。 + enabled: true + # Redisson 分布式锁的名称。 + # 当存在多个发送者实例时,需要分布式锁控制只有一个实例可以执行消息流水读取并发送到MQ的操作。 + distributeLockName: tenant-datasync + # 每次读取生产者消息流水的等待时间间隔,单位分钟。 + fetchTransIntervalMins: 2 + # 每次读取生产者消息流水的记录数量。 + fetchCount: 10 + +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: apelet_saas + +datafilter: + tenant: + enabled: false + dataperm: + enabled: true + # 在拼接数据权限过滤的SQL时,我们会用到sys_dept_relation表,该表的前缀由此配置项指定。 + # 如果没有前缀,请使用 "" 。 + deptRelationTablePrefix: xy_ + # 是否在每次执行数据权限查询过滤时,都要进行菜单Id和URL之间的越权验证。 + enableMenuPermVerify: true + +sequence: + # 是否使用基于美团Leaf的分布式Id生成器。 + advanceIdGenerator: true + # 多个zk服务之间逗号分隔。 + zkAddress: localhost:2181 + # 与本机的ip一起构成zk中标识不同服务实例的key值。 + idPort: 18083 + # zk中生成WorkNode的路径。不同的业务可以使用不同的路径,以免冲突。 + zkPath: apelet + +common-log: + # 操作日志配置,对应配置文件common-log/OperationLogProperties.java + operation-log: + enabled: false + kafkaTopic: SysOperationLog-TenantAdmin + +mybatis-plus: + mapper-locations: classpath:apelet/tenantadmin/tenant/dao/mapper/*Mapper.xml,apelet/tenantadmin/upms/dao/mapper/*Mapper.xml,apelet/common/report/dao/mapper/*Mapper.xml,apelet/common/datasync/dao/mapper/*Mapper.xml,apelet/common/log/dao/mapper/*Mapper.xml,apelet/common/flow/dao/mapper/*Mapper.xml,apelet/common/online/dao/mapper/*Mapper.xml,apelet/common/generator/dao/mapper/*Mapper.xml,apelet/common/botp/dao/mapper/*Mapper.xml,apelet/tenantadmin/app/dao/mapper/*Mapper.xml,apelet/common/mobile/dao/mapper/*Mapper.xml,apelet/common/eas/dao/mapper/*Mapper.xml + type-aliases-package: apelet.tenantadmin.*.model,apelet.common.report.model,apelet.common.datasync.model,apelet.common.log.model,apelet.common.flow.model,apelet.common.online.model,apelet.common.mobile.model,apelet.common.eas.model + global-config: + db-config: + logic-delete-value: -1 + logic-not-delete-value: 1 + +# 自动分页的配置 +pagehelper: + helperDialect: mysql + reasonable: true + supportMethodsArguments: false + params: count=countSql + +aj: + captcha: + # 缓存local/redis.. + cache-type: redis + # 验证码类型default两种都实例化。 + type: default + # 右下角水印文字(我的水印) + water-mark: 我的水印 + #点选字体样式 默认Font.BOLD + font-style: 1 + #点选字体字体大小 + font-size: 25 + # 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/jigsaw + jigsaw: classpath:images/jigsaw + # 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/pic-click + pic-click: classpath:images/pic-click + history-data-clear-enable: false + # 接口请求次数一分钟限制是否开启 true|false + req-frequency-limit-enable: false + # 验证失败5次,get接口锁定 + req-get-lock-limit: 5 + # 验证失败后,锁定时间间隔,s + req-get-lock-seconds: 360 + # get接口一分钟内请求数限制 + req-get-minute-limit: 30 + # check接口一分钟内请求数限制 + req-check-minute-limit: 30 + # verify接口一分钟内请求数限制 + req-verify-minute-limit: 60 + # 校验滑动拼图允许误差偏移量(默认5像素) + slip-offset: 5 + # aes加密坐标开启或者禁用(true|false) + aes-status: true + # 滑动干扰项(0/1/2) + interference-options: 2 + +# 开发数据库相关配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + # 租户管理数据源。 + tenant-admin: + url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: X7&9p8L2@6z4K7!8 + # 租户业务平台的通用业务数据,如字典、在线表单、流程、报表的定义等数据。 + tenant-common: + url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai + username: root + password: X7&9p8L2@6z4K7!8 + # 默认生成的操作日志数据源配置。 + operation-log: + url: jdbc:mysql://192.168.100.18:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: X7&9p8L2@6z4K7!8 + driverClassName: com.mysql.cj.jdbc.Driver + name: tenant-admin + initialSize: 10 + minIdle: 10 + maxActive: 50 + maxWait: 60000 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + poolPreparedStatements: true + maxPoolPreparedStatementPerConnectionSize: 20 + maxOpenPreparedStatements: 20 + validationQuery: SELECT 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + filters: stat,wall + useGlobalDataSourceStat: true + web-stat-filter: + enabled: true + url-pattern: "/*" + exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*,/actuator/*,/ai/*" + stat-view-servlet: + enabled: true + urlPattern: /druid/* + resetEnable: true +eas: + config: + easurl: + userName: + password: 1 + dcName: + dbType: 2 + ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken.properties + #easurl: https://ehr.jztweb.com + #userName: SSQadmin + #password: SSQadmin + #dcName: EHR_001 + #dbType: 2 + #ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken_zs.properties +hubCloud: + #测试 + #secret: RXmdRE0lf5ngGfLJSSj4 + #appId: 501019058 + secret: + appId: +dingTalk: + secret: 95gAMy_7nWGqRwFbt_kLzRpI9BqKhB7LM3SPgpcvV8Q7s0Az_PMei_yRxWE6LmhV +project: + server: + url: https://jgzy-api.nbjgcloud.com + sync: + url: http://10.22.98.37:9999 + loginUrl: /sys/login + loginName: nbjg + loginPassword: NBjj123456 + userUrl: + projectUrl: +ai: + dify: + baseUrl: + datasetKey: + appKey: + appUrl: + pandoc: + panurl: pandoc + genurl: + templateFileName: sample-orgin-change.docx + luaFileName: parse-html.lua + preUrl: +mineru: + workspace: + batchfile: + timeout: 10000 + baseurl: http://192.168.77.6:8000 + pdfurl: /file_parse + mdSaveDir: /app/work/pdf +cqurl: + config: + url: + gys: + #client_id: xy_mobile + #client_secret: asdasdnsandlkadnlkas!@#@!3WWW + #username: sunquan + client_id: + client_secret: + username: + accountId: + diff --git a/common/common-qy/pom.xml b/common/common-qy/pom.xml new file mode 100644 index 0000000..b0d9f3e --- /dev/null +++ b/common/common-qy/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + common + apelet + 1.0.0 + + + common-qy + jar + 1.0.0 + + + apelet + common-core + 1.0.0 + compile + + + + + + \ No newline at end of file diff --git a/common/common-qy/src/main/java/apelet/Main.java b/common/common-qy/src/main/java/apelet/Main.java new file mode 100644 index 0000000..90bef7d --- /dev/null +++ b/common/common-qy/src/main/java/apelet/Main.java @@ -0,0 +1,17 @@ +package apelet; + +//TIP To Run code, press or +// click the icon in the gutter. +public class Main { + public static void main(String[] args) { + //TIP Press with your caret at the highlighted text + // to see how IntelliJ IDEA suggests fixing it. + System.out.printf("Hello and welcome!"); + + for (int i = 1; i <= 5; i++) { + //TIP Press to start debugging your code. We have set one breakpoint + // for you, but you can always add more by pressing . + System.out.println("i = " + i); + } + } +} \ No newline at end of file diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/controller/DataMoveController.java b/common/common-qy/src/main/java/apelet/qy/importExport/controller/DataMoveController.java new file mode 100644 index 0000000..34541d6 --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/controller/DataMoveController.java @@ -0,0 +1,122 @@ +package apelet.qy.importExport.controller; + + +import apelet.common.core.object.ResponseResult; +import apelet.qy.importExport.service.DataMoveService; +import cn.hutool.core.util.StrUtil; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +@RestController +@Tag(name = "数据迁移导出导入接口") +@RequestMapping("/api/data-move") +public class DataMoveController { + + @Autowired + private DataMoveService dataMoveService; + + + /** + * 导出指定表的数据(生成 ZIP 包) + * @param request 包含表名列表(可选字段列表) + * @return ZIP 文件流 + */ + @PostMapping("/export") + public ResponseEntity exportData(@RequestBody ExportRequest request) throws IOException { + // 预处理 primaryKeyColumn:将驼峰命名转换为蛇形命名 + if (request.getPrimaryKeyColumn() != null) { + request.setPrimaryKeyColumn(StrUtil.toUnderlineCase(request.getPrimaryKeyColumn())); + } + + byte[] zipData = dataMoveService.exportData(request); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setContentDispositionFormData("attachment", "data-move.zip"); + return ResponseEntity.ok().headers(headers).body(zipData); + } + + /** + * 导入 ZIP 包 + * @param file 上传的 ZIP 文件 + * @return 操作结果 + */ + @PostMapping("/import") + public ResponseResult importData(@RequestParam("file") MultipartFile file) throws IOException { + dataMoveService.importData(file.getBytes()); + return ResponseResult.success("导入成功"); + } + + /** + * 根据页面ID列表查询关联的表单信息 + * @param pageIdList 页面ID列表 + * @return 表单信息列表 + */ + @GetMapping("/listFormByPageId") + public ResponseResult>> listFormByPageId(@RequestParam("pageIdList") List pageIdList) { + List> formList = dataMoveService.listFormByPageId(pageIdList); + return ResponseResult.success(formList); + } + + + /** + * 根据页面ID列表查询关联的菜单信息 + * @param pageIdList 页面ID列表 + * @return 菜单信息列表 + */ + @GetMapping("/listMenuByPageIds") + public ResponseResult>> listMenuByPageIds(@RequestParam("pageIdList") List pageIdList) { + List> menuList = dataMoveService.listMenuByPageIds(pageIdList); + return ResponseResult.success(menuList); + } + + /** + * 根据菜单id获取对应菜单权限字信息 + * @param menuId 菜单id + * @return 菜单权限字信息 + */ + @GetMapping("/listPermCodeByMenuId") + public ResponseResult>> listPermCodeByMenuId(@RequestParam("menuId") String menuId) { + List> permCodeList = dataMoveService.listPermCodeByMenuId(menuId); + return ResponseResult.success(permCodeList); + } + + // 请求体封装 + @Data + public static class ExportRequest { + + @Schema(description = "导出类型(页面导出-pageExport,表单导出-formExport,表单元数据-formHeadExport,数据字典-dictExport,菜单(权限导出)-menuExport)" + + ",流程设计导出-flowEntryExport,查询数据源导出-onlCgreportHeadExport" + , requiredMode = Schema.RequiredMode.REQUIRED) + private String type; + + @Schema(description = "主键字段名(可选,如果不提供则尝试从数据库获取主键字段)") + private String primaryKeyColumn; // 主键字段名(可选,如果不提供则尝试从数据库获取主键字段) + + @Schema(description = "主键值列表") + private List primaryKeys; // 主键值列表 + + @Hidden + private List tables; + // getter/setter + + @Data + public static class TableSpec { + private String name; + private List primaryKeys; // 主键值列表 + private String primaryKeyColumn; // 主键字段名(可选,如果不提供则尝试从数据库获取主键字段) + + } + } +} diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/model/DataMoveConfig.java b/common/common-qy/src/main/java/apelet/qy/importExport/model/DataMoveConfig.java new file mode 100644 index 0000000..6e7544a --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/model/DataMoveConfig.java @@ -0,0 +1,71 @@ +package apelet.qy.importExport.model; + +import lombok.Data; + +import javax.xml.bind.annotation.*; +import javax.xml.bind.annotation.XmlRootElement; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@XmlRootElement(name = "dataMoveConfig") +@XmlAccessorType(XmlAccessType.FIELD) +@Data +public class DataMoveConfig { + + @XmlAttribute(name = "id") + private String id; + + @XmlElement(name = "sysCheckInfo") + private SysCheckInfo sysCheckInfo; + + @XmlElement(name = "dataCheckInfo") + private DataCheckInfo dataCheckInfo; + + @Data + @XmlAccessorType(XmlAccessType.FIELD) + public static class SysCheckInfo { + @XmlAttribute(name = "ptm") + private String ptm; + } + + @Data + @XmlAccessorType(XmlAccessType.FIELD) + public static class DataCheckInfo { + @XmlElement(name = "tableInfo") + private TableInfo tableInfo; + } + + @Data + @XmlAccessorType(XmlAccessType.FIELD) + public static class TableInfo { + @XmlElement(name = "table") + private List tables; + } + + @XmlAccessorType(XmlAccessType.FIELD) + @Data + public static class Table { + @XmlAttribute(name = "name") + private String name; + + @XmlElement(name = "fields") + private String fields; + + @XmlElement(name = "primaryKey") + private String primaryKey; + + @XmlTransient + private List primaryValues; + + // 方便获取字段列表 + public List getFieldList() { + if (fields == null || fields.trim().isEmpty()) { + return new ArrayList<>(); + } + return Arrays.stream(fields.split("\\s*,\\s*")).collect(Collectors.toCollection(ArrayList::new)); + } + } +} diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/model/VerifyCode.java b/common/common-qy/src/main/java/apelet/qy/importExport/model/VerifyCode.java new file mode 100644 index 0000000..16f8fb5 --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/model/VerifyCode.java @@ -0,0 +1,14 @@ +package apelet.qy.importExport.model; + +import lombok.Data; + +import javax.xml.bind.annotation.*; + +@XmlRootElement(name = "verifyCode") +@XmlAccessorType(XmlAccessType.FIELD) +@Data +public class VerifyCode { + + @XmlValue + private String code; +} diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/service/DataMoveService.java b/common/common-qy/src/main/java/apelet/qy/importExport/service/DataMoveService.java new file mode 100644 index 0000000..3b6aa91 --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/service/DataMoveService.java @@ -0,0 +1,1056 @@ +package apelet.qy.importExport.service; + +import apelet.common.generator.model.OnlFormHead; +import apelet.common.generator.service.IOnlFormHeadService; +import apelet.qy.importExport.controller.DataMoveController; +import apelet.qy.importExport.model.DataMoveConfig; +import apelet.qy.importExport.model.VerifyCode; +import apelet.qy.importExport.utils.HashUtils; +import apelet.qy.importExport.utils.ZipUtils; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class DataMoveService { + private static final Logger log = LoggerFactory.getLogger(DataMoveService.class); + private static final String VERIFY_FILE = "verify.xml"; + private static final String CONFIG_FILE = "dataMoveConfig.xml"; + + /** + * 外键字段映射配置,按场景类型存储 + * 格式:场景类型 -> {表名.外键字段名 -> 对应表名} + */ + private static final Map> PARENT_KEY_TABLE_MAP = new HashMap<>(); + private static final Map> CHILD_KEY_TABLE_MAP = new HashMap<>(); + private static final Map> SELF_KEY_TABLE_MAP = new HashMap<>(); + private static final Map EXPORT_TYPE_TABLE_MAP = new HashMap<>(); + private static final String FOREIGN_KEY_CONFIG_FILE = "config/foreign-key-mapping.json"; + private static final String DEFAULT_TYPE = "default"; + + @Autowired + private IOnlFormHeadService onlFormHeadService; + + static { + // 从 JSON 配置文件加载外键映射关系 + loadForeignKeyMappings(); + } + + /** + * 从 JSON 配置文件加载外键映射关系 + */ + private static void loadForeignKeyMappings() { + try (InputStream inputStream = DataMoveService.class.getClassLoader() + .getResourceAsStream(FOREIGN_KEY_CONFIG_FILE)) { + if (inputStream == null) { + log.warn("外键映射配置文件不存在: {}", FOREIGN_KEY_CONFIG_FILE); + return; + } + ObjectMapper mapper = new ObjectMapper(); + // 尝试解析为数组格式 + List> configList = mapper.readValue(inputStream, + new TypeReference>>() { + }); + + if (configList != null && !configList.isEmpty()) { + for (Map config : configList) { + String type = (String) config.getOrDefault("type", DEFAULT_TYPE); + @SuppressWarnings("unchecked") + String table = (String) config.get("table"); + if (table != null && !table.isEmpty()) { + EXPORT_TYPE_TABLE_MAP.put(type, table); + } + @SuppressWarnings("unchecked") + Map selfMappings = (Map) config.get("selfMappings"); + if (selfMappings != null && !selfMappings.isEmpty()) { + SELF_KEY_TABLE_MAP.put(type, new HashMap<>(selfMappings)); + log.info("成功加载场景 [{}] 的 {} 条自键映射关系", type, selfMappings.size()); + } + @SuppressWarnings("unchecked") + Map childMappings = (Map) config.get("childMappings"); + if (childMappings != null && !childMappings.isEmpty()) { + CHILD_KEY_TABLE_MAP.put(type, new HashMap<>(childMappings)); + log.info("成功加载场景 [{}] 的 {} 条子键映射关系", type, childMappings.size()); + } + @SuppressWarnings("unchecked") + Map parentMappings = (Map) config.get("parentMappings"); + if (parentMappings != null && !parentMappings.isEmpty()) { + PARENT_KEY_TABLE_MAP.put(type, new HashMap<>(parentMappings)); + log.info("成功加载场景 [{}] 的 {} 条父键映射关系", type, parentMappings.size()); + } + } + } else { + log.warn("外键映射配置文件中未找到有效的配置"); + } + } catch (Exception e) { + log.error("加载外键映射配置文件失败: {}", e.getMessage(), e); + } + } + + /** + * 获取指定场景类型的外键映射表 + * + * @param type 场景类型,如 "pageExport"、"formExport" + * @return 该场景的外键映射表,如果不存在则返回默认场景的映射表 + */ + private static Map getParentKeyMapping(String type) { + Map mappings = PARENT_KEY_TABLE_MAP.get(type); + return mappings != null ? mappings : Collections.emptyMap(); + } + + /** + * 获取指定场景类型的外键映射表 + * + * @param type 场景类型,如 "pageExport"、"formExport" + * @return 该场景的外键映射表,如果不存在则返回默认场景的映射表 + */ + private static Map getChildKeyMapping(String type) { + Map mappings = CHILD_KEY_TABLE_MAP.get(type); + return mappings != null ? mappings : Collections.emptyMap(); + } + + /** + * 获取指定场景类型的自键映射表 + * + * @param type 场景类型,如 "pageExport"、"formExport" + * @return 该场景的外键映射表,如果不存在则返回默认场景的映射表 + */ + private static Map getSelfKeyMappings(String type) { + Map mappings = SELF_KEY_TABLE_MAP.get(type); + return mappings != null ? mappings : Collections.emptyMap(); + } + + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private ObjectMapper objectMapper; + + /** + * 导出数据为 ZIP 字节数组 + */ + public byte[] exportData(DataMoveController.ExportRequest request) throws IOException { + // 1. 构建 dataMoveConfig,同时记录主键信息 + DataMoveConfig config = buildDataMoveConfig2(request); + + // 2. 导出各表数据(使用主键过滤) + Map fileContents = new LinkedHashMap<>(); + String configXml = marshalConfig(config); + fileContents.put(CONFIG_FILE, configXml.getBytes(StandardCharsets.UTF_8)); + + for (DataMoveConfig.Table table : config.getDataCheckInfo().getTableInfo().getTables()) { + String tableName = table.getName(); + List fields = table.getFieldList(); + String pkColumn = table.getPrimaryKey(); + // 从请求中找到对应的主键值列表 + List pkValues = table.getPrimaryValues(); + List> data = exportTableData(tableName, fields, pkColumn, pkValues); + byte[] jsonData = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(data); + fileContents.put(tableName + ".json", jsonData); + } + + // 3. 计算除 verify.xml 外所有文件的哈希,生成 verify.xml + String hash = HashUtils.calculateCombinedHash(fileContents.values()); + VerifyCode verifyCode = new VerifyCode(); + verifyCode.setCode(hash); + String verifyXml = marshalVerify(verifyCode); + fileContents.put(VERIFY_FILE, verifyXml.getBytes(StandardCharsets.UTF_8)); + + // 4. 打包为 ZIP + return ZipUtils.createZip(fileContents); + } + + /** + * 导入 ZIP 数据 + */ + @Transactional(rollbackFor = Exception.class) + public void importData(byte[] zipBytes) throws IOException { + // 1. 解压 ZIP 到内存 + Map unzipped = ZipUtils.unzip(zipBytes); + + // 2. 验证 verify.xml + byte[] verifyBytes = unzipped.get(VERIFY_FILE); + if (verifyBytes == null) { + throw new IllegalArgumentException("ZIP 包缺少 verify.xml"); + } + VerifyCode verifyCode = unmarshalVerify(verifyBytes); + String expectedHash = verifyCode.getCode(); + + // 计算除 verify.xml 外文件的哈希 + Map otherFiles = new LinkedHashMap<>(unzipped); + otherFiles.remove(VERIFY_FILE); + String actualHash = HashUtils.calculateCombinedHash(otherFiles.values()); + if (!expectedHash.equals(actualHash)) { + throw new SecurityException("文件校验失败,可能已被篡改"); + } + + // 3. 解析 dataMoveConfig.xml + byte[] configBytes = unzipped.get(CONFIG_FILE); + if (configBytes == null) { + throw new IllegalArgumentException("ZIP 包缺少 dataMoveConfig.xml"); + } + DataMoveConfig config = unmarshalConfig(configBytes); + + // 4. 按表导入数据(注意顺序:可先禁用外键检查) + disableForeignKeyChecks(); + try { + for (DataMoveConfig.Table table : config.getDataCheckInfo().getTableInfo().getTables()) { + String tableName = table.getName(); + List fields = table.getFieldList(); + String primaryKey = table.getPrimaryKey(); + byte[] jsonBytes = unzipped.get(tableName + ".json"); + if (jsonBytes == null) { + throw new IllegalArgumentException("缺少数据文件: " + tableName + ".json"); + } + List> rows = objectMapper.readValue(jsonBytes, new TypeReference>>() { + }); + //流程管理表导入时将发布相关字段统一修改为未发布时的数据状态 + if (tableName.equals("zz_flow_entry")) { + for (Map row : rows) { + if (row.containsKey("status")) { + //修改发布状态为未发布0 + row.put("status", 0); + } else if (row.containsKey("main_entry_publish_id")) { + //修改部署发布主版本ID为null + row.put("main_entry_publish_id", null); + } else if (row.containsKey("latest_publish_time")) { + //修改最新发布时间为null + row.put("latest_publish_time", null); + } + } + } + importTableData(tableName, fields, primaryKey, rows); + //在导入后如果有表单元数据表导入,自动执行表单元数据同步流程 + if (tableName.equals("onl_form_head")) { + List onlFormHeads = objectMapper.readValue(jsonBytes, new TypeReference>() { + }); + for (OnlFormHead onlFormHead : onlFormHeads) { + onlFormHeadService.syncDB(onlFormHead, 1L); + } + } + } + } finally { + enableForeignKeyChecks(); + } + } + + /** + * 根据页面ID列表查询关联的表单信息 + * + * @param pageIdList 页面ID列表 + * @return 表单信息列表 + */ + public List> listFormByPageId(List pageIdList) { + if (pageIdList == null || pageIdList.isEmpty()) { + return new ArrayList<>(); + } + // 限制最大查询数量,防止性能问题 + if (pageIdList.size() > 1000) { + throw new IllegalArgumentException("查询的页面ID数量不能超过1000个"); + } + String placeholders = String.join(",", Collections.nCopies(pageIdList.size(), "?")); + String sql = "SELECT f.* FROM zz_online_form f " + + "LEFT JOIN zz_online_page p ON p.page_id = f.page_id " + + "WHERE p.page_id IN (" + placeholders + ")"; + return jdbcTemplate.queryForList(sql, pageIdList.toArray()); + } + + + /** + * 根据页面ID列表查询关联的菜单信息 + * + * @param pageIdList 页面ID列表 + * @return 菜单信息列表 + */ + public List> listMenuByPageIds(List pageIdList) { + if (pageIdList == null || pageIdList.isEmpty()) { + return new ArrayList<>(); + } + // 限制最大查询数量,防止性能问题 + if (pageIdList.size() > 1000) { + throw new IllegalArgumentException("查询的页面ID数量不能超过1000个"); + } + String placeholders = String.join(",", Collections.nCopies(pageIdList.size(), "?")); + String sql = "SELECT m.* " + + "FROM xy_sys_menu m " + + "LEFT JOIN zz_online_form f ON f.form_id = m.online_form_id " + + "LEFT JOIN zz_online_page p ON p.page_id = f.page_id " + + "WHERE p.page_id IN (" + placeholders + ")"; + return jdbcTemplate.queryForList(sql, pageIdList.toArray()); + } + + /** + * 根据菜单id获取对应菜单权限字信息 + * + * @param menuId 菜单id + * @return 菜单权限字信息 + */ + public List> listPermCodeByMenuId(String menuId) { + if (menuId == null || menuId.isEmpty()) { + return new ArrayList<>(); + } + // 参数校验:确保menuId是数字,防止SQL注入 + if (!menuId.matches("^\\d+$")) { + throw new IllegalArgumentException("菜单ID必须是数字"); + } + String sql = "SELECT pc.* FROM xy_sys_perm_code pc " + + "LEFT JOIN xy_sys_menu_perm_code mpc ON mpc.perm_code_id = pc.perm_code_id " + + "LEFT JOIN xy_sys_menu sm ON sm.menu_id = mpc.menu_id " + + "WHERE sm.menu_id = ?"; + return jdbcTemplate.queryForList(sql, menuId); + } + + + // ---------- 私有辅助方法 ---------- + +// /** +// * 根据表名和外键字段名列表获取对应的外键表名列表 +// * +// * @param tableName 表名 +// * @param foreignKeyColumns 外键字段名列表 +// * @return 对应的外键表名列表,如果某个字段未找到对应表名则该位置为 null +// */ +// public static List getForeignKeyTableNames(String tableName, List foreignKeyColumns) { +// if (tableName == null || foreignKeyColumns == null || foreignKeyColumns.isEmpty()) { +// return new ArrayList<>(); +// } +// List result = new ArrayList<>(); +// for (String foreignKeyColumn : foreignKeyColumns) { +// if (foreignKeyColumn == null) { +// result.add(null); +// } else { +// String key = tableName + "." + foreignKeyColumn; +// String mapping = getForeignKeyMappings(DEFAULT_TYPE).get(key); +// if (mapping != null && mapping.contains(".")) { +// result.add(mapping.split("\\.")[0]); +// } else { +// result.add(null); +// } +// } +// } +// return result; +// } +// +// private DataMoveConfig buildDataMoveConfig(List tableSpecs) { +// DataMoveConfig config = new DataMoveConfig(); +// config.setDataCheckInfo(new DataMoveConfig.DataCheckInfo()); +// +// DataMoveConfig.TableInfo tableInfo = new DataMoveConfig.TableInfo(); +// List tables = new ArrayList<>(); +// +// // 用于去重检查 +// Set processedTableNames = new HashSet<>(); +// +// // 使用索引遍历,支持在循环过程中动态添加新表 +// for (int i = 0; i < tableSpecs.size(); i++) { +// DataMoveController.ExportRequest.TableSpec spec = tableSpecs.get(i); +// // 跳过已处理的表(去重) +// if (processedTableNames.contains(spec.getName())) { +// continue; +// } +// processedTableNames.add(spec.getName()); +// DataMoveConfig.Table table = new DataMoveConfig.Table(); +// table.setName(spec.getName()); +// // 获取所有字段(或指定字段,这里沿用全字段) +// List columns = getAllColumns(spec.getName()); +// table.setFields(String.join(", ", columns)); +// // 如果指定了主键列,则设置 +// if (spec.getPrimaryKeyColumn() != null && !spec.getPrimaryKeyColumn().isEmpty()) { +// table.setPrimaryKey(spec.getPrimaryKeyColumn()); +// } +// tables.add(table); +// List foreignKeyTableNames = getForeignKeyTableNames(spec.getName(), columns); +// // 将外键对应表名添加到 tableSpecs 中(去重检查) +// if (!foreignKeyTableNames.isEmpty()) { +// for (String foreignKeyTableName : foreignKeyTableNames) { +// // 过滤掉 null 值,并检查是否已存在或已处理 +// if (foreignKeyTableName != null && !processedTableNames.contains(foreignKeyTableName)) { +// DataMoveController.ExportRequest.TableSpec newSpec = new DataMoveController.ExportRequest.TableSpec(); +// newSpec.setName(foreignKeyTableName); +// tableSpecs.add(newSpec); +// log.info("添加外键关联表: {}", foreignKeyTableName); +// } +// } +// } +// } +// +// tableInfo.setTables(tables); +// config.getDataCheckInfo().setTableInfo(tableInfo); +// return config; +// } + + /** + * 根据外键关联关系,获取入参表所有关联的父表和子表,并填充主键信息 + *

+ * 父表:当前表的外键字段指向的表(如 user.role_id -> role.id,role是user的父表) + * 子表:某个表的外键字段指向当前表(如 user_login.user_id -> user.user_id,user_login是user的子表) + *

+ * + * @param request 入参表规格列表,包含导出类型,表名、主键列(可选)和主键值列表(可选) + * 如果未提供主键信息,将导出全表数据 + * @return DataMoveConfig 配置对象 + */ + private DataMoveConfig buildDataMoveConfig2(DataMoveController.ExportRequest request) { + List tableSpecs = new ArrayList<>(); + DataMoveConfig config = new DataMoveConfig(); + config.setDataCheckInfo(new DataMoveConfig.DataCheckInfo()); + + DataMoveConfig.TableInfo tableInfo = new DataMoveConfig.TableInfo(); + List tables = new ArrayList<>(); + + // 用于存储已处理字段的信息:表名.主键列 -> {主键列, 主键值列表} + Map processedFields = new LinkedHashMap<>(); + + String type = request.getType(); + // 如果导出类型不存在,直接返回空config + String startTableName = EXPORT_TYPE_TABLE_MAP.get(type); + if (startTableName == null) { + throw new IllegalArgumentException("导出类型不存在"); + } + //添加初始表 + DataMoveController.ExportRequest.TableSpec tableSpec = new DataMoveController.ExportRequest.TableSpec(); + tableSpec.setName(startTableName); + tableSpec.setPrimaryKeyColumn(request.getPrimaryKeyColumn()); + tableSpec.setPrimaryKeys(request.getPrimaryKeys()); + tableSpecs.add(tableSpec); + // 使用索引遍历,支持在循环过程中动态添加新表 + for (int i = 0; i < tableSpecs.size() ; i++) { + DataMoveController.ExportRequest.TableSpec spec = tableSpecs.get(i); + String tableName = spec.getName(); + + // 生成当前表主键字段的唯一标识 + String fieldKey = tableName + "." + spec.getPrimaryKeyColumn(); + log.info("处理表:{}", tableName); + + // 跳过已处理的字段(去重) + if (processedFields.containsKey(fieldKey)) { + log.info("跳过已处理的字段:{}", fieldKey); + continue; + } + + // 记录当前字段信息 + TableInfo currentTableInfo = new TableInfo( + spec.getPrimaryKeyColumn(), + spec.getPrimaryKeys() + ); + processedFields.put(fieldKey, currentTableInfo); + + // 处理自表结构数据(例如菜单树结构) + processSelfData(tableName, spec, type); + + // 处理父表:当前表的外键字段指向的表 + processParentTables(tableName, spec, tableSpecs, type); + + // 处理子表:某个表的外键字段指向当前表 + processChildTables(tableName, spec, tableSpecs, type); + } + + // 构建 DataMoveConfig.Table 列表,按 tableName 归类数据 + Map> tableGroups = new LinkedHashMap<>(); + for (Map.Entry entry : processedFields.entrySet()) { + String fieldKey = entry.getKey(); + String tableName = fieldKey.substring(0, fieldKey.lastIndexOf('.')); + TableInfo info = entry.getValue(); + tableGroups.computeIfAbsent(tableName, k -> new ArrayList<>()).add(info); + } + + for (Map.Entry> entry : tableGroups.entrySet()) { + String tableName = entry.getKey(); + List infos = entry.getValue(); + + DataMoveConfig.Table table = new DataMoveConfig.Table(); + table.setName(tableName); + // 获取所有字段 + List columns = getAllColumns(tableName); + table.setFields(String.join(", ", columns)); + + // 合并同一表的所有主键值(取第一个非空主键列和合并所有主键值) + String primaryKeyColumn = null; + Set mergedPrimaryKeys = new LinkedHashSet<>(); + for (TableInfo info : infos) { + if (primaryKeyColumn == null && info.primaryKeyColumn != null) { + primaryKeyColumn = info.primaryKeyColumn; + } + if (info.primaryKeys != null) { + mergedPrimaryKeys.addAll(info.primaryKeys); + } + } + table.setPrimaryKey(primaryKeyColumn); + table.setPrimaryValues(new ArrayList<>(mergedPrimaryKeys)); + tables.add(table); + } + + tableInfo.setTables(tables); + config.getDataCheckInfo().setTableInfo(tableInfo); + return config; + } + + /** + * 处理自引用关系:同一表内的字段引用关系(如菜单表的 parent_id 指向 menu_id) + * 根据已知的主键值,递归查找所有相关的自引用记录,并补充到主键列表中 + *

+ * 例如:xy_sys_menu 表中,menu_id=2 的记录的 parent_id=1, + * 则需要将 menu_id=1 也加入到主键列表中,以便导出完整的菜单树 + *

+ *

+ * 支持主键可选场景:如果入参未提供主键信息(primaryKeyColumn或primaryKeys为空), + * 将查询表中所有记录的主键值 + *

+ * + * @param tableName 表名 + * @param spec 表规格,包含主键列和主键值列表 + * @param exportType 导出类型,用于获取对应的 selfMappings 配置 + */ + private void processSelfData(String tableName, + DataMoveController.ExportRequest.TableSpec spec, + String exportType) { + // 获取当前场景的自键映射配置 + Map selfKeyMappings = getSelfKeyMappings(exportType); + if (selfKeyMappings.isEmpty()) { + return; + } + + // 查找当前表的自引用配置 + // 配置格式:"表名.主键列" -> "表名.自引用列" + // 例如:"xy_sys_menu.menu_id" -> "xy_sys_menu.parent_id" + String tablePrefix = tableName + "."; + for (Map.Entry entry : selfKeyMappings.entrySet()) { + String keyField = entry.getKey(); // 如 xy_sys_menu.menu_id + String valueField = entry.getValue(); // 如 xy_sys_menu.parent_id + + // 检查是否是当前表的配置 + if (!keyField.startsWith(tablePrefix)) { + continue; + } + + // 解析字段名 + String[] keyParts = keyField.split("\\."); + String[] valueParts = valueField.split("\\."); + + if (keyParts.length < 2 || valueParts.length < 2) { + continue; + } + + String keyTable = keyParts[0]; // 主键所在的表 + String keyColumn = keyParts[1]; // 主键列名(如 menu_id) + String valueTable = valueParts[0]; // 自引用字段所在的表 + String valueColumn = valueParts[1]; // 自引用列名(如 parent_id) + + // 只处理同一表内的自引用关系 + if (!keyTable.equals(valueTable)) { + log.debug("跳过跨表自引用配置: {} -> {}", keyField, valueField); + continue; + } + + // 获取当前主键信息 + String primaryKeyColumn = spec.getPrimaryKeyColumn(); + List primaryKeys = spec.getPrimaryKeys(); + + // 如果没有主键列信息,尝试使用配置中的主键列 + if (primaryKeyColumn == null || primaryKeyColumn.isEmpty()) { + primaryKeyColumn = keyColumn; + spec.setPrimaryKeyColumn(primaryKeyColumn); + } + + // 如果主键列表为空,查询表中所有记录的主键值 + if (primaryKeys == null) { + primaryKeys = new ArrayList<>(); + spec.setPrimaryKeys(primaryKeys); + } + + // 递归查找所有相关的自引用记录 + Set allRelatedPrimaryKeys = findAllSelfReferencedKeys( + tableName, primaryKeyColumn, valueColumn, new HashSet<>(primaryKeys)); + + // 更新主键列表 + if (allRelatedPrimaryKeys.size() > primaryKeys.size()) { + primaryKeys.clear(); + primaryKeys.addAll(allRelatedPrimaryKeys); + log.info("表 {} 处理自引用关系完成,主键列表已更新,共 {} 条记录", tableName, primaryKeys.size()); + } + } + } + + /** + * 递归查找所有自引用相关的主键值 + * + * @param tableName 表名 + * @param primaryKeyColumn 主键列名 + * @param selfRefColumn 自引用列名(如 parent_id) + * @param knownPrimaryKeys 已知的主键值集合 + * @return 所有相关的主键值集合(包含已知和递归查找到的) + */ + private Set findAllSelfReferencedKeys(String tableName, String primaryKeyColumn, + String selfRefColumn, Set knownPrimaryKeys) { + Set allKeys = new LinkedHashSet<>(knownPrimaryKeys); + Set toProcess = new LinkedHashSet<>(knownPrimaryKeys); + + // 循环查找,直到没有新的主键需要处理 + while (!toProcess.isEmpty()) { + // 查询当前待处理主键对应的自引用字段值 + List selfRefValues = getSelfRefValuesFromTable( + tableName, primaryKeyColumn, selfRefColumn, new ArrayList<>(toProcess)); + + // 清空待处理集合,准备下一轮 + toProcess.clear(); + + // 将新发现的主键值加入集合 + for (String value : selfRefValues) { + if (value != null && !value.isEmpty() && !allKeys.contains(value)) { + allKeys.add(value); + toProcess.add(value); // 新发现的主键需要继续查找其自引用关系 + } + } + } + + return allKeys; + } + + /** + * 根据主键值查询自引用字段的值 + * + * @param tableName 表名 + * @param primaryKeyColumn 主键列名 + * @param selfRefColumn 自引用列名 + * @param primaryKeys 主键值列表 + * @return 自引用字段值列表(去重、非空) + */ + private List getSelfRefValuesFromTable(String tableName, String primaryKeyColumn, + String selfRefColumn, List primaryKeys) { + if (primaryKeys == null || primaryKeys.isEmpty()) { + return new ArrayList<>(); + } + + String placeholders = String.join(",", Collections.nCopies(primaryKeys.size(), "?")); + String sql = String.format("SELECT DISTINCT %s FROM %s WHERE %s IN (%s)", + selfRefColumn, tableName, primaryKeyColumn, placeholders); + + List values = jdbcTemplate.queryForList(sql, primaryKeys.toArray(), String.class); + + return values.stream() + .filter(Objects::nonNull) + .filter(v -> !v.isEmpty()) + .distinct() + .collect(Collectors.toList()); + } + + /** + * 处理父表:当前表的外键字段指向的表 + * 例如:user.role_id -> role.id,role是user的父表 + * 父表的主键值 = 当前表中外键字段的值 + *

+ * 支持主键可选场景:如果入参未提供主键信息(primaryKeyColumn或primaryKeys为空), + * 将查询当前表所有记录的外键字段值作为父表的主键值 + *

+ */ + private void processParentTables(String tableName, + DataMoveController.ExportRequest.TableSpec spec, + List tableSpecs, + String exportType) { + // 获取当前表的所有字段 + List columns = getAllColumns(tableName); + + // 遍历所有字段,查找外键关联 + for (String column : columns) { + String key = tableName + "." + column; + Map parentKeyMapping = getParentKeyMapping(exportType); + if (parentKeyMapping.isEmpty()) { + return; + } + String mapping = parentKeyMapping.get(key); + + if (mapping != null && mapping.contains(".")) { + log.info("处理表 {} 的外键字段 {}", tableName, column); + // mapping格式:目标表.目标字段,如 role.id + String[] parts = mapping.split("\\."); + String parentTableName = parts[0]; + String parentPrimaryKeyColumn = parts[1]; + +// // 如果父表外键字段已处理,跳过 +// if (processedFields.containsKey(mapping)) { +// log.info("跳过已处理的父表外键字段:{}", mapping); +// continue; +// } + + // 从当前表中获取外键字段的值作为父表的主键值 + // 查询当前表的外键字段值 + List parentPrimaryKeys = getForeignKeyValuesFromTable( + tableName, column, spec.getPrimaryKeyColumn(), spec.getPrimaryKeys()); + + if (parentPrimaryKeys.isEmpty()) { + log.info("表 {} 的外键字段 {} 没有找到关联值,跳过父表 {}", tableName, column, parentTableName); + continue; + } + + // 检查tableSpecs中是否已存在相同name的数据 + DataMoveController.ExportRequest.TableSpec existingSpec = null; + for (DataMoveController.ExportRequest.TableSpec ts : tableSpecs) { + if (parentTableName.equals(ts.getName())) { + existingSpec = ts; + break; + } + } + + if (existingSpec != null) { + // 已存在相同name的数据,合并parentPrimaryKeys + List existingPrimaryKeys = existingSpec.getPrimaryKeys(); + if (existingPrimaryKeys != null && parentPrimaryKeys != null) { + Set mergedKeys = new LinkedHashSet<>(existingPrimaryKeys); + mergedKeys.addAll(parentPrimaryKeys); + existingSpec.setPrimaryKeys(new ArrayList<>(mergedKeys)); + } else { + existingSpec.setPrimaryKeys(new ArrayList<>(parentPrimaryKeys)); + } + log.info("合并父表主键: {},主键列: {},合并后主键值: {}", parentTableName, parentPrimaryKeyColumn, existingSpec.getPrimaryKeys()); + } else { + // 不存在相同name的数据,新增 + DataMoveController.ExportRequest.TableSpec parentSpec = new DataMoveController.ExportRequest.TableSpec(); + parentSpec.setName(parentTableName); + parentSpec.setPrimaryKeyColumn(parentPrimaryKeyColumn); + parentSpec.setPrimaryKeys(parentPrimaryKeys); + tableSpecs.add(parentSpec); + log.info("添加父表: {},主键列: {},主键值: {}", parentTableName, parentPrimaryKeyColumn, parentPrimaryKeys); + } + } + } + } + + /** + * 处理子表:某个表的外键字段指向当前表 + * 例如:user_login.user_id -> user.user_id,user_login是user的子表 + * 子表的主键值 = 从子表中查询外键字段匹配当前表主键值的记录的主键 + *

+ * 支持主键可选场景: + * - 如果入参提供了主键值列表,使用WHERE IN条件查询子表关联记录 + * - 如果未提供主键信息,通过INNER JOIN查询子表所有关联记录 + *

+ */ + private void processChildTables(String tableName, + DataMoveController.ExportRequest.TableSpec spec, + List tableSpecs, + String exportType) { + // 遍历FOREIGN_KEY_TABLE_MAP,找到指向当前表的映射 + // 格式:子表.外键字段 -> 当前表.当前表字段 + String targetPrefix = tableName + "."; + Map childKeyMapping = getChildKeyMapping(exportType); + if (childKeyMapping.isEmpty()) { + return; + } + for (Map.Entry entry : childKeyMapping.entrySet()) { + String sourceKey = entry.getKey(); // 如 user_login.user_id + String targetValue = entry.getValue(); // 如 user.user_id + + // 检查是否指向当前表 + if (targetValue.startsWith(targetPrefix)) { + String[] sourceParts = sourceKey.split("\\."); + String childTableName = sourceParts[0]; + String childForeignKeyColumn = sourceParts[1]; + log.info("子表 {} 的外键字段 {}", childTableName, childForeignKeyColumn); + + String[] targetParts = targetValue.split("\\."); + String currentTableForeignKeyColumn = targetParts[1]; + +// // 如果子表外键已处理,跳过 +// if (processedFields.containsKey(sourceKey)) { +// log.info("跳过已处理的子表外键字段:{}", sourceKey); +// continue; +// } + + // 获取子表的主键列 + String childPrimaryKeyColumn = getTablePrimaryKeyColumn(childTableName); + + // 从子表中查询:外键字段值匹配当前表主键值的记录的主键值 + List childPrimaryKeys = getChildTablePrimaryKeys(tableName, + childTableName, childPrimaryKeyColumn, childForeignKeyColumn, + currentTableForeignKeyColumn, spec.getPrimaryKeys()); + + if (childPrimaryKeys.isEmpty()) { + log.info("子表 {} 中没有找到关联到表 {} 的记录", childTableName, tableName); + continue; + } + + // 检查tableSpecs中是否已存在相同name的数据 + DataMoveController.ExportRequest.TableSpec existingSpec = null; + for (DataMoveController.ExportRequest.TableSpec ts : tableSpecs) { + if (childTableName.equals(ts.getName())) { + existingSpec = ts; + break; + } + } + + if (existingSpec != null) { + // 已存在相同name的数据,合并childPrimaryKeys + List existingPrimaryKeys = existingSpec.getPrimaryKeys(); + if (existingPrimaryKeys != null && childPrimaryKeys != null) { + Set mergedKeys = new LinkedHashSet<>(existingPrimaryKeys); + mergedKeys.addAll(childPrimaryKeys); + existingSpec.setPrimaryKeys(new ArrayList<>(mergedKeys)); + } else { + existingSpec.setPrimaryKeys(new ArrayList<>(childPrimaryKeys)); + } + log.info("合并子表主键: {},主键列: {},合并后主键值: {}", childTableName, childPrimaryKeyColumn, existingSpec.getPrimaryKeys()); + } else { + // 不存在相同name的数据,新增 + DataMoveController.ExportRequest.TableSpec childSpec = new DataMoveController.ExportRequest.TableSpec(); + childSpec.setName(childTableName); + childSpec.setPrimaryKeyColumn(childPrimaryKeyColumn); + childSpec.setPrimaryKeys(childPrimaryKeys); + tableSpecs.add(childSpec); + log.info("添加子表: {},主键列: {},主键值: {}", childTableName, childPrimaryKeyColumn, childPrimaryKeys); + } + } + } + } + + /** + * 从表中查询外键字段的值 + * + * @param tableName 表名 + * @param foreignKeyColumn 外键字段名 + * @param primaryKeyColumn 主键字段名 + * @param primaryKeys 主键值列表 + * @return 外键字段值列表(去重、非空) + */ + private List getForeignKeyValuesFromTable(String tableName, String foreignKeyColumn, + String primaryKeyColumn, List primaryKeys) { + String sql; + List values; + + // 如果主键信息完整,使用WHERE条件查询;否则查询全表 + if (primaryKeyColumn != null && !primaryKeyColumn.isEmpty() + && primaryKeys != null && !primaryKeys.isEmpty()) { + String placeholders = String.join(",", Collections.nCopies(primaryKeys.size(), "?")); + sql = String.format("SELECT DISTINCT %s FROM %s WHERE %s IN (%s)", + foreignKeyColumn, tableName, primaryKeyColumn, placeholders); + values = jdbcTemplate.queryForList(sql, primaryKeys.toArray(), String.class); + } else { + sql = String.format("SELECT DISTINCT %s FROM %s", foreignKeyColumn, tableName); + values = jdbcTemplate.queryForList(sql, String.class); + } + + return values.stream() + .filter(Objects::nonNull) + .filter(v -> !v.isEmpty()) + .distinct() + .collect(Collectors.toList()); + } + + /** + * 从子表中查询主键值 + * + * @param parentTableName 父表名 + * @param childTableName 子表名 + * @param childPrimaryKeyColumn 子表主键字段名 + * @param childForeignKeyColumn 子表外键字段名 + * @param currentTableColumn 当前表关联字段名 + * @param currentTablePrimaryKeys 当前表主键值列表 + * @return 子表主键值列表 + */ + private List getChildTablePrimaryKeys(String parentTableName, String childTableName, String childPrimaryKeyColumn, + String childForeignKeyColumn, String currentTableColumn, + List currentTablePrimaryKeys) { + String sql; + List values; + + if (currentTablePrimaryKeys == null || currentTablePrimaryKeys.isEmpty()) { + // 无主键值时,通过 INNER JOIN 查询子表 + sql = String.format("SELECT DISTINCT %s FROM %s INNER JOIN %s ON %s.%s = %s.%s", + childPrimaryKeyColumn, childTableName, parentTableName, + childTableName, childForeignKeyColumn, parentTableName, currentTableColumn); + values = jdbcTemplate.queryForList(sql, String.class); + } else { + // 有主键值时,使用 WHERE IN 条件查询 + String placeholders = String.join(",", Collections.nCopies(currentTablePrimaryKeys.size(), "?")); + sql = String.format("SELECT DISTINCT %s FROM %s WHERE %s IN (%s)", + childPrimaryKeyColumn, childTableName, childForeignKeyColumn, placeholders); + values = jdbcTemplate.queryForList(sql, currentTablePrimaryKeys.toArray(), String.class); + } + + return values.stream() + .filter(Objects::nonNull) + .filter(v -> !v.isEmpty()) + .distinct() + .collect(Collectors.toList()); + } + + /** + * 获取表的主键字段名 + * + * @param tableName 表名 + * @return 主键字段名,如果没有主键返回 "id" + */ + private String getTablePrimaryKeyColumn(String tableName) { + final String[] pkColumn = {"id"}; // 默认主键字段名 + jdbcTemplate.execute((Connection con) -> { + DatabaseMetaData meta = con.getMetaData(); + try (ResultSet rs = meta.getPrimaryKeys(con.getCatalog(), null, tableName)) { + if (rs.next()) { + pkColumn[0] = rs.getString("COLUMN_NAME"); + } + } + return null; + }); + return pkColumn[0]; + } + + /** + * 内部类:用于存储表的主键信息 + */ + private static class TableInfo { + String primaryKeyColumn; + List primaryKeys; + + TableInfo(String primaryKeyColumn, List primaryKeys) { + this.primaryKeyColumn = primaryKeyColumn; + this.primaryKeys = primaryKeys; + } + } + + /** + * 获取表的所有字段名(按数据库顺序) + */ + private List getAllColumns(String tableName) { + // 使用 JDBC 元数据 + List columns = new ArrayList<>(); + jdbcTemplate.execute((Connection con) -> { + DatabaseMetaData meta = con.getMetaData(); + try (ResultSet rs = meta.getColumns(con.getCatalog(), null, tableName, "%")) { + while (rs.next()) { + columns.add(rs.getString("COLUMN_NAME")); + } + } + return null; + }); + if (columns.isEmpty()) { + throw new IllegalArgumentException("表 " + tableName + " 不存在或没有字段"); + } + return columns; + } + + /** + * 导出表数据(仅查询指定字段) + */ + private List> exportTableData( + String tableName, List fields, String pkColumn, List pkValues) { + String sql = "SELECT " + String.join(", ", fields) + " FROM " + tableName; + if (pkColumn != null && pkValues != null && !pkValues.isEmpty()) { + sql += " WHERE " + pkColumn + " IN (" + + String.join(",", Collections.nCopies(pkValues.size(), "?")) + ")"; + return jdbcTemplate.queryForList(sql, pkValues.toArray()); + } else { + return jdbcTemplate.queryForList(sql); + } + } + + /** + * 导入表数据:先清空表,再批量插入 + */ + private void importTableData( + String tableName, List fields, String primaryKey, List> rows) { + if (rows.isEmpty()) { + log.info("表 {} 无数据,跳过", tableName); + return; + } + + // 1. 删除旧数据(根据主键或全表) + if (primaryKey != null && !primaryKey.isEmpty()) { + // 提取所有主键值 + List pkValues = rows.stream() + .map(row -> row.get(primaryKey)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!pkValues.isEmpty()) { + String deleteSql = "DELETE FROM " + tableName + " WHERE " + primaryKey + + " IN (" + String.join(",", Collections.nCopies(pkValues.size(), "?")) + ")"; + jdbcTemplate.update(deleteSql, pkValues.toArray()); + } + } else { + // 无主键信息,全表清空 + jdbcTemplate.execute("DELETE FROM " + tableName); + } + + // 2. 插入新数据(与原逻辑相同) + String placeholders = String.join(",", Collections.nCopies(fields.size(), "?")); + String insertSql = "INSERT INTO " + tableName + " (" + String.join(",", fields) + ") VALUES (" + placeholders + ")"; + List batchArgs = new ArrayList<>(); + for (Map row : rows) { + Object[] values = new Object[fields.size()]; + for (int i = 0; i < fields.size(); i++) { + values[i] = row.get(fields.get(i)); + } + batchArgs.add(values); + } + jdbcTemplate.batchUpdate(insertSql, batchArgs); + } + + // 外键约束控制(MySQL示例) + private void disableForeignKeyChecks() { + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0"); + } + + private void enableForeignKeyChecks() { + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1"); + } + + // JAXB 工具方法 + private String marshalConfig(DataMoveConfig config) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(DataMoveConfig.class); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + StringWriter sw = new StringWriter(); + marshaller.marshal(config, sw); + return sw.toString(); + } catch (Exception e) { + throw new IOException("序列化 config 失败", e); + } + } + + private DataMoveConfig unmarshalConfig(byte[] bytes) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(DataMoveConfig.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + return (DataMoveConfig) unmarshaller.unmarshal(new ByteArrayInputStream(bytes)); + } catch (Exception e) { + throw new IOException("解析 config.xml 失败", e); + } + } + + private String marshalVerify(VerifyCode verify) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(VerifyCode.class); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + StringWriter sw = new StringWriter(); + marshaller.marshal(verify, sw); + return sw.toString(); + } catch (Exception e) { + throw new IOException("序列化 verify 失败", e); + } + } + + private VerifyCode unmarshalVerify(byte[] bytes) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(VerifyCode.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + return (VerifyCode) unmarshaller.unmarshal(new ByteArrayInputStream(bytes)); + } catch (Exception e) { + throw new IOException("解析 verify.xml 失败", e); + } + } +} \ No newline at end of file diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/utils/HashUtils.java b/common/common-qy/src/main/java/apelet/qy/importExport/utils/HashUtils.java new file mode 100644 index 0000000..b707646 --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/utils/HashUtils.java @@ -0,0 +1,33 @@ +package apelet.qy.importExport.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; + +public class HashUtils { + + /** + * 计算多个字节数组的组合哈希(排序后拼接再哈希) + */ + public static String calculateCombinedHash(Collection byteContents) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + // 将所有内容按固定顺序合并(这里使用自然顺序,但为了可重复,先排序?但 Collection 无序,我们按传入顺序处理即可) + for (byte[] content : byteContents) { + digest.update(content); + } + byte[] hash = digest.digest(); + return bytesToHex(hash); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("SHA-256 算法不可用", e); + } + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/common/common-qy/src/main/java/apelet/qy/importExport/utils/ZipUtils.java b/common/common-qy/src/main/java/apelet/qy/importExport/utils/ZipUtils.java new file mode 100644 index 0000000..8957249 --- /dev/null +++ b/common/common-qy/src/main/java/apelet/qy/importExport/utils/ZipUtils.java @@ -0,0 +1,56 @@ +package apelet.qy.importExport.utils; + +import java.io.*; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class ZipUtils { + + /** + * 将多个文件打包为 ZIP 字节数组 + * @param files 文件名 -> 内容字节 + * @return ZIP 字节 + */ + public static byte[] createZip(Map files) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + for (Map.Entry entry : files.entrySet()) { + ZipEntry zipEntry = new ZipEntry(entry.getKey()); + zos.putNextEntry(zipEntry); + zos.write(entry.getValue()); + zos.closeEntry(); + } + } + return baos.toByteArray(); + } + + /** + * 解压 ZIP 字节到内存 Map + * @param zipBytes ZIP 字节 + * @return 文件名 -> 内容字节 + */ + public static Map unzip(byte[] zipBytes) throws IOException { + Map result = new LinkedHashMap<>(); + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String name = entry.getName(); + // 防止路径遍历攻击 + if (name.contains("..") || name.startsWith("/")) { + throw new SecurityException("非法文件名: " + name); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int len; + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + result.put(name, baos.toByteArray()); + } + } + return result; + } +} \ No newline at end of file diff --git a/common/common-qy/src/main/resources/config/foreign-key-mapping.json b/common/common-qy/src/main/resources/config/foreign-key-mapping.json new file mode 100644 index 0000000..0045786 --- /dev/null +++ b/common/common-qy/src/main/resources/config/foreign-key-mapping.json @@ -0,0 +1,93 @@ +[ + { + "type": "formExport", + "table": "zz_online_form", + "selfMappings": { + }, + "parentMappings": { + "zz_online_form.page_id": "zz_online_page.page_id", + "zz_online_page_datasource.datasource_id": "zz_online_datasource.datasource_id", + "zz_online_datasource_table.relation_id": "zz_online_datasource_relation.relation_id", + "zz_online_datasource_table.table_id": "zz_online_table.table_id", + "zz_online_datasource.dblink_id": "zz_online_dblink.dblink_id", + "zz_online_column.dict_id": "zz_online_dict.dict_id", + "zz_online_column_rule.rule_id": "zz_online_rule.rule_id" + }, + "childMappings": { + "zz_online_form_datasource.form_id": "zz_online_form.form_id", + "zz_online_page_datasource.page_id": "zz_online_page.page_id", + "zz_online_datasource_table.datasource_id": "zz_online_datasource.datasource_id", + "zz_online_column_rule.column_id": "zz_online_column.column_id", + "zz_online_column.table_id": "zz_online_table.table_id", + "zz_online_virtual_column.datasource_id": "zz_online_page_datasource.datasource_id" + } + }, + { + "type": "formHeadExport", + "table": "onl_form_head", + "selfMappings": { + }, + "parentMappings": { + }, + "childMappings": { + "onl_form_field.head_id": "onl_form_head.id", + "onl_form_page.head_id": "onl_form_head.id", + "onl_form_find.head_id": "onl_form_head.id", + "onl_form_key.head_id": "onl_form_head.id", + "onl_form_check.head_id": "onl_form_head.id", + "onl_form_index.head_id": "onl_form_head.id" + } + }, + { + "type": "dictExport", + "table": "zz_online_dict", + "selfMappings": { + }, + "parentMappings": { + "zz_online_dict.dblink_id": "zz_online_dblink.dblink_id" + }, + "childMappings": { + } + }, + { + "type": "menuExport", + "table": "xy_sys_menu", + "selfMappings": { + "xy_sys_menu.menu_id": "xy_sys_menu.parent_id", + "xy_sys_perm_code.perm_code_id": "xy_sys_perm_code.parent_id" + }, + "parentMappings": { + "xy_sys_menu_perm_code.perm_code_id": "xy_sys_perm_code.perm_code_id" + }, + "childMappings": { + "xy_sys_menu_perm_code.menu_id": "xy_sys_menu.menu_id" + + } + }, + { + "type": "flowEntryExport", + "table": "zz_flow_entry", + "selfMappings": { + }, + "parentMappings": { + "zz_flow_entry.category_id": "zz_flow_category.category_id", + "zz_flow_entry.main_entry_publish_id": "zz_flow_entry_publish.entry_publish_id" + }, + "childMappings": { + "zz_flow_entry.entry_id": "zz_flow_entry_variable.entry_id", + "zz_flow_entry_publish.entry_publish_id":"zz_flow_entry_publish_variable.entry_publish_id" + } + }, + { + "type": "onlCgreportHeadExport", + "table": "onl_cgreport_head", + "selfMappings": { + }, + "parentMappings": { + }, + "childMappings": { + "onl_cgreport_param.cgrhead_id": "onl_cgreport_head.id", + "onl_cgreport_item.cgrhead_id": "onl_cgreport_head.id" + } + } +] \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 0000000..e31bfac --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + common + apelet + 1.0.0 + + + common + pom + + common-qy + + 1.0.0 + + + apelet + common-core + 1.0.0 + compile + + + + apelet + common-generator + 1.0.0 + compile + + + apelet + common-online + 1.0.0 + compile + + + common-orm + apelet + 1.0.0 + compile + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1fad44e --- /dev/null +++ b/pom.xml @@ -0,0 +1,374 @@ + + + 4.0.0 + + apelet + apelet_saas + 1.0.0 + apelet_saas + pom + + + 2021.0.6 + 2021.1 + 2.7.10 + 2.7.10 + UTF-8 + 1.8 + 1.8 + 1.8 + + 2.10.13 + 20.0 + 2.6 + 4.4 + 1.8 + 5.0.0 + 5.8.23 + 2.3.2 + 0.12.3 + 1.2.83 + 1.1.5 + 2.9.3 + 1.4.2.Final + 1.18.20 + 4.3.0 + 2.4.0 + 2.12.10 + 6.2.0.Final + 6.8.0 + 0.2.0 + 2.1.1 + 3.15.4 + 8.4.5 + 1.1.4 + 2.0.0 + 4.5.0 + 2.10.5 + 1.5.22 + 1.3.0 + + 1.8.0 + 1.2.16 + 3.5.4.1 + 1.4.7 + + + + application-tenant + common + + + + + + dev + + true + + + dev + + localhost:8848 + + localhost:8848 + + DEFAULT_GROUP + + + + localhost:9092 + + + + + test + + test + + grcc792d:8848 + + grcc792d:8848 + + DEFAULT_GROUP + + + + gr1bb775:9092 + + + + + prod + + prod + + grcc792d:8848 + + grcc792d:8848 + + DEFAULT_GROUP + + + + gr1bb775:9092 + + + + + + + + nexus + nexus + http://171.83.126.150:8081/repository/maven-releases/ + + + + maven-snapshots + maven-snapshots + http://171.83.126.150:8081/repository/maven-snapshots/ + + + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + javax.servlet + javax.servlet-api + + + + com.github.danielwegener + logback-kafka-appender + 0.2.0-RC2 + runtime + + + org.springframework.boot + spring-boot-starter-logging + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-cache + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-actuator + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + org.apache.httpcomponents + httpasyncclient + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + + org.projectlombok + lombok + provided + + + + org.apache.curator + curator-recipes + ${curator.version} + + + + org.apache.kafka + kafka-clients + ${kafka.version} + + + org.scala-lang + scala-library + ${scala-library.version} + + + org.apache.skywalking + apm-toolkit-logback-1.x + 7.0.0 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.apache.commons + commons-jexl3 + 3.3 + + + + + + + mysql + mysql-connector-java + 8.0.22 + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + ${project.name} + + + src/main/resources + + **/*.* + + false + + + src/main/resources + + **/*.xml + + true + + + src/main/resources + + **/*.yml + + true + + + src/main/java + + **/*.xml + + false + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + maven-compiler-plugin + 3.8.0 + + + -parameters + + ${maven.compiler.target} + ${maven.compiler.source} + UTF-8 + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + + + +