diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9405b2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +out +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +logs +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd95df1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 若依 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f64b4ef --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +## 系统模块 + +~~~ +com.muyu +├── cloud-ui // 前端框架 [80] +├── cloud-gateway // 网关模块 [8080] +├── cloud-auth // 认证中心 [9200] +├── cloud-common // 通用模块 +│ └── cloud-common-core // 核心模块 +│ └── cloud-common-datascope // 权限范围 +│ └── cloud-common-datasource // 多数据源 +│ └── cloud-common-log // 日志记录 +│ └── cloud-common-redis // 缓存服务 +│ └── cloud-common-seata // 分布式事务 +│ └── cloud-common-security // 安全模块 +│ └── cloud-common-swagger // 系统接口 +│ └── cloud-common-system // 系统基础 +├── cloud-modules // 业务模块 +│ └── cloud-system // 系统模块 [9201] +│ └── cloud-gen // 代码生成 [9202] +│ └── cloud-job // 定时任务 [9203] +│ └── cloud-file // 文件服务 [9300] +├── cloud-visual // 图形化管理模块 +│ └── cloud-visual-monitor // 监控中心 [9100] +├──pom.xml // 公共依赖 +~~~ diff --git a/cloud-auth/pom.xml b/cloud-auth/pom.xml new file mode 100644 index 0000000..0a76aae --- /dev/null +++ b/cloud-auth/pom.xml @@ -0,0 +1,80 @@ + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + cloud-auth + + + cloud-auth认证授权中心 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.muyu + cloud-common-security + + + + + com.muyu + cloud-common-api-doc + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java b/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java new file mode 100644 index 0000000..e6ea4fd --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java @@ -0,0 +1,19 @@ +package com.muyu.auth; + +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 认证授权中心 + * + * @author muyu + */ +@EnableMyFeignClients +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudAuthApplication { + public static void main (String[] args) { + SpringApplication.run(CloudAuthApplication.class, args); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java b/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java new file mode 100644 index 0000000..d870762 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java @@ -0,0 +1,72 @@ +package com.muyu.auth.controller; + +import com.muyu.auth.form.LoginBody; +import com.muyu.auth.form.RegisterBody; +import com.muyu.auth.service.SysLoginService; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.auth.AuthUtil; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * token 控制 + * + * @author muyu + */ +@RestController +public class TokenController { + @Autowired + private TokenService tokenService; + + @Autowired + private SysLoginService sysLoginService; + + @PostMapping("login") + public Result login (@RequestBody LoginBody form) { + // 用户登录 + LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); + // 获取登录token + return Result.success(tokenService.createToken(userInfo)); + } + + @DeleteMapping("logout") + public Result logout (HttpServletRequest request) { + String token = SecurityUtils.getToken(request); + if (StringUtils.isNotEmpty(token)) { + String username = JwtUtils.getUserName(token); + // 删除用户缓存记录 + AuthUtil.logoutByToken(token); + // 记录用户退出日志 + sysLoginService.logout(username); + } + return Result.success(); + } + + @PostMapping("refresh") + public Result refresh (HttpServletRequest request) { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { + // 刷新令牌有效期 + tokenService.refreshToken(loginUser); + return Result.success(); + } + return Result.success(); + } + + @PostMapping("register") + public Result register (@RequestBody RegisterBody registerBody) { + // 用户注册 + sysLoginService.register(registerBody.getUsername(), registerBody.getPassword()); + return Result.success(); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java b/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java new file mode 100644 index 0000000..999ad44 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java @@ -0,0 +1,34 @@ +package com.muyu.auth.form; + +/** + * 用户登录对象 + * + * @author muyu + */ +public class LoginBody { + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + public String getUsername () { + return username; + } + + public void setUsername (String username) { + this.username = username; + } + + public String getPassword () { + return password; + } + + public void setPassword (String password) { + this.password = password; + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java b/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java new file mode 100644 index 0000000..c2a4d5a --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java @@ -0,0 +1,10 @@ +package com.muyu.auth.form; + +/** + * 用户注册对象 + * + * @author muyu + */ +public class RegisterBody extends LoginBody { + +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java new file mode 100644 index 0000000..ff0af1a --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java @@ -0,0 +1,127 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.enums.UserStatus; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.remote.RemoteUserService; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 登录校验方法 + * + * @author muyu + */ +@Component +public class SysLoginService { + @Autowired + private RemoteUserService remoteUserService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysRecordLogService recordLogService; + + @Autowired + private RedisService redisService; + + /** + * 登录 + */ + public LoginUser login (String username, String password) { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); + throw new ServiceException("用户/密码必须填写"); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); + throw new ServiceException("用户密码不在指定范围"); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new ServiceException("用户名不在指定范围"); + } + // IP黑名单校验 + String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); + throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); + } + // 查询用户信息 + Result userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + + if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new ServiceException("登录用户:" + username + " 不存在"); + } + + if (Result.FAIL == userResult.getCode()) { + throw new ServiceException(userResult.getMsg()); + } + + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + passwordService.validate(user, password); + recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + return userInfo; + } + + public void logout (String loginName) { + recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); + } + + /** + * 注册 + */ + public void register (String username, String password) { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) { + throw new ServiceException("用户/密码必须填写"); + } + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { + throw new ServiceException("账户长度必须在2到20个字符之间"); + } + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + throw new ServiceException("密码长度必须在5到20个字符之间"); + } + + // 注册用户信息 + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + Result registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); + + if (Result.FAIL == registerResult.getCode()) { + throw new ServiceException(registerResult.getMsg()); + } + recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java new file mode 100644 index 0000000..5f3c126 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java @@ -0,0 +1,76 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * 登录密码方法 + * + * @author muyu + */ +@Component +public class SysPasswordService { + @Autowired + private RedisService redisService; + + private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT; + + private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME; + + @Autowired + private SysRecordLogService recordLogService; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * + * @return 缓存键key + */ + private String getCacheKey (String username) { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate (SysUser user, String password) { + String username = user.getUserName(); + + Integer retryCount = redisService.getCacheObject(getCacheKey(username)); + + if (retryCount == null) { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) { + String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime); + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, errMsg); + throw new ServiceException(errMsg); + } + + if (!matches(user, password)) { + retryCount = retryCount + 1; + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount)); + redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new ServiceException("用户不存在/密码错误"); + } else { + clearLoginRecordCache(username); + } + } + + public boolean matches (SysUser user, String rawPassword) { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache (String loginName) { + if (redisService.hasKey(getCacheKey(loginName))) { + redisService.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java new file mode 100644 index 0000000..2d4de80 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java @@ -0,0 +1,44 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysLogininfor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 记录日志方法 + * + * @author muyu + */ +@Component +public class SysRecordLogService { + @Autowired + private RemoteLogService remoteLogService; + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * + * @return + */ + public void recordLogininfor (String username, String status, String message) { + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(IpUtils.getIpAddr()); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS); + } else if (Constants.LOGIN_FAIL.equals(status)) { + logininfor.setStatus(Constants.LOGIN_FAIL_STATUS); + } + remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER); + } +} diff --git a/cloud-auth/src/main/resources/banner.txt b/cloud-auth/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-auth/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-auth/src/main/resources/bootstrap.yml b/cloud-auth/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..c115c71 --- /dev/null +++ b/cloud-auth/src/main/resources/bootstrap.yml @@ -0,0 +1,47 @@ +# Tomcat +server: + port: 9500 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud +# Spring +spring: + application: + # 应用名称 + name: cloud-auth + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + diff --git a/cloud-auth/src/main/resources/logback/dev.xml b/cloud-auth/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..880c02d --- /dev/null +++ b/cloud-auth/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-auth/src/main/resources/logback/prod.xml b/cloud-auth/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..d05b780 --- /dev/null +++ b/cloud-auth/src/main/resources/logback/prod.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-auth/src/main/resources/logback/test.xml b/cloud-auth/src/main/resources/logback/test.xml new file mode 100644 index 0000000..d05b780 --- /dev/null +++ b/cloud-auth/src/main/resources/logback/test.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-common/cloud-common-api-doc/pom.xml b/cloud-common/cloud-common-api-doc/pom.xml new file mode 100644 index 0000000..261e760 --- /dev/null +++ b/cloud-common/cloud-common-api-doc/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-api-doc + + + cloud-common-api-doc系统接口 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + + diff --git a/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java b/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java new file mode 100644 index 0000000..08d7bff --- /dev/null +++ b/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java @@ -0,0 +1,34 @@ +package com.muyu.common.api.doc.config; + +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringDocConfig { + + @Value("${spring.application.name}") + private String applicationName; + + private Info info(){ + return new Info() + .title(String.format("%S-微服务接口文档", applicationName)) + .description("微服务接口文档,根据此接口文档可以进行前后端功能对接/联调") + .version("v1.0.0"); + } + private ExternalDocumentation externalDocumentation() { + return new ExternalDocumentation() + .description("服务总站") + .url("https://gitea.qinmian.online"); + } + + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(info()) + .externalDocs(externalDocumentation()); + } +} diff --git a/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..4b1b17b --- /dev/null +++ b/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.api.doc.config.SpringDocConfig \ No newline at end of file diff --git a/cloud-common/cloud-common-core/pom.xml b/cloud-common/cloud-common-core/pom.xml new file mode 100644 index 0000000..57cf128 --- /dev/null +++ b/cloud-common/cloud-common-core/pom.xml @@ -0,0 +1,169 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-core + + + cloud-common-core核心模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + com.alibaba + transmittable-thread-local + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + 3.5.6 + + + com.github.jsqlparser + jsqlparser + + + org.mybatis + mybatis + + + + + + org.mybatis + mybatis + 3.5.16 + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + io.jsonwebtoken + jjwt + + + + + org.apache.commons + commons-lang3 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + + jakarta.servlet + jakarta.servlet-api + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + + + + + + javax.annotation + javax.annotation-api + + + + + org.projectlombok + lombok + + + + + org.apache.skywalking + apm-toolkit-trace + + + + + org.apache.skywalking + apm-toolkit-logback-1.x + + + + cn.hutool + hutool-all + + + + diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java new file mode 100644 index 0000000..36180e5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java @@ -0,0 +1,176 @@ +package com.muyu.common.core.annotation; + +import com.muyu.common.core.utils.poi.ExcelHandlerAdapter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +/** + * 自定义导出Excel数据注解 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel { + /** + * 导出时在excel中排序 + */ + public int sort () default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name () default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat () default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp () default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator () default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale () default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode () default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height () default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width () default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix () default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue () default ""; + + /** + * 提示信息 + */ + public String prompt () default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo () default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge () default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport () default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr () default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics () default false; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType () default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor () default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor () default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor () default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color () default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align () default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler () default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args () default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type () default Type.ALL; + + public enum Type { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } + + public enum ColumnType { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java new file mode 100644 index 0000000..f8fc165 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author muyu + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels { + Excel[] value (); +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..532c9f2 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java @@ -0,0 +1,58 @@ +package com.muyu.common.core.constant; + +/** + * 缓存常量信息 + * + * @author muyu + */ +public class CacheConstants { + /** + * 缓存有效期,默认720(分钟) + */ + public final static long EXPIRATION = 720; + + /** + * 缓存刷新时间,默认120(分钟) + */ + public final static long REFRESH_TIME = 120; + + /** + * 密码最大错误次数 + */ + public final static int PASSWORD_MAX_RETRY_COUNT = 5; + + /** + * 密码锁定时间,默认10(分钟) + */ + public final static long PASSWORD_LOCK_TIME = 10; + + /** + * 权限缓存前缀 + */ + public final static String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + /** + * 登录IP黑名单 cache key + */ + public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java new file mode 100644 index 0000000..a3540fc --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java @@ -0,0 +1,134 @@ +package com.muyu.common.core.constant; + +/** + * 通用常量信息 + * + * @author muyu + */ +public class Constants { + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "0"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"}; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = {"com.muyu"}; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.muyu.common.core.utils.file"}; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java new file mode 100644 index 0000000..fde9967 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java @@ -0,0 +1,186 @@ +package com.muyu.common.core.constant; + +/** + * 代码生成通用常量 + * + * @author muyu + */ +public class GenConstants { + /** + * 单表(增删改查) + */ + public static final String TPL_CRUD = "crud"; + + /** + * 树表(增删改查) + */ + public static final String TPL_TREE = "tree"; + + /** + * 主子表(增删改查) + */ + public static final String TPL_SUB = "sub"; + + /** + * 树编码字段 + */ + public static final String TREE_CODE = "treeCode"; + + /** + * 树父编码字段 + */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** + * 树名称字段 + */ + public static final String TREE_NAME = "treeName"; + + /** + * 上级菜单ID字段 + */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** + * 上级菜单名称字段 + */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** + * 数据库字符串类型 + */ + public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"}; + + /** + * 数据库文本类型 + */ + public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"}; + + /** + * 数据库时间类型 + */ + public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"}; + + /** + * 数据库数字类型 + */ + public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", + "bigint", "float", "double", "decimal"}; + + /** + * 页面不需要编辑字段 + */ + public static final String[] COLUMNNAME_NOT_EDIT = {"id", "create_by", "create_time", "del_flag"}; + + /** + * 页面不需要显示的列表字段 + */ + public static final String[] COLUMNNAME_NOT_LIST = {"id", "create_by", "create_time", "del_flag", "update_by", + "update_time"}; + + /** + * 页面不需要查询字段 + */ + public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark"}; + + /** + * Entity基类字段 + */ + public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime", "remark"}; + + /** + * Tree基类字段 + */ + public static final String[] TREE_ENTITY = {"parentName", "parentId", "orderNum", "ancestors"}; + + /** + * 文本框 + */ + public static final String HTML_INPUT = "input"; + + /** + * 文本域 + */ + public static final String HTML_TEXTAREA = "textarea"; + + /** + * 下拉框 + */ + public static final String HTML_SELECT = "select"; + + /** + * 单选框 + */ + public static final String HTML_RADIO = "radio"; + + /** + * 复选框 + */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** + * 日期控件 + */ + public static final String HTML_DATETIME = "datetime"; + + /** + * 图片上传控件 + */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** + * 文件上传控件 + */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** + * 富文本控件 + */ + public static final String HTML_EDITOR = "editor"; + + /** + * 字符串类型 + */ + public static final String TYPE_STRING = "String"; + + /** + * 整型 + */ + public static final String TYPE_INTEGER = "Integer"; + + /** + * 长整型 + */ + public static final String TYPE_LONG = "Long"; + + /** + * 浮点型 + */ + public static final String TYPE_DOUBLE = "Double"; + + /** + * 高精度计算类型 + */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** + * 时间类型 + */ + public static final String TYPE_DATE = "Date"; + + /** + * 模糊查询 + */ + public static final String QUERY_LIKE = "LIKE"; + + /** + * 相等查询 + */ + public static final String QUERY_EQ = "EQ"; + + /** + * 需要 + */ + public static final String REQUIRE = "1"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..36e0783 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.constant; + +/** + * 返回状态码 + * + * @author muyu + */ +public class HttpStatus { + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java new file mode 100644 index 0000000..b5bceb0 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.constant; + +/** + * 任务调度通用常量 + * + * @author muyu + */ +public class ScheduleConstants { + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** + * 执行目标key + */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** + * 默认 + */ + public static final String MISFIRE_DEFAULT = "0"; + + /** + * 立即触发执行 + */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** + * 触发一次执行 + */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** + * 不触发立即执行 + */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status (String value) { + this.value = value; + } + + public String getValue () { + return value; + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..c862929 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java @@ -0,0 +1,48 @@ +package com.muyu.common.core.constant; + +/** + * 权限相关通用常量 + * + * @author muyu + */ +public class SecurityConstants { + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + /** + * 角色权限 + */ + public static final String ROLE_PERMISSION = "role_permission"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java new file mode 100644 index 0000000..bb59b70 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java @@ -0,0 +1,23 @@ +package com.muyu.common.core.constant; + +/** + * 服务名称 + * + * @author muyu + */ +public class ServiceNameConstants { + /** + * 认证服务的serviceid + */ + public static final String AUTH_SERVICE = "cloud-auth"; + + /** + * 系统模块的serviceid + */ + public static final String SYSTEM_SERVICE = "cloud-system"; + + /** + * 文件服务的serviceid + */ + public static final String FILE_SERVICE = "cloud-file"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java new file mode 100644 index 0000000..38abd57 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java @@ -0,0 +1,24 @@ +package com.muyu.common.core.constant; + +/** + * Token的Key常量 + * + * @author muyu + */ +public class TokenConstants { + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + public final static String SECRET = "abcdefghijklmnsalieopadfaqawefwerstuvwxyz"; + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java new file mode 100644 index 0000000..0df401c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java @@ -0,0 +1,113 @@ +package com.muyu.common.core.constant; + +/** + * 用户常量信息 + * + * @author muyu + */ +public class UserConstants { + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** + * 正常状态 + */ + public static final String NORMAL = "0"; + + /** + * 异常状态 + */ + public static final String EXCEPTION = "1"; + + /** + * 用户封禁状态 + */ + public static final String USER_DISABLE = "1"; + + /** + * 角色封禁状态 + */ + public static final String ROLE_DISABLE = "1"; + + /** + * 部门正常状态 + */ + public static final String DEPT_NORMAL = "0"; + + /** + * 部门停用状态 + */ + public static final String DEPT_DISABLE = "1"; + + /** + * 字典正常状态 + */ + public static final String DICT_NORMAL = "0"; + + /** + * 是否为系统默认(是) + */ + public static final String YES = "Y"; + + /** + * 是否菜单外链(是) + */ + public static final String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + public static final String NO_FRAME = "1"; + + /** + * 菜单类型(目录) + */ + public static final String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + public static final String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + public static final String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + public final static String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + public final static String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + public final static String INNER_LINK = "InnerLink"; + + /** + * 校验是否唯一的返回标识 + */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java new file mode 100644 index 0000000..80ea42b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java @@ -0,0 +1,83 @@ +package com.muyu.common.core.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 获取当前线程变量中的 用户id、用户名称、Token等信息 + * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 + * + * @author muyu + */ +public class SecurityContextHolder { + private static final TransmittableThreadLocal> THREAD_LOCAL = new TransmittableThreadLocal<>(); + + public static void set (String key, Object value) { + Map map = getLocalMap(); + map.put(key, value == null ? StringUtils.EMPTY : value); + } + + public static String get (String key) { + Map map = getLocalMap(); + return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); + } + + public static T get (String key, Class clazz) { + Map map = getLocalMap(); + return StringUtils.cast(map.getOrDefault(key, null)); + } + + public static Map getLocalMap () { + Map map = THREAD_LOCAL.get(); + if (map == null) { + map = new ConcurrentHashMap(); + THREAD_LOCAL.set(map); + } + return map; + } + + public static void setLocalMap (Map threadLocalMap) { + THREAD_LOCAL.set(threadLocalMap); + } + + public static Long getUserId () { + return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); + } + + public static void setUserId (String account) { + set(SecurityConstants.DETAILS_USER_ID, account); + } + + public static String getUserName () { + return get(SecurityConstants.DETAILS_USERNAME); + } + + public static void setUserName (String username) { + set(SecurityConstants.DETAILS_USERNAME, username); + } + + public static String getUserKey () { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey (String userKey) { + set(SecurityConstants.USER_KEY, userKey); + } + + public static String getPermission () { + return get(SecurityConstants.ROLE_PERMISSION); + } + + public static void setPermission (String permissions) { + set(SecurityConstants.ROLE_PERMISSION, permissions); + } + + public static void remove () { + THREAD_LOCAL.remove(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java new file mode 100644 index 0000000..70faa40 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java @@ -0,0 +1,112 @@ +package com.muyu.common.core.domain; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.HttpStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Result implements Serializable { + /** + * 成功 + */ + public static final int SUCCESS = Constants.SUCCESS; + /** + * 失败 + */ + public static final int FAIL = Constants.FAIL; + /** + * 警告 + */ + public static final int WARN = HttpStatus.WARN; + + private static final long serialVersionUID = 1L; + private int code; + + private String msg; + + private T data; + + public static Result success () { + return restResult(null, SUCCESS, null); + } + + public static Result success (T data) { + return restResult(data, SUCCESS, null); + } + + public static Result success (T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static Result error () { + return restResult(null, FAIL, null); + } + + public static Result error (String msg) { + return restResult(null, FAIL, msg); + } + + public static Result error (T data) { + return restResult(data, FAIL, null); + } + + public static Result error (T data, String msg) { + return restResult(data, FAIL, msg); + } + + public static Result error (int code, String msg) { + return restResult(null, code, msg); + } + + + + public static Result warn () { + return restResult(null, WARN, null); + } + + public static Result warn (String msg) { + return restResult(null, WARN, msg); + } + + public static Result warn (T data) { + return restResult(data, WARN, null); + } + + public static Result warn (T data, String msg) { + return restResult(data, WARN, msg); + } + + public static Result warn (int code, String msg) { + return restResult(null, code, msg); + } + + private static Result restResult (T data, int code, String msg) { + return Result.builder() + .code(code) + .data(data) + .msg(msg) + .build(); + } + + public static Boolean isError (Result ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess (Result ret) { + return Result.SUCCESS == ret.getCode(); + } + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java new file mode 100644 index 0000000..32ff39a --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.enums; + +/** + * 用户状态 + * + * @author muyu + */ +public enum UserStatus { + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus (String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode () { + return code; + } + + public String getInfo () { + return info; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java new file mode 100644 index 0000000..eb32d0b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 验证码错误异常类 + * + * @author muyu + */ +public class CaptchaException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CaptchaException (String msg) { + super(msg); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java new file mode 100644 index 0000000..4f12893 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.exception; + +/** + * 检查异常 + * + * @author muyu + */ +public class CheckedException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CheckedException (String message) { + super(message); + } + + public CheckedException (Throwable cause) { + super(cause); + } + + public CheckedException (String message, Throwable cause) { + super(message, cause); + } + + public CheckedException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java new file mode 100644 index 0000000..82249cf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 演示模式异常 + * + * @author muyu + */ +public class DemoModeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DemoModeException () { + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java new file mode 100644 index 0000000..b14e03c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java @@ -0,0 +1,51 @@ +package com.muyu.common.core.exception; + +/** + * 全局异常 + * + * @author muyu + */ +public class GlobalException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException () { + } + + public GlobalException (String message) { + this.message = message; + } + + public String getDetailMessage () { + return detailMessage; + } + + public GlobalException setDetailMessage (String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage () { + return message; + } + + public GlobalException setMessage (String message) { + this.message = message; + return this; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java new file mode 100644 index 0000000..f211c7f --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 内部认证异常 + * + * @author muyu + */ +public class InnerAuthException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public InnerAuthException (String message) { + super(message); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java new file mode 100644 index 0000000..6cb8636 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 权限异常 + * + * @author muyu + */ +public class PreAuthorizeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PreAuthorizeException () { + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java new file mode 100644 index 0000000..5039bc0 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java @@ -0,0 +1,65 @@ +package com.muyu.common.core.exception; + +/** + * 业务异常 + * + * @author muyu + */ +public final class ServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException () { + } + + public ServiceException (String message) { + this.message = message; + } + + public ServiceException (String message, Integer code) { + this.message = message; + this.code = code; + } + + public String getDetailMessage () { + return detailMessage; + } + + public ServiceException setDetailMessage (String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage () { + return message; + } + + public ServiceException setMessage (String message) { + this.message = message; + return this; + } + + public Integer getCode () { + return code; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java new file mode 100644 index 0000000..8de4bbf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.exception; + +/** + * 工具类异常 + * + * @author muyu + */ +public class UtilException extends RuntimeException { + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException (Throwable e) { + super(e.getMessage(), e); + } + + public UtilException (String message) { + super(message); + } + + public UtilException (String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java new file mode 100644 index 0000000..40293bf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.auth; + +/** + * 未能通过的登录认证异常 + * + * @author muyu + */ +public class NotLoginException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotLoginException (String message) { + super(message); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java new file mode 100644 index 0000000..e464840 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的权限认证异常 + * + * @author muyu + */ +public class NotPermissionException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotPermissionException (String permission) { + super(permission); + } + + public NotPermissionException (String[] permissions) { + super(StringUtils.join(permissions, ",")); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java new file mode 100644 index 0000000..53a1522 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的角色认证异常 + * + * @author muyu + */ +public class NotRoleException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotRoleException (String role) { + super(role); + } + + public NotRoleException (String[] roles) { + super(StringUtils.join(roles, ",")); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..9bb1356 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.exception.base; + +/** + * 基础异常 + * + * @author muyu + */ +public class BaseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException (String module, String code, Object[] args, String defaultMessage) { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException (String module, String code, Object[] args) { + this(module, code, args, null); + } + + public BaseException (String module, String defaultMessage) { + this(module, null, null, defaultMessage); + } + + public BaseException (String code, Object[] args) { + this(null, code, args, null); + } + + public BaseException (String defaultMessage) { + this(null, null, null, defaultMessage); + } + + public String getModule () { + return module; + } + + public String getCode () { + return code; + } + + public Object[] getArgs () { + return args; + } + + public String getDefaultMessage () { + return defaultMessage; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java new file mode 100644 index 0000000..ae2e184 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.exception.file; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author muyu + */ +public class FileException extends BaseException { + private static final long serialVersionUID = 1L; + + public FileException (String code, Object[] args, String msg) { + super("file", code, args, msg); + } + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..3a85df3 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author muyu + */ +public class FileNameLengthLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException (int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}, "the filename is too long"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..7570be5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author muyu + */ +public class FileSizeLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException (long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}, "the filesize is too large"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java new file mode 100644 index 0000000..94341ab --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java @@ -0,0 +1,52 @@ +package com.muyu.common.core.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author muyu + */ +public class FileUploadException extends Exception { + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException () { + this(null, null); + } + + public FileUploadException (final String msg) { + this(msg, null); + } + + public FileUploadException (String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace (PrintStream stream) { + super.printStackTrace(stream); + if (cause != null) { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace (PrintWriter writer) { + super.printStackTrace(writer); + if (cause != null) { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause () { + return cause; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..3a993c2 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java @@ -0,0 +1,67 @@ +package com.muyu.common.core.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author muyu + */ +public class InvalidExtensionException extends FileUploadException { + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException (String[] allowedExtension, String extension, String filename) { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension () { + return allowedExtension; + } + + public String getExtension () { + return extension; + } + + public String getFilename () { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException (String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException (String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException (String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException (String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java new file mode 100644 index 0000000..aee364a --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java @@ -0,0 +1,29 @@ +package com.muyu.common.core.exception.job; + +/** + * 计划策略异常 + * + * @author muyu + */ +public class TaskException extends Exception { + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException (String msg, Code code) { + this(msg, code, null); + } + + public TaskException (String msg, Code code, Exception nestedEx) { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode () { + return code; + } + + public enum Code { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..a95a57b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 验证码失效异常类 + * + * @author muyu + */ +public class CaptchaExpireException extends UserException { + private static final long serialVersionUID = 1L; + + public CaptchaExpireException () { + super("user.jcaptcha.expire", null); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java new file mode 100644 index 0000000..f113749 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java @@ -0,0 +1,16 @@ +package com.muyu.common.core.exception.user; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author muyu + */ +public class UserException extends BaseException { + private static final long serialVersionUID = 1L; + + public UserException (String code, Object[] args) { + super("user", code, args, null); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..7615cda --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author muyu + */ +public class UserPasswordNotMatchException extends UserException { + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException () { + super("user.password.not.match", null); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java new file mode 100644 index 0000000..77a12fe --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java @@ -0,0 +1,19 @@ +package com.muyu.common.core.feign; + +import feign.Contract; +import org.springframework.cloud.openfeign.support.SpringMvcContract; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FeginConfig { +// @Bean +// public Contract feignConfiguration() { +// return new feign.Contract.Default(); +// } + + @Bean + public Contract feignConfiguration() { + return new SpringMvcContract(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java new file mode 100644 index 0000000..fe5e9fa --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java @@ -0,0 +1,94 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author muyu + */ +public class CharsetKit { + /** + * ISO-8859-1 + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** + * UTF-8 + */ + public static final String UTF_8 = "UTF-8"; + /** + * GBK + */ + public static final String GBK = "GBK"; + + /** + * ISO-8859-1 + */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** + * UTF-8 + */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** + * GBK + */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * + * @return Charset + */ + public static Charset charset (String charset) { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * + * @return 转换后的字符集 + */ + public static String convert (String source, String srcCharset, String destCharset) { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * + * @return 转换后的字符集 + */ + public static String convert (String source, Charset srcCharset, Charset destCharset) { + if (null == srcCharset) { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset () { + return Charset.defaultCharset().name(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java new file mode 100644 index 0000000..fb57cb9 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java @@ -0,0 +1,903 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author muyu + */ +public class Convert { + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static String toStr (Object value, String defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof String) { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static String toStr (Object value) { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Character toChar (Object value, Character defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof Character) { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Character toChar (Object value) { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Byte toByte (Object value, Byte defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Byte) { + return (Byte) value; + } + if (value instanceof Number) { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Byte.parseByte(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Byte toByte (Object value) { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Short toShort (Object value, Short defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Short) { + return (Short) value; + } + if (value instanceof Number) { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Short.parseShort(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Short toShort (Object value) { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Number toNumber (Object value, Number defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Number) { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return NumberFormat.getInstance().parse(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Number toNumber (Object value) { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Integer toInt (Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Integer.parseInt(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Integer toInt (Object value) { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static Integer[] toIntArray (String str) { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static Long[] toLongArray (String str) { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * + * @return 结果 + */ + public static Integer[] toIntArray (String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Integer[]{}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0 ; i < arr.length ; i++) { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * + * @return 结果 + */ + public static Long[] toLongArray (String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Long[]{}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0 ; i < arr.length ; i++) { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static String[] toStrArray (String str) { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * + * @return 结果 + */ + public static String[] toStrArray (String split, String str) { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Long toLong (Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Long toLong (Object value) { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Double toDouble (Object value, Double defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Double toDouble (Object value) { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Float toFloat (Object value, Float defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Float) { + return (Float) value; + } + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Float.parseFloat(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Float toFloat (Object value) { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Boolean toBool (Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Boolean toBool (Object value) { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * + * @return Enum + */ + public static > E toEnum (Class clazz, Object value, E defaultValue) { + if (value == null) { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * + * @return Enum + */ + public static > E toEnum (Class clazz, Object value) { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static BigInteger toBigInteger (Object value, BigInteger defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigInteger) { + return (BigInteger) value; + } + if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigInteger(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigInteger toBigInteger (Object value) { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static BigDecimal toBigDecimal (Object value, BigDecimal defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + if (value instanceof Long) { + return new BigDecimal((Long) value); + } + if (value instanceof Double) { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigDecimal toBigDecimal (Object value) { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * + * @return 字符串 + */ + public static String utf8Str (Object obj) { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * + * @return 字符串 + */ + public static String str (Object obj, String charsetName) { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * + * @return 字符串 + */ + public static String str (Object obj, Charset charset) { + if (null == obj) { + return null; + } + + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof byte[] || obj instanceof Byte[]) { + if (obj instanceof byte[]) { + return str((byte[]) obj, charset); + } else { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0 ; i < length ; i++) { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } else if (obj instanceof ByteBuffer) { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * + * @return 字符串 + */ + public static String str (byte[] bytes, String charset) { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * + * @return 解码后的字符串 + */ + public static String str (byte[] data, Charset charset) { + if (data == null) { + return null; + } + + if (null == charset) { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * + * @return 字符串 + */ + public static String str (ByteBuffer data, String charset) { + if (data == null) { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * + * @return 字符串 + */ + public static String str (ByteBuffer data, Charset charset) { + if (null == charset) { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + + /** + * 半角转全角 + * + * @param input String. + * + * @return 全角字符串. + */ + public static String toSBC (String input) { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * + * @return 全角字符串. + */ + public static String toSBC (String input, Set notConvertSet) { + char[] c = input.toCharArray(); + for (int i = 0 ; i < c.length ; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') { + c[i] = '\u3000'; + } else if (c[i] < '\177') { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * + * @return 半角字符串 + */ + public static String toDBC (String input) { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * + * @return 替换后的字符 + */ + public static String toDBC (String text, Set notConvertSet) { + char[] c = text.toCharArray(); + for (int i = 0 ; i < c.length ; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') { + c[i] = ' '; + } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * + * @return 中文大写数字 + */ + public static String digitUppercase (double n) { + String[] fraction = {"角", "分"}; + String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; + String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}}; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0 ; i < fraction.length ; i++) { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0 ; i < unit[0].length && integerPart > 0 ; i++) { + String p = ""; + for (int j = 0 ; j < unit[1].length && n > 0 ; j++) { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java new file mode 100644 index 0000000..0c07cf5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java @@ -0,0 +1,77 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author muyu + */ +public class StrFormatter { + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * + * @return 结果 + */ + public static String format (final String strPattern, final Object... argArray) { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0 ; argIndex < argArray.length ; argIndex++) { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) { + if (handledPosition == 0) { + return strPattern; + } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } else { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } else { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } else { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java new file mode 100644 index 0000000..fb15c59 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java @@ -0,0 +1,158 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; + +/** + * 时间工具类 + * + * @author muyu + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate () { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate () { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime () { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow () { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow (final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime (final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr (final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime (final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath () { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime () { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate (Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate () { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance (Date endDate, Date startTime) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate (LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate (LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java new file mode 100644 index 0000000..e6abdf9 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java @@ -0,0 +1,35 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 错误信息处理类。 + * + * @author muyu + */ +public class ExceptionUtil { + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage (Throwable e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage (Exception e) { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) { + return ""; + } + String msg = root.getMessage(); + if (msg == null) { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java new file mode 100644 index 0000000..62816cb --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java @@ -0,0 +1,165 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.text.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SecureDigestAlgorithm; + +import javax.crypto.SecretKey; +import java.util.Date; +import java.util.Map; + +/** + * Jwt工具类 + * + * @author muyu + */ +public class JwtUtils { + + /** + * 加密算法 + */ + private final static SecureDigestAlgorithm ALGORITHM = Jwts.SIG.HS256; + /** + * 私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥不能外露,只在服务端使用,在任何场景都不应该流露出去。 + * 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 + * 应该大于等于 256位(长度32及以上的字符串),并且是随机的字符串 + */ + private final static String secret = TokenConstants.SECRET; + /** + * 秘钥实例 + */ + public static final SecretKey KEY = Keys.hmacShaKeyFor(secret.getBytes()); + /** + * jwt签发者 + */ + private final static String JWT_ISS = "MUYU"; + /** + * jwt主题 + */ + private final static String SUBJECT = "Peripherals"; + + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * + * @return 令牌 + */ + public static String createToken (Map claims) { + return Jwts.builder() + // 设置头部信息header + .header().add("typ", "JWT").add("alg", "HS256").and() + // 设置自定义负载信息payload + .claims(claims) + // 签发时间 + .issuedAt(new Date()) + // 主题 + .subject(SUBJECT) + // 签发者 + .issuer(JWT_ISS) + // 签名 + .signWith(KEY, ALGORITHM) + .compact(); + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * + * @return 数据声明 + */ + public static Claims parseToken (String token) { + return Jwts.parser() + .verifyWith(KEY) + .build() + .parseSignedClaims(token) + .getPayload(); + } + + /** + * 根据令牌获取用户标识 + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserKey (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserKey (Claims claims) { + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户ID + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserId (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据身份信息获取用户ID + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserId (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据令牌获取用户名 + * + * @param token 令牌 + * + * @return 用户名 + */ + public static String getUserName (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取用户名 + * + * @param claims 身份信息 + * + * @return 用户名 + */ + public static String getUserName (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取键值 + * + * @param claims 身份信息 + * @param key 键 + * + * @return 值 + */ + public static String getValue (Claims claims, String key) { + return Convert.toStr(claims.get(key), ""); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java new file mode 100644 index 0000000..d4b0554 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java @@ -0,0 +1,32 @@ +package com.muyu.common.core.utils; + +import com.github.pagehelper.PageHelper; +import com.muyu.common.core.utils.sql.SqlUtil; +import com.muyu.common.core.web.page.PageDomain; +import com.muyu.common.core.web.page.TableSupport; + +/** + * 分页工具类 + * + * @author muyu + */ +public class PageUtils extends PageHelper { + /** + * 设置请求分页数据 + */ + public static void startPage () { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage () { + PageHelper.clearPage(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..9a50531 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java @@ -0,0 +1,289 @@ +package com.muyu.common.core.utils; + +import com.alibaba.fastjson2.JSON; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.text.Convert; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import reactor.core.publisher.Mono; + + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 客户端工具类 + * + * @author muyu + */ +public class ServletUtils { + /** + * 获取String参数 + */ + public static String getParameter (String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter (String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt (String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt (String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool (String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool (String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * + * @return Map + */ + public static Map getParams (ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * + * @return Map + */ + public static Map getParamMap (ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest () { + try { + return Objects.requireNonNull(getRequestAttributes()).getRequest(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse () { + try { + return Objects.requireNonNull(getRequestAttributes()).getResponse(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取session + */ + public static HttpSession getSession () { + return Objects.requireNonNull(getRequest()).getSession(); + } + + public static ServletRequestAttributes getRequestAttributes () { + try { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } catch (Exception e) { + return null; + } + } + + public static String getHeader (HttpServletRequest request, String name) { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + public static Map getHeaders (HttpServletRequest request) { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString (HttpServletResponse response, String string) { + try { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest (HttpServletRequest request) { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * + * @return 编码后的内容 + */ + public static String urlEncode (String str) { + try { + return URLEncoder.encode(str, Constants.UTF8); + } catch (UnsupportedEncodingException e) { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * + * @return 解码后的内容 + */ + public static String urlDecode (String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value, int code) { + return webFluxResponseWriter(response, HttpStatus.OK, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, HttpStatus status, Object value, int code) { + return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + Result result = Result.error(code, value.toString()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..c37a65c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java @@ -0,0 +1,114 @@ +package com.muyu.common.core.utils; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author muyu + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor { + /** + * Spring应用上下文环境 + */ + private static ConfigurableListableBeanFactory beanFactory; + + /** + * 获取对象 + * + * @param name + * + * @return Object 一个以所给名字注册的bean的实例 + * + * @throws org.springframework.beans.BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean (String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * + * @return + * + * @throws org.springframework.beans.BeansException + */ + public static T getBean (Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * + * @return boolean + */ + public static boolean containsBean (String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * + * @return boolean + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static boolean isSingleton (String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * + * @return Class 注册对象的类型 + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static Class getType (String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * + * @return + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static String[] getAliases (String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy (T invoker) { + return (T) AopContext.currentProxy(); + } + + @Override + public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java new file mode 100644 index 0000000..ec8f557 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java @@ -0,0 +1,504 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 字符串工具类 + * + * @author muyu + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + /** + * 空字符串 + */ + private static final String NULLSTR = ""; + + /** + * 下划线 + */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * + * @return value 返回值 + */ + public static T nvl (T value, T defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (Collection coll) { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Collection coll) { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + * * @return true:为空 false:非空 + */ + public static boolean isEmpty (Object[] objects) { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Object[] objects) { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (Map map) { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Map map) { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (String str) { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty (String str) { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * + * @return true:为空 false:非空 + */ + public static boolean isNull (Object object) { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * + * @return true:非空 false:空 + */ + public static boolean isNotNull (Object object) { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * + * @return true:是数组 false:不是数组 + */ + public static boolean isArray (Object object) { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim (String str) { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * + * @return 结果 + */ + public static String substring (final String str, int start) { + if (str == null) { + return NULLSTR; + } + + if (start < 0) { + start = str.length() + start; + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * + * @return 结果 + */ + public static String substring (final String str, int start, int end) { + if (str == null) { + return NULLSTR; + } + + if (end < 0) { + end = str.length() + end; + } + if (start < 0) { + start = str.length() + start; + } + + if (end > str.length()) { + end = str.length(); + } + + if (start > end) { + return NULLSTR; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * + * @return 结果 + */ + public static boolean hasText (String str) { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText (CharSequence str) { + int strLen = str.length(); + for (int i = 0 ; i < strLen ; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * + * @return 格式化后的文本 + */ + public static String format (String template, Object... params) { + if (isEmpty(params) || isEmpty(template)) { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * + * @return 结果 + */ + public static boolean ishttp (String link) { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * + * @return boolean 结果 + */ + public static boolean containsAny (Collection collection, String... array) { + if (isEmpty(collection) || isEmpty(array)) { + return false; + } else { + for (String str : array) { + if (collection.contains(str)) { + return true; + } + } + return false; + } + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase (String str) { + if (str == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0 ; i < str.length() ; i++) { + char c = str.charAt(i); + if (i > 0) { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } else { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { + sb.append(SEPARATOR); + } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * + * @return 包含返回true + */ + public static boolean inStringIgnoreCase (String str, String... strs) { + if (str != null && strs != null) { + for (String s : strs) { + if (str.equalsIgnoreCase(trim(s))) { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase (String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase (String s) { + if (s == null) { + return null; + } + if (s.indexOf(SEPARATOR) == -1) { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0 ; i < s.length() ; i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * + * @return 是否匹配 + */ + public static boolean matches (String str, List strs) { + if (isEmpty(str) || isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * + * @return + */ + public static boolean isMatch (String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast (Object obj) { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl (final Number num, final int size) { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl (final String s, final int size, final char c) { + final StringBuilder sb = new StringBuilder(size); + if (s != null) { + final int len = s.length(); + if (s.length() <= size) { + for (int i = size - len ; i > 0 ; i--) { + sb.append(c); + } + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + for (int i = size ; i > 0 ; i--) { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java new file mode 100644 index 0000000..d44b351 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java @@ -0,0 +1,107 @@ +package com.muyu.common.core.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author muyu + */ +public class BeanUtils extends org.springframework.beans.BeanUtils { + /** + * Bean方法名中属性名开始的下标 + */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** + * 匹配getter方法的正则表达式 + */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** + * 匹配setter方法的正则表达式 + */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp (Object dest, Object src) { + try { + copyProperties(src, dest); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * + * @return 对象的setter方法列表 + */ + public static List getSetterMethods (Object obj) { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods (Object obj) { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals (String m1, String m2) { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java new file mode 100644 index 0000000..b904747 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.utils.bean; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; + +import java.util.Set; + +/** + * bean对象属性验证 + * + * @author muyu + */ +public class BeanValidators { + public static void validateWithException (Validator validator, Object object, Class... groups) + throws ConstraintViolationException { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..dde7e85 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java @@ -0,0 +1,85 @@ +package com.muyu.common.core.utils.file; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.Objects; + +/** + * 文件类型工具类 + * + * @author muyu + */ +public class FileTypeUtils { + /** + * 获取文件类型 + *

+ * 例如: muyu.txt, 返回: txt + * + * @param file 文件名 + * + * @return 后缀(不含".") + */ + public static String getFileType (File file) { + if (null == file) { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: muyu.txt, 返回: txt + * + * @param fileName 文件名 + * + * @return 后缀(不含".") + */ + public static String getFileType (String fileName) { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * + * @return 后缀名 + */ + public static final String getExtension (MultipartFile file) { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * + * @return 后缀(不含".") + */ + public static String getFileExtendName (byte[] photoByte) { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) { + strFileExtendName = "GIF"; + } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) { + strFileExtendName = "JPG"; + } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) { + strFileExtendName = "BMP"; + } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..5766365 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java @@ -0,0 +1,223 @@ +package com.muyu.common.core.utils.file; + +import com.muyu.common.core.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author muyu + */ +public class FileUtils { + /** + * 字符常量:斜杠 {@code '/'} + */ + public static final char SLASH = '/'; + + /** + * 字符常量:反斜杠 {@code '\\'} + */ + public static final char BACKSLASH = '\\'; + + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * + * @return + */ + public static void writeBytes (String filePath, OutputStream os) throws IOException { + FileInputStream fis = null; + try { + File file = new File(filePath); + if (!file.exists()) { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) { + os.write(b, 0, length); + } + } catch (IOException e) { + throw e; + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + if (fis != null) { + try { + fis.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + } + + /** + * 删除文件 + * + * @param filePath 文件 + * + * @return + */ + public static boolean deleteFile (String filePath) { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * + * @return true 正常 false 非法 + */ + public static boolean isValidFilename (String filename) { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload (String resource) { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) { + return false; + } + // 判断是否在允许下载的文件规则内 + return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader (HttpServletRequest request, String fileName) throws UnsupportedEncodingException { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } else if (agent.contains("Firefox")) { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } else if (agent.contains("Chrome")) { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } else { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 返回文件名 + * + * @param filePath 文件 + * + * @return 文件名 + */ + public static String getName (String filePath) { + if (null == filePath) { + return null; + } + int len = filePath.length(); + if (0 == len) { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1 ; i > -1 ; i--) { + c = filePath.charAt(i); + if (isFileSeparator(c)) { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator (char c) { + return SLASH == c || BACKSLASH == c; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * + * @return + */ + public static void setAttachmentResponseHeader (HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * + * @return 百分号编码后的字符串 + */ + public static String percentEncode (String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java new file mode 100644 index 0000000..7e23345 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.utils.file; + +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author muyu + */ +public class ImageUtils { + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage (String imagePath) { + InputStream is = getFile(imagePath); + try { + return IOUtils.toByteArray(is); + } catch (Exception e) { + log.error("图片加载异常 {}", e); + return null; + } finally { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile (String imagePath) { + try { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } catch (Exception e) { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * + * @return 字节数据 + */ + public static byte[] readFile (String url) { + InputStream in = null; + try { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + return IOUtils.toByteArray(in); + } catch (Exception e) { + log.error("访问文件异常 {}", e); + return null; + } finally { + IOUtils.closeQuietly(in); + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..9eb1d84 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author muyu + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf"}; + + public static String getExtension (String prefix) { + switch (prefix) { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java new file mode 100644 index 0000000..7dba9be --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java @@ -0,0 +1,145 @@ +package com.muyu.common.core.utils.html; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author muyu + */ +public class EscapeUtil { + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static { + for (int i = 0 ; i < 64 ; i++) { + TEXT[i] = new char[]{(char) i}; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * + * @return 转义后的文本 + */ + public static String escape (String text) { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * + * @return 转换后的字符串 + */ + public static String unescape (String content) { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * + * @return 清除标签后的文本 + */ + public static String clean (String content) { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * + * @return 编码后的字符 + */ + private static String encode (String text) { + if (StringUtils.isEmpty(text)) { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0 ; i < text.length() ; i++) { + c = text.charAt(i); + if (c < 256) { + tmp.append("%"); + if (c < 16) { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } else { + tmp.append("%u"); + if (c <= 0xfff) { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * + * @return 解码后的字符串 + */ + public static String decode (String content) { + if (StringUtils.isEmpty(content)) { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) { + if (content.charAt(pos + 1) == 'u') { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } else { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } else { + if (pos == -1) { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } else { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main (String[] args) { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java new file mode 100644 index 0000000..68221f3 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java @@ -0,0 +1,498 @@ +package com.muyu.common.core.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author muyu + */ +public final class HTMLFilter { + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter () { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[]{"img"}; + vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; + vDisallowed = new String[]{}; + vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp. + vProtocolAtts = new String[]{"src", "href"}; + vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; + vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter (final Map conf) { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr (final int decimal) { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars (final String s) { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + private static String regexReplace (final Pattern regex_pattern, final String replacement, final String s) { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + // --------------------------------------------------------------- + + private static boolean inArray (final String s, final String[] array) { + for (String item : array) { + if (item != null && item.equals(s)) { + return true; + } + } + return false; + } + + private void reset () { + vTagCounts.clear(); + } + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter (final String input) { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags () { + return alwaysMakeTags; + } + + public boolean isStripComments () { + return stripComment; + } + + private String escapeComments (final String s) { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML (String s) { + if (alwaysMakeTags) { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } else { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags (String s) { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) { + for (int ii = 0 ; ii < vTagCounts.get(key) ; ii++) { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks (final String s) { + String result = s; + for (String tag : vRemoveBlanks) { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private String processTag (final String s) { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) { + if (!inArray(name, vSelfClosingTags)) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0 ; ii < paramNames.size() ; ii++) { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) { + if (inArray(paramName, vProtocolAtts)) { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) { + ending = ""; + } + + if (ending == null || ending.length() < 1) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } else { + vTagCounts.put(name, 1); + } + } else { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } else { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol (String s) { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities (String s) { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities (final String s) { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes (final String s) { + if (encodeQuotes) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } else { + return s; + } + } + + private String checkEntity (final String preamble, final String term) { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity (final String entity) { + return inArray(entity, vAllowedEntities); + } + + private boolean allowed (final String name) { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute (final String name, final String paramName) { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java new file mode 100644 index 0000000..e750583 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java @@ -0,0 +1,331 @@ +package com.muyu.common.core.utils.ip; + +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; + +import jakarta.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author muyu + */ +public class IpUtils { + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr () { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * + * @return IP地址 + */ + public static String getIpAddr (HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * + * @return 结果 + */ + public static boolean internalIp (String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * + * @return 结果 + */ + private static boolean internalIp (byte[] addr) { + if (StringUtils.isNull(addr) || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + case SECTION_5: + switch (b1) { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4 (String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2 ; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4 ; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp () { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName () { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp (String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (false == isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * + * @return 是否未知 + */ + public static boolean isUnknown (String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP (String ip) { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard (String ip) { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck (String ipWildCard, String ip) { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0 ; i < s1.length && !s1[i].equals("*") ; i++) { + if (!s1[i].equals(s2[i])) { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment (String ipSeg) { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck (String iparea, String ip) { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0 ; i < 4 ; ++i) { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * + * @return boolean 结果 + */ + public static boolean isMatchedIp (String filter, String ip) { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) { + if (isIP(iStr) && iStr.equals(ip)) { + return true; + } else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) { + return true; + } else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) { + return true; + } + } + return false; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..e2041fc --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,23 @@ +package com.muyu.common.core.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author muyu + */ +public interface ExcelHandlerAdapter { + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format (Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..16f2c39 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java @@ -0,0 +1,1230 @@ +package com.muyu.common.core.utils.poi; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.annotation.Excel.Type; +import com.muyu.common.core.annotation.Excels; +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.ImageUtils; +import com.muyu.common.core.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author muyu + */ +public class ExcelUtil { + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + public static final String[] FORMULA_STR = {"=", "-", "+", "@"}; + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + /** + * 实体对象 + */ + public Class clazz; + /** + * 需要排除列属性 + */ + public String[] excludeFields; + /** + * 工作表名称 + */ + private String sheetName; + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + /** + * 工作薄对象 + */ + private Workbook wb; + /** + * 工作表对象 + */ + private Sheet sheet; + /** + * 样式列表 + */ + private Map styles; + /** + * 导入导出数据列表 + */ + private List list; + /** + * 注解列表 + */ + private List fields; + /** + * 当前行号 + */ + private int rownum; + /** + * 标题 + */ + private String title; + /** + * 最大高度 + */ + private short maxHeight; + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + /** + * 对象的子列表方法 + */ + private Method subMethod; + /** + * 对象的子列表属性 + */ + private List subFields; + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + public ExcelUtil (Class clazz) { + this.clazz = clazz; + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch (Sheet sheet) { + if (sheet.getDrawingPatriarch() == null) { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * + * @return 解析后值 + */ + public static String convertByExp (String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * + * @return 解析后值 + */ + public static String reverseByExp (String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * + * @throws Exception + */ + public void hideColumn (String... fields) { + this.excludeFields = fields; + } + + public void init (List list, String sheetName, String title, Type type) { + if (list == null) { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle () { + if (StringUtils.isNotEmpty(title)) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead () { + if (isSubList()) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * + * @return 转换后集合 + */ + public List importExcel (InputStream is) { + List list = null; + try { + list = importExcel(is, 0); + } catch (Exception e) { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } finally { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * + * @return 转换后集合 + */ + public List importExcel (InputStream is, int titleNum) throws Exception { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * + * @return 转换后集合 + */ + public List importExcel (String sheetName, InputStream is, int titleNum) throws Exception { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) { + throw new IOException("文件sheet不存在"); + } + + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0 ; i < heard.getPhysicalNumberOfCells() ; i++) { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } else { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1 ; i <= rows ; i++) { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) { + val = parseDateToStr(dateFormat, val); + } else { + val = Convert.toStr(val); + } + } + } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toInt(val); + } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toLong(val); + } else if (Double.TYPE == fieldType || Double.class == fieldType) { + val = Convert.toDouble(val); + } else if (Float.TYPE == fieldType || Float.class == fieldType) { + val = Convert.toFloat(val); + } else if (BigDecimal.class == fieldType) { + val = Convert.toBigDecimal(val); + } else if (Date.class == fieldType) { + if (val instanceof String) { + val = DateUtils.parseDate(val); + } else if (val instanceof Double) { + val = DateUtil.getJavaDate((Double) val); + } + } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + val = dataFormatHandlerAdapter(val, attr, null); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response, List list, String sheetName) { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response, List list, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * + * @return 结果 + */ + public void importTemplateExcel (HttpServletResponse response, String sheetName) { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * + * @return 结果 + */ + public void importTemplateExcel (HttpServletResponse response, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response) { + try { + writeSheet(); + wb.write(response.getOutputStream()); + } catch (Exception e) { + log.error("导出Excel异常{}", e.getMessage()); + } finally { + IOUtils.closeQuietly(wb); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet () { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0 ; index < sheetNo ; index++) { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) { + for (Field subField : subFields) { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } else { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData (int index, Row row) { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo ; i < endNo ; i++) { + rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) { + if (isSubListValue(vo)) { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } else { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) { + boolean subFirst = false; + for (Object obj : subList) { + if (subFirst) { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) { + if (subField.isAnnotationPresent(Excel.class)) { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } else { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * + * @return 样式列表 + */ + private Map createStyles (Workbook wb) { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles (Workbook wb, Map styles) { + Map headerStyles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * + * @return 自定义样式列表 + */ + private Map annotationDataStyles (Workbook wb) { + Map styles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell (Excel attr, Row row, int column) { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo (Object value, Excel attr, Cell cell) { + if (ColumnType.STRING == attr.cellType()) { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } else if (ColumnType.NUMERIC == attr.cellType()) { + if (StringUtils.isNotNull(value)) { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } else if (ColumnType.IMAGE == attr.cellType()) { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType (byte[] value) { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_JPEG; + } else if ("PNG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation (Excel attr, Row row, int column) { + if (attr.name().indexOf("注:") >= 0) { + sheet.setColumnWidth(column, 6000); + } else { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) { + if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } else { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell (Excel attr, Row row, T vo, Field field, int column) { + Cell cell = null; + try { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } else if (value instanceof BigDecimal && -1 != attr.scale()) { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } else { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } catch (Exception e) { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation (Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden (Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0 ; i < textlist.length ; i++) { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * + * @return + */ + public String dataFormatHandlerAdapter (Object value, Excel excel, Cell cell) { + try { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[]{Object.class, String[].class, Cell.class, Workbook.class}); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } catch (Exception e) { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData (Integer index, String text, Excel entity) { + if (entity != null && entity.isStatistics()) { + Double temp = 0D; + if (!statistics.containsKey(index)) { + statistics.put(index, temp); + } + try { + temp = Double.valueOf(text); + } catch (NumberFormatException e) { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow () { + if (statistics.size() > 0) { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * + * @return 最终的属性值 + * + * @throws Exception + */ + private Object getTargetValue (T vo, Field field, Excel excel) throws Exception { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) { + String target = excel.targetAttr(); + if (target.contains(".")) { + String[] targets = target.split("[.]"); + for (String name : targets) { + o = getValue(o, name); + } + } else { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * + * @return value + * + * @throws Exception + */ + private Object getValue (Object o, String name) throws Exception { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField () { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields () { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + if (Collection.class.isAssignableFrom(field.getType())) { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight () { + double maxHeight = 0; + for (Object[] os : this.fields) { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook () { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet (int sheetNo, int index) { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * + * @return 单元格值 + */ + public Object getCellValue (Row row, int column) { + if (row == null) { + return row; + } + Object val = ""; + try { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } else { + if ((Double) val % 1 != 0) { + val = new BigDecimal(val.toString()); + } else { + val = new DecimalFormat("0").format(val); + } + } + } else if (cell.getCellType() == CellType.STRING) { + val = cell.getStringCellValue(); + } else if (cell.getCellType() == CellType.BOOLEAN) { + val = cell.getBooleanCellValue(); + } else if (cell.getCellType() == CellType.ERROR) { + val = cell.getErrorCellValue(); + } + + } + } catch (Exception e) { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * + * @return + */ + private boolean isRowEmpty (Row row) { + if (row == null) { + return true; + } + for (int i = row.getFirstCellNum() ; i < row.getLastCellNum() ; i++) { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) { + return false; + } + } + return true; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * + * @return 格式化后的日期字符 + */ + public String parseDateToStr (String dateFormat, Object val) { + if (val == null) { + return ""; + } + String str; + if (val instanceof Date) { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } else if (val instanceof LocalDateTime) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } else if (val instanceof LocalDate) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } else { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList () { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue (T vo) { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue (Object obj) { + Object value; + try { + value = subMethod.invoke(obj, new Object[]{}); + } catch (Exception e) { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * + * @return 子列表方法 + */ + public Method getSubMethod (String name, Class pojoClass) { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try { + method = pojoClass.getMethod(getMethodName.toString(), new Class[]{}); + } catch (Exception e) { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..2ec7e4f --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,324 @@ +package com.muyu.common.core.utils.reflect; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.util.Date; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author muyu + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils { + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter (Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter (Object obj, String propertyName, E value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0 ; i < names.length ; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[]{value}); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue (final Object obj, final String fieldName) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try { + result = (E) field.get(obj); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue (final Object obj, final String fieldName, final E value) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod (final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) { + if (obj == null || methodName == null) { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName (final Object obj, final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0 ; i < cs.length ; i++) { + if (args[i] != null && !args[i].getClass().equals(cs[i])) { + if (cs[i] == String.class) { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } else if (cs[i] == Integer.class) { + args[i] = Convert.toInt(args[i]); + } else if (cs[i] == Long.class) { + args[i] = Convert.toLong(args[i]); + } else if (cs[i] == Double.class) { + args[i] = Convert.toDouble(args[i]); + } else if (cs[i] == Float.class) { + args[i] = Convert.toFloat(args[i]); + } else if (cs[i] == Date.class) { + if (args[i] instanceof String) { + args[i] = DateUtils.parseDate(args[i]); + } else { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } else if (cs[i] == boolean.class || cs[i] == Boolean.class) { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField (final Object obj, final String fieldName) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass() ; superClass != Object.class ; superClass = superClass.getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } catch (NoSuchFieldException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod (final Object obj, final String methodName, + final Class... parameterTypes) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName (final Object obj, final String methodName, int argsNum) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible (Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible (Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType (final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType (final Class clazz, final int index) { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass (Object instance) { + if (instance == null) { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked (String msg, Exception e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(msg, e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java new file mode 100644 index 0000000..038933f --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java @@ -0,0 +1,256 @@ +package com.muyu.common.core.utils.sign; + +/** + * Base64工具类 + * + * @author muyu + */ +public final class Base64 { + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static { + for (int i = 0 ; i < BASELENGTH ; ++i) { + base64Alphabet[i] = -1; + } + for (int i = 'Z' ; i >= 'A' ; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z' ; i >= 'a' ; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9' ; i >= '0' ; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0 ; i <= 25 ; i++) { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0 ; i <= 51 ; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0 ; i <= 61 ; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace (char octect) { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad (char octect) { + return (octect == PAD); + } + + private static boolean isData (char octect) { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * + * @return Encoded Base64 array + */ + public static String encode (byte[] binaryData) { + if (binaryData == null) { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0 ; i < numberTriplets ; i++) { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } else if (fewerThan24bits == SIXTEENBIT) { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * + * @return Array containind decoded data. + */ + public static byte[] decode (String encoded) { + if (encoded == null) { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for ( ; i < numberQuadruple - 1 ; i++) { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } else if (!isPad(d3) && isPad(d4)) { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } else { + return null; + } + } else { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * + * @return the new length + */ + private static int removeWhiteSpace (char[] data) { + if (data == null) { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0 ; i < len ; i++) { + if (!isWhiteSpace(data[i])) { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..3f418e7 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,59 @@ +package com.muyu.common.core.utils.sql; + +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author muyu + */ +public class SqlUtil { + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql (String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql (String value) { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword (String value) { + if (StringUtils.isEmpty(value)) { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java new file mode 100644 index 0000000..375b034 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java @@ -0,0 +1,44 @@ +package com.muyu.common.core.utils.uuid; + +/** + * ID生成器工具类 + * + * @author muyu + */ +public class IdUtils { + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID () { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID () { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID () { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID () { + return UUID.fastUUID().toString(true); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java new file mode 100644 index 0000000..b72e3dd --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java @@ -0,0 +1,78 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author muyu 序列生成类 + */ +public class Seq { + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + // 机器标识 + private static final String machineCode = "A"; + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId () { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId (String type) { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * + * @return 序列值 + */ + public static String getId (AtomicInteger atomicInt, int length) { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq (AtomicInteger atomicInt, int length) { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java new file mode 100644 index 0000000..37c3c9e --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java @@ -0,0 +1,450 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.exception.UtilException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author muyu + */ +public final class UUID implements java.io.Serializable, Comparable { + private static final long serialVersionUID = -1185015143654744140L; + /** + * 此UUID的最高64有效位 + */ + private final long mostSigBits; + /** + * 此UUID的最低64有效位 + */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID (byte[] data) { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0 ; i < 8 ; i++) { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8 ; i < 16 ; i++) { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID (long mostSigBits, long leastSigBits) { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID () { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID () { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID (boolean isSecure) { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes (byte[] name) { + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * + * @return 具有指定值的 {@code UUID} + * + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + */ + public static UUID fromString (String name) { + String[] components = name.split("-"); + if (components.length != 5) { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0 ; i < 5 ; i++) { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * + * @return 值 + */ + private static String digits (long val, int digits) { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom () { + try { + return SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom () { + return ThreadLocalRandom.current(); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits () { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits () { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version () { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant () { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp () throws UnsupportedOperationException { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence () throws UnsupportedOperationException { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node () throws UnsupportedOperationException { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * + * @see #toString(boolean) + */ + @Override + public String toString () { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString (boolean isSimple) { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (false == isSimple) { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (false == isSimple) { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (false == isSimple) { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (false == isSimple) { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + // Comparison Operations + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode () { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals (Object obj) { + if ((null == obj) || (obj.getClass() != UUID.class)) { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + */ + @Override + public int compareTo (UUID val) { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase () { + if (version() != 1) { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * SecureRandom 的单例 + */ + private static class Holder { + static final SecureRandom numberGenerator = getSecureRandom(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java new file mode 100644 index 0000000..0540064 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.validation; + +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ValidationConfig { + @Bean + public Validator validator() { + return Validation.byDefaultProvider().configure().buildValidatorFactory().getValidator(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java new file mode 100644 index 0000000..3789a82 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java @@ -0,0 +1,129 @@ +package com.muyu.common.core.web.controller; + +import com.github.pagehelper.PageInfo; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.PageUtils; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +/** + * web层通用数据处理 + * + * @author muyu + */ +public class BaseController { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder (WebDataBinder binder) { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText (String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage () { + PageUtils.startPage(); + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage () { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected Result> getDataTable (List list) { + return Result.success( + TableDataInfo.builder() + .total(new PageInfo(list).getTotal()) + .rows(list) + .build() + ); + } + + /** + * 返回成功 + */ + public Result success () { + return Result.success(); + } + + /** + * 返回成功消息 + */ + public Result success (String message) { + return Result.success(message); + } + + /** + * 返回成功消息 + */ + public Result success (Object data) { + return Result.success(data); + } + + /** + * 返回失败消息 + */ + public Result error () { + return Result.error(); + } + + /** + * 返回失败消息 + */ + public Result error (String message) { + return Result.error(message); + } + + /** + * 返回警告消息 + */ + public Result warn (String message) { + return Result.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * + * @return 操作结果 + */ + protected Result toAjax (int rows) { + return rows > 0 ? Result.success() : Result.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * + * @return 操作结果 + */ + protected Result toAjax (boolean result) { + return result ? success() : error(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java new file mode 100644 index 0000000..add8e3e --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java @@ -0,0 +1,80 @@ +package com.muyu.common.core.web.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params; + + public Map getParams () { + if (params == null) { + params = new HashMap<>(); + } + return params; + } + + public void setParams (Map params) { + this.params = params; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java new file mode 100644 index 0000000..85ea8df --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java @@ -0,0 +1,47 @@ +package com.muyu.common.core.web.domain; + +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TreeEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 父菜单名称 + */ + private String parentName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 子部门 + */ + private List children = new ArrayList<>(); + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java new file mode 100644 index 0000000..b9c5e45 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 分页数据 + * + * @author muyu + */ +public class PageDomain { + /** + * 当前记录起始索引 + */ + private Integer pageNum; + + /** + * 每页显示记录数 + */ + private Integer pageSize; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc = "asc"; + + /** + * 分页参数合理化 + */ + private Boolean reasonable = true; + + public String getOrderBy () { + if (StringUtils.isEmpty(orderByColumn)) { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum () { + return pageNum; + } + + public void setPageNum (Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize () { + return pageSize; + } + + public void setPageSize (Integer pageSize) { + this.pageSize = pageSize; + } + + public String getOrderByColumn () { + return orderByColumn; + } + + public void setOrderByColumn (String orderByColumn) { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc () { + return isAsc; + } + + public void setIsAsc (String isAsc) { + if (StringUtils.isNotEmpty(isAsc)) { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) { + isAsc = "asc"; + } else if ("descending".equals(isAsc)) { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable () { + if (StringUtils.isNull(reasonable)) { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable (Boolean reasonable) { + this.reasonable = reasonable; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java new file mode 100644 index 0000000..d677cce --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java @@ -0,0 +1,45 @@ +package com.muyu.common.core.web.page; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.poi.ss.formula.functions.T; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author muyu + */ + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java new file mode 100644 index 0000000..ce0328d --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java @@ -0,0 +1,53 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author muyu + */ +public class TableSupport { + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain () { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest () { + return getPageDomain(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java new file mode 100644 index 0000000..fa31755 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java @@ -0,0 +1,27 @@ +package com.muyu.common.core.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Constraint(validatedBy = {XssValidator.class}) +public @interface Xss { + String message () + + default "不允许任何脚本运行"; + + Class[] groups () default {}; + + Class[] payload () default {}; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java new file mode 100644 index 0000000..3a03eab --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java @@ -0,0 +1,31 @@ +package com.muyu.common.core.xss; + +import com.muyu.common.core.utils.StringUtils; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author muyu + */ +public class XssValidator implements ConstraintValidator { + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + public static boolean containsHtml (String value) { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } + + @Override + public boolean isValid (String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isBlank(value)) { + return true; + } + return !containsHtml(value); + } +} diff --git a/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..17f73ec --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.core.utils.SpringUtils +com.muyu.common.core.feign.FeginConfig diff --git a/cloud-common/cloud-common-datascope/pom.xml b/cloud-common/cloud-common-datascope/pom.xml new file mode 100644 index 0000000..a7681bb --- /dev/null +++ b/cloud-common/cloud-common-datascope/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-datascope + + + cloud-common-datascope权限范围 + + + + + + + com.muyu + cloud-common-security + + + + diff --git a/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java new file mode 100644 index 0000000..498f06b --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java @@ -0,0 +1,28 @@ +package com.muyu.common.datascope.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限过滤注解 + * + * @author muyu + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope { + /** + * 部门表的别名 + */ + public String deptAlias () default ""; + + /** + * 用户表的别名 + */ + public String userAlias () default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来 + */ + public String permission () default ""; +} diff --git a/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java new file mode 100644 index 0000000..3d66b0d --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java @@ -0,0 +1,149 @@ +package com.muyu.common.datascope.aspect; + +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据过滤处理 + * + * @author muyu + */ +@Aspect +@Component +public class DataScopeAspect { + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter (JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + + for (SysRole role : user.getRoles()) { + String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } else if (DATA_SCOPE_DEPT.equals(dataScope)) { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } else if (DATA_SCOPE_SELF.equals(dataScope)) { + if (StringUtils.isNotBlank(userAlias)) { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } else { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + @Before("@annotation(controllerDataScope)") + public void doBefore (JoinPoint point, DataScope controllerDataScope) throws Throwable { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope (final JoinPoint joinPoint, DataScope controllerDataScope) { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) { + SysUser currentUser = loginUser.getSysUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope (final JoinPoint joinPoint) { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..8f4967b --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.datascope.aspect.DataScopeAspect diff --git a/cloud-common/cloud-common-datasource/pom.xml b/cloud-common/cloud-common-datasource/pom.xml new file mode 100644 index 0000000..d1639d5 --- /dev/null +++ b/cloud-common/cloud-common-datasource/pom.xml @@ -0,0 +1,35 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-datasource + + + cloud-common-datasource多数据源 + + + + + + + com.alibaba + druid-spring-boot-3-starter + ${druid.version} + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-ds.version} + + + + diff --git a/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java new file mode 100644 index 0000000..d9aae00 --- /dev/null +++ b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java @@ -0,0 +1,18 @@ +package com.muyu.common.datasource.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 主库数据源 + * + * @author muyu + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("master") +public @interface Master { + +} diff --git a/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java new file mode 100644 index 0000000..9663cd4 --- /dev/null +++ b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java @@ -0,0 +1,18 @@ +package com.muyu.common.datasource.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 从库数据源 + * + * @author muyu + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("slave") +public @interface Slave { + +} diff --git a/cloud-common/cloud-common-log/pom.xml b/cloud-common/cloud-common-log/pom.xml new file mode 100644 index 0000000..27ab5a5 --- /dev/null +++ b/cloud-common/cloud-common-log/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-log + + + cloud-common-log日志记录 + + + + + + + com.muyu + cloud-common-security + + + + diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java new file mode 100644 index 0000000..ac6394e --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java @@ -0,0 +1,46 @@ +package com.muyu.common.log.annotation; + +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.log.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author muyu + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + public String title () default ""; + + /** + * 功能 + */ + public BusinessType businessType () default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType () default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData () default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData () default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames () default {}; +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java new file mode 100644 index 0000000..f82ffab --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java @@ -0,0 +1,220 @@ +package com.muyu.common.log.aspect; + +import com.alibaba.fastjson2.JSON; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessStatus; +import com.muyu.common.log.filter.PropertyPreExcludeFilter; +import com.muyu.common.log.service.AsyncLogService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysOperLog; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.NamedThreadLocal; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author muyu + */ +@Aspect +@Component +public class LogAspect { + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"}; + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + /** + * 计算操作消耗时间 + */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + @Autowired + private AsyncLogService asyncLogService; + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void boBefore (JoinPoint joinPoint, Log controllerLog) { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning (JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing (JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog (final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + String username = SecurityUtils.getUsername(); + if (StringUtils.isNotBlank(username)) { + operLog.setOperName(username); + } + + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + asyncLogService.saveSysLog(operLog); + } catch (Exception exp) { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } finally { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * + * @throws Exception + */ + public void getControllerMethodDescription (JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * + * @throws Exception 异常 + */ + private void setRequestValue (JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception { + String requestMethod = operLog.getRequestMethod(); + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + if (StringUtils.isEmpty(paramsMap) + && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } else { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString (Object[] paramsArray, String[] excludeParamNames) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj.toString() + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter (String[] excludeParamNames) { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject (final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java new file mode 100644 index 0000000..45e9713 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package com.muyu.common.log.enums; + +/** + * 操作状态 + * + * @author muyu + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java new file mode 100644 index 0000000..2e928c7 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java @@ -0,0 +1,58 @@ +package com.muyu.common.log.enums; + +/** + * 业务操作类型 + * + * @author muyu + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java new file mode 100644 index 0000000..645777f --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.muyu.common.log.enums; + +/** + * 操作人类别 + * + * @author muyu + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..2245256 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,20 @@ +package com.muyu.common.log.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author muyu + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter { + public PropertyPreExcludeFilter () { + } + + public PropertyPreExcludeFilter addExcludes (String... filters) { + for (int i = 0 ; i < filters.length ; i++) { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java new file mode 100644 index 0000000..46e1cdf --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java @@ -0,0 +1,27 @@ +package com.muyu.common.log.service; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysOperLog; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +/** + * 异步调用日志服务 + * + * @author muyu + */ +@Service +public class AsyncLogService { + @Autowired + private RemoteLogService remoteLogService; + + /** + * 保存系统日志记录 + */ + @Async + public void saveSysLog (SysOperLog sysOperLog) throws Exception { + remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER); + } +} diff --git a/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e46e773 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.log.service.AsyncLogService +com.muyu.common.log.aspect.LogAspect diff --git a/cloud-common/cloud-common-rabbit/pom.xml b/cloud-common/cloud-common-rabbit/pom.xml new file mode 100644 index 0000000..fa6d383 --- /dev/null +++ b/cloud-common/cloud-common-rabbit/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-rabbit + + + 17 + 17 + UTF-8 + + + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + + com.muyu + cloud-common-core + + + + \ No newline at end of file diff --git a/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java b/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java new file mode 100644 index 0000000..51cb359 --- /dev/null +++ b/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java @@ -0,0 +1,41 @@ +package com.muyu.common.rabbit; + +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; + +@Configuration +public class RabbitListenerConfigurer implements org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer { + + static { + System.setProperty("spring.amqp.deserialization.trust.all", "true"); + } + + //以下配置RabbitMQ消息服务 + @Autowired + public ConnectionFactory connectionFactory; + + + /** + * 处理器方法工厂 + * @return + */ + @Bean + public DefaultMessageHandlerMethodFactory handlerMethodFactory() { + DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory(); + // 这里的转换器设置实现了 通过 @Payload 注解 自动反序列化message body + factory.setMessageConverter(new MappingJackson2MessageConverter()); + return factory; + } + + @Override + public void configureRabbitListeners(RabbitListenerEndpointRegistrar rabbitListenerEndpointRegistrar) { + rabbitListenerEndpointRegistrar.setMessageHandlerMethodFactory(handlerMethodFactory()); + } + +} + diff --git a/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..189ea2c --- /dev/null +++ b/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.rabbit.RabbitListenerConfigurer \ No newline at end of file diff --git a/cloud-common/cloud-common-redis/pom.xml b/cloud-common/cloud-common-redis/pom.xml new file mode 100644 index 0000000..4a2d5ea --- /dev/null +++ b/cloud-common/cloud-common-redis/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-redis + + + cloud-common-redis缓存服务 + + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.muyu + cloud-common-core + + + + diff --git a/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..f575143 --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,49 @@ +package com.muyu.common.redis.configure; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.muyu.common.core.constant.Constants; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Redis使用FastJson序列化 + * + * @author muyu + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer (Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize (T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize (byte[] bytes) throws SerializationException { + if (bytes == null || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java new file mode 100644 index 0000000..ba8760e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java @@ -0,0 +1,41 @@ +package com.muyu.common.redis.configure; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author muyu + */ +@Configuration +@EnableCaching +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig extends CachingConfigurerSupport { + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate redisTemplate (RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java new file mode 100644 index 0000000..db90c1e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java @@ -0,0 +1,258 @@ +package com.muyu.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author muyu + **/ +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +@Component +public class RedisService { + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject (final String key, final T value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject (final String key, final T value, final Long timeout, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * + * @return true=设置成功;false=设置失败 + */ + public boolean expire (final String key, final long timeout) { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * + * @return true=设置成功;false=设置失败 + */ + public boolean expire (final String key, final long timeout, final TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * + * @return 有效时间 + */ + public long getExpire (final String key) { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * + * @return true 存在 false不存在 + */ + public Boolean hasKey (String key) { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * + * @return 缓存键值对应的数据 + */ + public T getCacheObject (final String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject (final String key) { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * + * @return + */ + public boolean deleteObject (final Collection collection) { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * + * @return 缓存的对象 + */ + public long setCacheList (final String key, final List dataList) { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * + * @return 缓存键值对应的数据 + */ + public List getCacheList (final String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet (final String key, final Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * + * @return + */ + public Set getCacheSet (final String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap (final String key, final Map dataMap) { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * + * @return + */ + public Map getCacheMap (final String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue (final String key, final String hKey, final T value) { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * + * @return Hash中的对象 + */ + public T getCacheMapValue (final String key, final String hKey) { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * + * @return Hash对象集合 + */ + public List getMultiCacheMapValue (final String key, final Collection hKeys) { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * + * @return 是否成功 + */ + public boolean deleteCacheMapValue (final String key, final String hKey) { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * + * @return 对象列表 + */ + public Collection keys (final String pattern) { + return redisTemplate.keys(pattern); + } +} diff --git a/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..27b030e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.redis.configure.RedisConfig +com.muyu.common.redis.service.RedisService diff --git a/cloud-common/cloud-common-seata/pom.xml b/cloud-common/cloud-common-seata/pom.xml new file mode 100644 index 0000000..3725683 --- /dev/null +++ b/cloud-common/cloud-common-seata/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-seata + + + cloud-common-seata分布式事务 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-seata + + + + diff --git a/cloud-common/cloud-common-security/pom.xml b/cloud-common/cloud-common-security/pom.xml new file mode 100644 index 0000000..f1a480a --- /dev/null +++ b/cloud-common/cloud-common-security/pom.xml @@ -0,0 +1,39 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-security + + + cloud-common-security安全模块 + + + + + + + org.springframework + spring-webmvc + + + + + com.muyu + cloud-common-redis + + + + + com.muyu + cloud-common-system + + + + + diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java new file mode 100644 index 0000000..ca7a07a --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java @@ -0,0 +1,26 @@ +package com.muyu.common.security.annotation; + +import com.muyu.common.security.config.ApplicationConfig; +import com.muyu.common.security.feign.FeignAutoConfiguration; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.muyu.**.mapper") +// 开启线程异步执行 +@EnableAsync +// 自动加载类 +@Import({ApplicationConfig.class, FeignAutoConfiguration.class}) +public @interface EnableCustomConfig { + +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java new file mode 100644 index 0000000..7a59fa4 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java @@ -0,0 +1,27 @@ +package com.muyu.common.security.annotation; + +import org.springframework.cloud.openfeign.EnableFeignClients; + +import java.lang.annotation.*; + +/** + * 自定义feign注解 + * 添加basePackages路径 + * + * @author muyu + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@EnableFeignClients +public @interface EnableMyFeignClients { + String[] value () default {}; + + String[] basePackages () default {"com.muyu"}; + + Class[] basePackageClasses () default {}; + + Class[] defaultConfiguration () default {}; + + Class[] clients () default {}; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java new file mode 100644 index 0000000..092a573 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.*; + +/** + * 内部认证注解 + * + * @author muyu + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface InnerAuth { + /** + * 是否校验用户信息 + */ + boolean isUser () default false; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java new file mode 100644 index 0000000..0be306a --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.annotation; + +/** + * 权限注解的验证模式 + * + * @author muyu + */ +public enum Logical { + /** + * 必须具有所有的元素 + */ + AND, + + /** + * 只需具有其中一个元素 + */ + OR +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java new file mode 100644 index 0000000..4eff911 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java @@ -0,0 +1,16 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 登录认证:只有登录之后才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresLogin { +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java new file mode 100644 index 0000000..8d95bb4 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java @@ -0,0 +1,25 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 权限认证:必须具有指定权限才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresPermissions { + /** + * 需要校验的权限码 + */ + String[] value () default {}; + + /** + * 验证模式:AND | OR,默认AND + */ + Logical logical () default Logical.AND; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java new file mode 100644 index 0000000..78911cc --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java @@ -0,0 +1,25 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 角色认证:必须具有指定角色标识才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresRoles { + /** + * 需要校验的角色标识 + */ + String[] value () default {}; + + /** + * 验证逻辑:AND | OR,默认AND + */ + Logical logical () default Logical.AND; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java new file mode 100644 index 0000000..1707742 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java @@ -0,0 +1,46 @@ +package com.muyu.common.security.aspect; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.exception.InnerAuthException; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.annotation.InnerAuth; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +/** + * 内部服务调用验证处理 + * + * @author muyu + */ +@Aspect +@Component +public class InnerAuthAspect implements Ordered { + @Around("@annotation(innerAuth)") + public Object innerAround (ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable { + String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE); + // 内部请求验证 + if (!StringUtils.equals(SecurityConstants.INNER, source)) { + throw new InnerAuthException("没有内部访问权限,不允许访问"); + } + + String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID); + String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME); + // 用户信息验证 + if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))) { + throw new InnerAuthException("没有设置用户信息,不允许访问 "); + } + return point.proceed(); + } + + /** + * 确保在权限认证aop执行前执行 + */ + @Override + public int getOrder () { + return Ordered.HIGHEST_PRECEDENCE + 1; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java new file mode 100644 index 0000000..4cdd933 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java @@ -0,0 +1,89 @@ +package com.muyu.common.security.aspect; + +import com.muyu.common.security.annotation.RequiresLogin; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.security.auth.AuthUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 基于 Spring Aop 的注解鉴权 + * + * @author kong + */ +@Aspect +@Component +public class PreAuthorizeAspect { + /** + * 定义AOP签名 (切入所有使用鉴权注解的方法) + */ + public static final String POINTCUT_SIGN = " @annotation(com.muyu.common.security.annotation.RequiresLogin) || " + + "@annotation(com.muyu.common.security.annotation.RequiresPermissions) || " + + "@annotation(com.muyu.common.security.annotation.RequiresRoles)"; + + /** + * 构建 + */ + public PreAuthorizeAspect () { + } + + /** + * 声明AOP签名 + */ + @Pointcut(POINTCUT_SIGN) + public void pointcut () { + } + + /** + * 环绕切入 + * + * @param joinPoint 切面对象 + * + * @return 底层方法执行后的返回值 + * + * @throws Throwable 底层方法抛出的异常 + */ + @Around("pointcut()") + public Object around (ProceedingJoinPoint joinPoint) throws Throwable { + // 注解鉴权 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + checkMethodAnnotation(signature.getMethod()); + try { + // 执行原有逻辑 + Object obj = joinPoint.proceed(); + return obj; + } catch (Throwable e) { + throw e; + } + } + + /** + * 对一个Method对象进行注解检查 + */ + public void checkMethodAnnotation (Method method) { + // 校验 @RequiresLogin 注解 + RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class); + if (requiresLogin != null) { + AuthUtil.checkLogin(); + } + + // 校验 @RequiresRoles 注解 + RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class); + if (requiresRoles != null) { + AuthUtil.checkRole(requiresRoles); + } + + // 校验 @RequiresPermissions 注解 + RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class); + if (requiresPermissions != null) { + AuthUtil.checkPermi(requiresPermissions); + } + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java new file mode 100644 index 0000000..beb3426 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java @@ -0,0 +1,327 @@ +package com.muyu.common.security.auth; + +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.exception.auth.NotLoginException; +import com.muyu.common.core.exception.auth.NotPermissionException; +import com.muyu.common.core.exception.auth.NotRoleException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.annotation.Logical; +import com.muyu.common.security.annotation.RequiresLogin; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.util.PatternMatchUtils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Token 权限验证,逻辑实现类 + * + * @author muyu + */ +public class AuthLogic { + /** + * 所有权限标识 + */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + private static final String SUPER_ADMIN = "admin"; + + public TokenService tokenService = SpringUtils.getBean(TokenService.class); + + /** + * 会话注销 + */ + public void logout () { + String token = SecurityUtils.getToken(); + if (token == null) { + return; + } + logoutByToken(token); + } + + /** + * 会话注销,根据指定Token + */ + public void logoutByToken (String token) { + tokenService.delLoginUser(token); + } + + /** + * 检验用户是否已经登录,如未登录,则抛出异常 + */ + public void checkLogin () { + getLoginUser(); + } + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @return 用户缓存信息 + */ + public LoginUser getLoginUser () { + String token = SecurityUtils.getToken(); + if (token == null) { + throw new NotLoginException("未提供token"); + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser == null) { + throw new NotLoginException("无效的token"); + } + return loginUser; + } + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @param token 前端传递的认证信息 + * + * @return 用户缓存信息 + */ + public LoginUser getLoginUser (String token) { + return tokenService.getLoginUser(token); + } + + /** + * 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存 + * + * @param loginUser 当前用户信息 + */ + public void verifyLoginUserExpire (LoginUser loginUser) { + tokenService.verifyToken(loginUser); + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public boolean hasPermi (String permission) { + return hasPermi(getPermiList(), permission); + } + + /** + * 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public void checkPermi (String permission) { + if (!hasPermi(getPermiList(), permission)) { + throw new NotPermissionException(permission); + } + } + + /** + * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 注解对象 + */ + public void checkPermi (RequiresPermissions requiresPermissions) { + SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ",")); + if (requiresPermissions.logical() == Logical.AND) { + checkPermiAnd(requiresPermissions.value()); + } else { + checkPermiOr(requiresPermissions.value()); + } + } + + /** + * 验证用户是否含有指定权限,必须全部拥有 + * + * @param permissions 权限列表 + */ + public void checkPermiAnd (String... permissions) { + Set permissionList = getPermiList(); + for (String permission : permissions) { + if (!hasPermi(permissionList, permission)) { + throw new NotPermissionException(permission); + } + } + } + + /** + * 验证用户是否含有指定权限,只需包含其中一个 + * + * @param permissions 权限码数组 + */ + public void checkPermiOr (String... permissions) { + Set permissionList = getPermiList(); + for (String permission : permissions) { + if (hasPermi(permissionList, permission)) { + return; + } + } + if (permissions.length > 0) { + throw new NotPermissionException(permissions); + } + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色标识 + * + * @return 用户是否具备某角色 + */ + public boolean hasRole (String role) { + return hasRole(getRoleList(), role); + } + + /** + * 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException + * + * @param role 角色标识 + */ + public void checkRole (String role) { + if (!hasRole(role)) { + throw new NotRoleException(role); + } + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param requiresRoles 注解对象 + */ + public void checkRole (RequiresRoles requiresRoles) { + if (requiresRoles.logical() == Logical.AND) { + checkRoleAnd(requiresRoles.value()); + } else { + checkRoleOr(requiresRoles.value()); + } + } + + /** + * 验证用户是否含有指定角色,必须全部拥有 + * + * @param roles 角色标识数组 + */ + public void checkRoleAnd (String... roles) { + Set roleList = getRoleList(); + for (String role : roles) { + if (!hasRole(roleList, role)) { + throw new NotRoleException(role); + } + } + } + + /** + * 验证用户是否含有指定角色,只需包含其中一个 + * + * @param roles 角色标识数组 + */ + public void checkRoleOr (String... roles) { + Set roleList = getRoleList(); + for (String role : roles) { + if (hasRole(roleList, role)) { + return; + } + } + if (roles.length > 0) { + throw new NotRoleException(roles); + } + } + + /** + * 根据注解(@RequiresLogin)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresLogin at) { + this.checkLogin(); + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresRoles at) { + String[] roleArray = at.value(); + if (at.logical() == Logical.AND) { + this.checkRoleAnd(roleArray); + } else { + this.checkRoleOr(roleArray); + } + } + + /** + * 根据注解(@RequiresPermissions)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresPermissions at) { + String[] permissionArray = at.value(); + if (at.logical() == Logical.AND) { + this.checkPermiAnd(permissionArray); + } else { + this.checkPermiOr(permissionArray); + } + } + + /** + * 获取当前账号的角色列表 + * + * @return 角色列表 + */ + public Set getRoleList () { + try { + LoginUser loginUser = getLoginUser(); + return loginUser.getRoles(); + } catch (Exception e) { + return new HashSet<>(); + } + } + + /** + * 获取当前账号的权限列表 + * + * @return 权限列表 + */ + public Set getPermiList () { + try { + LoginUser loginUser = getLoginUser(); + return loginUser.getPermissions(); + } catch (Exception e) { + return new HashSet<>(); + } + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public boolean hasPermi (Collection authorities, String permission) { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * + * @return 用户是否具备某角色权限 + */ + public boolean hasRole (Collection roles, String role) { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java new file mode 100644 index 0000000..131d150 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java @@ -0,0 +1,154 @@ +package com.muyu.common.security.auth; + +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.system.domain.LoginUser; + +/** + * Token 权限验证工具类 + * + * @author muyu + */ +public class AuthUtil { + /** + * 底层的 AuthLogic 对象 + */ + public static AuthLogic authLogic = new AuthLogic(); + + /** + * 会话注销 + */ + public static void logout () { + authLogic.logout(); + } + + /** + * 会话注销,根据指定Token + * + * @param token 指定token + */ + public static void logoutByToken (String token) { + authLogic.logoutByToken(token); + } + + /** + * 检验当前会话是否已经登录,如未登录,则抛出异常 + */ + public static void checkLogin () { + authLogic.checkLogin(); + } + + /** + * 获取当前登录用户信息 + * + * @param token 指定token + * + * @return 用户信息 + */ + public static LoginUser getLoginUser (String token) { + return authLogic.getLoginUser(token); + } + + /** + * 验证当前用户有效期 + * + * @param loginUser 用户信息 + */ + public static void verifyLoginUserExpire (LoginUser loginUser) { + authLogic.verifyLoginUserExpire(loginUser); + } + + /** + * 当前账号是否含有指定角色标识, 返回true或false + * + * @param role 角色标识 + * + * @return 是否含有指定角色标识 + */ + public static boolean hasRole (String role) { + return authLogic.hasRole(role); + } + + /** + * 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException + * + * @param role 角色标识 + */ + public static void checkRole (String role) { + authLogic.checkRole(role); + } + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException + * + * @param requiresRoles 角色权限注解 + */ + public static void checkRole (RequiresRoles requiresRoles) { + authLogic.checkRole(requiresRoles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] + * + * @param roles 角色标识数组 + */ + public static void checkRoleAnd (String... roles) { + authLogic.checkRoleAnd(roles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] + * + * @param roles 角色标识数组 + */ + public static void checkRoleOr (String... roles) { + authLogic.checkRoleOr(roles); + } + + /** + * 当前账号是否含有指定权限, 返回true或false + * + * @param permission 权限码 + * + * @return 是否含有指定权限 + */ + public static boolean hasPermi (String permission) { + return authLogic.hasPermi(permission); + } + + /** + * 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限码 + */ + public static void checkPermi (String permission) { + authLogic.checkPermi(permission); + } + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 权限注解 + */ + public static void checkPermi (RequiresPermissions requiresPermissions) { + authLogic.checkPermi(requiresPermissions); + } + + /** + * 当前账号是否含有指定权限 [指定多个,必须全部验证通过] + * + * @param permissions 权限码数组 + */ + public static void checkPermiAnd (String... permissions) { + authLogic.checkPermiAnd(permissions); + } + + /** + * 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] + * + * @param permissions 权限码数组 + */ + public static void checkPermiOr (String... permissions) { + authLogic.checkPermiOr(permissions); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java new file mode 100644 index 0000000..b78abbf --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java @@ -0,0 +1,21 @@ +package com.muyu.common.security.config; + +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; + +import java.util.TimeZone; + +/** + * 系统配置 + * + * @author muyu + */ +public class ApplicationConfig { + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization () { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java new file mode 100644 index 0000000..8acde35 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java @@ -0,0 +1,32 @@ +package com.muyu.common.security.config; + +import com.muyu.common.security.interceptor.HeaderInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 拦截器配置 + * + * @author muyu + */ +public class WebMvcConfig implements WebMvcConfigurer { + /** + * 不需要拦截地址 + */ + public static final String[] excludeUrls = {"/login", "/logout", "/refresh"}; + + @Override + public void addInterceptors (InterceptorRegistry registry) { + registry.addInterceptor(getHeaderInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns(excludeUrls) + .order(-10); + } + + /** + * 自定义请求头拦截器 + */ + public HeaderInterceptor getHeaderInterceptor () { + return new HeaderInterceptor(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java new file mode 100644 index 0000000..4bfda6d --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.feign; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Feign 配置注册 + * + * @author muyu + **/ +@Configuration +public class FeignAutoConfiguration { + @Bean + public RequestInterceptor requestInterceptor () { + return new FeignRequestInterceptor(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java new file mode 100644 index 0000000..8ae21da --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java @@ -0,0 +1,48 @@ +package com.muyu.common.security.feign; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * feign 请求拦截器 + * + * @author muyu + */ +@Component +public class FeignRequestInterceptor implements RequestInterceptor { + @Override + public void apply (RequestTemplate requestTemplate) { + HttpServletRequest httpServletRequest = ServletUtils.getRequest(); + if (StringUtils.isNotNull(httpServletRequest)) { + Map headers = ServletUtils.getHeaders(httpServletRequest); + // 传递用户信息请求头,防止丢失 + String userId = headers.get(SecurityConstants.DETAILS_USER_ID); + if (StringUtils.isNotEmpty(userId)) { + requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId); + } + String userKey = headers.get(SecurityConstants.USER_KEY); + if (StringUtils.isNotEmpty(userKey)) { + requestTemplate.header(SecurityConstants.USER_KEY, userKey); + } + String userName = headers.get(SecurityConstants.DETAILS_USERNAME); + if (StringUtils.isNotEmpty(userName)) { + requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName); + } + String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER); + if (StringUtils.isNotEmpty(authentication)) { + requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication); + } + + // 配置客户端IP + requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr()); + } + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..170211c --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java @@ -0,0 +1,147 @@ +package com.muyu.common.security.handler; + +import com.muyu.common.core.constant.HttpStatus; +import com.muyu.common.core.exception.DemoModeException; +import com.muyu.common.core.exception.InnerAuthException; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.exception.auth.NotPermissionException; +import com.muyu.common.core.exception.auth.NotRoleException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.domain.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 全局异常处理器 + * + * @author muyu + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限码异常 + */ + @ExceptionHandler(NotPermissionException.class) + public Result handleNotPermissionException (NotPermissionException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); + return Result.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 角色权限异常 + */ + @ExceptionHandler(NotRoleException.class) + public Result handleNotRoleException (NotRoleException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + return Result.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Result handleHttpRequestMethodNotSupported (HttpRequestMethodNotSupportedException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return Result.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public Result handleServiceException (ServiceException e, HttpServletRequest request) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? Result.error(code, e.getMessage()) : Result.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public Result handleMissingPathVariableException (MissingPathVariableException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return Result.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public Result handleMethodArgumentTypeMismatchException (MethodArgumentTypeMismatchException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return Result.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public Result handleRuntimeException (RuntimeException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return Result.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public Result handleException (Exception e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return Result.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public Result handleBindException (BindException e) { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return Result.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException (MethodArgumentNotValidException e) { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return Result.error(message); + } + + /** + * 内部认证异常 + */ + @ExceptionHandler(InnerAuthException.class) + public Result handleInnerAuthException (InnerAuthException e) { + return Result.error(e.getMessage()); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public Result handleDemoModeException (DemoModeException e) { + return Result.error("演示模式,不允许操作"); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java new file mode 100644 index 0000000..0b1938d --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java @@ -0,0 +1,50 @@ +package com.muyu.common.security.interceptor; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.auth.AuthUtil; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * 自定义请求头拦截器,将Header数据封装到线程变量中方便获取 + * 注意:此拦截器会同时验证当前用户有效期自动刷新有效期 + * + * @author muyu + */ +public class HeaderInterceptor implements AsyncHandlerInterceptor { + + @Override + public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)) { + return true; + } + + SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID)); + SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME)); + SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY)); + + String token = SecurityUtils.getToken(); + if (StringUtils.isNotEmpty(token)) { + LoginUser loginUser = AuthUtil.getLoginUser(token); + if (StringUtils.isNotNull(loginUser)) { + AuthUtil.verifyLoginUserExpire(loginUser); + SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser); + } + } + return true; + } + + @Override + public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + throws Exception { + SecurityContextHolder.remove(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java new file mode 100644 index 0000000..3955ddc --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java @@ -0,0 +1,153 @@ +package com.muyu.common.security.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.core.utils.uuid.IdUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * token验证处理 + * + * @author muyu + */ +@Component +public class TokenService { + protected static final long MILLIS_SECOND = 1000; + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE; + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + private final static long expireTime = CacheConstants.EXPIRATION; + + private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; + @Autowired + private RedisService redisService; + + /** + * 创建令牌 + */ + public Map createToken (LoginUser loginUser) { + String token = IdUtils.fastUUID(); + Long userId = loginUser.getSysUser().getUserId(); + String userName = loginUser.getSysUser().getUserName(); + loginUser.setToken(token); + loginUser.setUserid(userId); + loginUser.setUsername(userName); + loginUser.setIpaddr(IpUtils.getIpAddr()); + refreshToken(loginUser); + + // Jwt存储信息 + Map claimsMap = new HashMap(); + claimsMap.put(SecurityConstants.USER_KEY, token); + claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); + claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); + + // 接口返回信息 + Map rspMap = new HashMap(); + rspMap.put("access_token", JwtUtils.createToken(claimsMap)); + rspMap.put("expires_in", expireTime); + return rspMap; + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser () { + return getLoginUser(ServletUtils.getRequest()); + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser (LoginUser loginUser) { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { + refreshToken(loginUser); + } + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser (HttpServletRequest request) { + // 获取请求携带的令牌 + String token = SecurityUtils.getToken(request); + return getLoginUser(token); + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser (String token) { + LoginUser user = null; + try { + if (StringUtils.isNotEmpty(token)) { + String userkey = JwtUtils.getUserKey(token); + user = redisService.getCacheObject(getTokenKey(userkey)); + return user; + } + } catch (Exception e) { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + return user; + } + + /** + * 删除用户缓存信息 + */ + public void delLoginUser (String token) { + if (StringUtils.isNotEmpty(token)) { + String userkey = JwtUtils.getUserKey(token); + redisService.deleteObject(getTokenKey(userkey)); + } + } + + /** + * 验证令牌有效期,相差不足120分钟,自动刷新缓存 + * + * @param loginUser + */ + public void verifyToken (LoginUser loginUser) { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken (LoginUser loginUser) { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + private String getTokenKey (String token) { + return ACCESS_TOKEN + token; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java new file mode 100644 index 0000000..04ee068 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java @@ -0,0 +1,71 @@ +package com.muyu.common.security.utils; + +import com.alibaba.fastjson2.JSONArray; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.system.domain.SysDictData; + +import java.util.Collection; +import java.util.List; + +/** + * 字典工具类 + * + * @author muyu + */ +public class DictUtils { + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache (String key, List dictDatas) { + SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * + * @return dictDatas 字典数据列表 + */ + public static List getDictCache (String key) { + JSONArray arrayCache = SpringUtils.getBean(RedisService.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache (String key) { + SpringUtils.getBean(RedisService.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache () { + Collection keys = SpringUtils.getBean(RedisService.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisService.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * + * @return 缓存键key + */ + public static String getCacheKey (String configKey) { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java new file mode 100644 index 0000000..6e3f92b --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java @@ -0,0 +1,109 @@ +package com.muyu.common.security.utils; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 权限获取工具类 + * + * @author muyu + */ +public class SecurityUtils { + /** + * 获取用户ID + */ + public static Long getUserId () { + return SecurityContextHolder.getUserId(); + } + + /** + * 获取用户名称 + */ + public static String getUsername () { + return SecurityContextHolder.getUserName(); + } + + /** + * 获取用户key + */ + public static String getUserKey () { + return SecurityContextHolder.getUserKey(); + } + + /** + * 获取登录用户信息 + */ + public static LoginUser getLoginUser () { + return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); + } + + /** + * 获取请求token + */ + public static String getToken () { + return getToken(ServletUtils.getRequest()); + } + + /** + * 根据request获取请求token + */ + public static String getToken (HttpServletRequest request) { + // 从header获取token标识 + String token = request.getHeader(TokenConstants.AUTHENTICATION); + return replaceTokenPrefix(token); + } + + /** + * 裁剪token前缀 + */ + public static String replaceTokenPrefix (String token) { + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { + token = token.replaceFirst(TokenConstants.PREFIX, ""); + } + return token; + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * + * @return 结果 + */ + public static boolean isAdmin (Long userId) { + return userId != null && 1L == userId; + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * + * @return 加密字符串 + */ + public static String encryptPassword (String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * + * @return 结果 + */ + public static boolean matchesPassword (String rawPassword, String encodedPassword) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..f4e76b1 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.muyu.common.security.config.WebMvcConfig +com.muyu.common.security.service.TokenService +com.muyu.common.security.aspect.PreAuthorizeAspect +com.muyu.common.security.aspect.InnerAuthAspect +com.muyu.common.security.handler.GlobalExceptionHandler diff --git a/cloud-common/cloud-common-system/pom.xml b/cloud-common/cloud-common-system/pom.xml new file mode 100644 index 0000000..ad4356f --- /dev/null +++ b/cloud-common/cloud-common-system/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-system + + cloud-common-system系统级远程调用 + + + 17 + 17 + UTF-8 + + + + + + com.muyu + cloud-common-core + + + diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java new file mode 100644 index 0000000..a18af37 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java @@ -0,0 +1,66 @@ +package com.muyu.common.system.domain; + + +import lombok.*; + +import java.io.Serializable; +import java.util.Set; + +/** + * 用户信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginUser implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户名id + */ + private Long userid; + + /** + * 用户名 + */ + private String username; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 角色列表 + */ + private Set roles; + + /** + * 用户信息 + */ + private SysUser sysUser; + +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java new file mode 100644 index 0000000..65ada9e --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java @@ -0,0 +1,108 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import java.util.ArrayList; +import java.util.List; + +/** + * 部门表 sys_dept + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysDept extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 负责人 + */ + private String leader; + + /** + * 联系电话 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 部门状态:0正常,1停用 + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private String delFlag; + + /** + * 父部门名称 + */ + private String parentName; + + /** + * 子部门 + */ + private List children = new ArrayList(); + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName () { + return deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum () { + return orderNum; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone () { + return phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail () { + return email; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java new file mode 100644 index 0000000..5beee3f --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java @@ -0,0 +1,104 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 字典数据表 sys_dict_data + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysDictData extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 字典编码 + */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** + * 字典排序 + */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** + * 字典标签 + */ + @Excel(name = "字典标签") + private String dictLabel; + + /** + * 字典键值 + */ + @Excel(name = "字典键值") + private String dictValue; + + /** + * 字典类型 + */ + @Excel(name = "字典类型") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + private String cssClass; + + /** + * 表格字典样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel () { + return dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue () { + return dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType () { + return dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass () { + return cssClass; + } + + public boolean getDefault () { + return UserConstants.YES.equals(this.isDefault); + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java new file mode 100644 index 0000000..d623333 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java @@ -0,0 +1,62 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** + * 字典类型表 sys_dict_type + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysDictType extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 字典主键 + */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** + * 字典名称 + */ + @Excel(name = "字典名称") + private String dictName; + + /** + * 字典类型 + */ + @Excel(name = "字典类型") + private String dictType; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName () { + return dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType () { + return dictType; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java new file mode 100644 index 0000000..ad48a1e --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java @@ -0,0 +1,27 @@ +package com.muyu.common.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysFile { + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址 + */ + private String url; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java new file mode 100644 index 0000000..c66f131 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java @@ -0,0 +1,64 @@ +package com.muyu.common.system.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 系统访问记录表 sys_logininfor + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysLogininfor extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + private Long infoId; + + /** + * 用户账号 + */ + @Excel(name = "用户账号") + private String userName; + + /** + * 状态 0成功 1失败 + */ + @Excel(name = "状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** + * 地址 + */ + @Excel(name = "地址") + private String ipaddr; + + /** + * 描述 + */ + @Excel(name = "描述") + private String msg; + + /** + * 访问时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date accessTime; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java new file mode 100644 index 0000000..b207611 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java @@ -0,0 +1,129 @@ +package com.muyu.common.system.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 操作日志记录表 oper_log + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysOperLog extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + private Long operId; + + /** + * 操作模块 + */ + @Excel(name = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + @Excel(name = "请求方法") + private String method; + + /** + * 请求方式 + */ + @Excel(name = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** + * 操作人员 + */ + @Excel(name = "操作人员") + private String operName; + + /** + * 部门名称 + */ + @Excel(name = "部门名称") + private String deptName; + + /** + * 请求url + */ + @Excel(name = "请求地址") + private String operUrl; + + /** + * 操作地址 + */ + @Excel(name = "操作地址") + private String operIp; + + /** + * 请求参数 + */ + @Excel(name = "请求参数") + private String operParam; + + /** + * 返回参数 + */ + @Excel(name = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** + * 错误消息 + */ + @Excel(name = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** + * 消耗时间 + */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java new file mode 100644 index 0000000..1aab56a --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java @@ -0,0 +1,127 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.Set; + +/** + * 角色表 sys_role + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysRole extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** + * 角色名称 + */ + @Excel(name = "角色名称") + private String roleName; + + /** + * 角色权限 + */ + @Excel(name = "角色权限") + private String roleKey; + + /** + * 角色排序 + */ + @Excel(name = "角色排序") + private Integer roleSort; + + /** + * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) + */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) + */ + private boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private String delFlag; + + /** + * 用户是否存在此角色标识 默认不存在 + */ + private boolean flag = false; + + /** + * 菜单组 + */ + private Long[] menuIds; + + /** + * 部门组(数据权限) + */ + private Long[] deptIds; + + /** + * 角色菜单权限 + */ + private Set permissions; + + + public SysRole (Long roleId) { + this.roleId = roleId; + } + + public static boolean isAdmin (Long roleId) { + return roleId != null && 1L == roleId; + } + + public boolean isAdmin () { + return isAdmin(this.roleId); + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName () { + return roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey () { + return roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort () { + return roleSort; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java new file mode 100644 index 0000000..999b22c --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java @@ -0,0 +1,173 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.annotation.Excel.Type; +import com.muyu.common.core.annotation.Excels; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.core.xss.Xss; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; +import java.util.List; + +/** + * 用户对象 sys_user + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysUser extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + /** + * 部门ID + */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** + * 用户账号 + */ + @Excel(name = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @Excel(name = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @Excel(name = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @Excel(name = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** + * 用户头像 + */ + private String avatar; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private String delFlag; + + /** + * 最后登录IP + */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** + * 最后登录时间 + */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** + * 部门对象 + */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** + * 角色对象 + */ + private List roles; + + /** + * 角色组 + */ + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 角色ID + */ + private Long roleId; + + public SysUser (Long userId) { + this.userId = userId; + } + + public static boolean isAdmin (Long userId) { + return userId != null && 1L == userId; + } + + public boolean isAdmin () { + return isAdmin(this.userId); + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName () { + return nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName () { + return userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail () { + return email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber () { + return phonenumber; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java new file mode 100644 index 0000000..96dd181 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java @@ -0,0 +1,29 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.SysFile; +import com.muyu.common.system.remote.factory.RemoteFileFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class) +public interface RemoteFileService { + /** + * 上传文件 + * + * @param file 文件信息 + * + * @return 结果 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public Result upload (@RequestPart(value = "file") MultipartFile file); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java new file mode 100644 index 0000000..7e767c4 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java @@ -0,0 +1,42 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.common.system.remote.factory.RemoteLogFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +/** + * 日志服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class) +public interface RemoteLogService { + /** + * 保存系统日志 + * + * @param sysOperLog 日志实体 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/operlog") + public Result saveLog (@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) throws Exception; + + /** + * 保存访问记录 + * + * @param sysLogininfor 访问实体 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/logininfor") + public Result saveLogininfor (@RequestBody SysLogininfor sysLogininfor, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java new file mode 100644 index 0000000..cb968e0 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java @@ -0,0 +1,40 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.remote.factory.RemoteUserFallbackFactory; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +/** + * 用户服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) +public interface RemoteUserService { + /** + * 通过用户名查询用户信息 + * + * @param username 用户名 + * @param source 请求来源 + * + * @return 结果 + */ + @GetMapping("/user/info/{username}") + public Result getUserInfo (@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + /** + * 注册用户信息 + * + * @param sysUser 用户信息 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/user/register") + public Result registerUserInfo (@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java new file mode 100644 index 0000000..395f74f --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java @@ -0,0 +1,31 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.remote.RemoteFileService; +import com.muyu.common.system.domain.SysFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteFileFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class); + + @Override + public RemoteFileService create (Throwable throwable) { + log.error("文件服务调用失败:{}", throwable.getMessage()); + return new RemoteFileService() { + @Override + public Result upload (MultipartFile file) { + return Result.error("上传文件失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java new file mode 100644 index 0000000..1fc0491 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java @@ -0,0 +1,37 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.common.system.domain.SysOperLog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * 日志服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteLogFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteLogFallbackFactory.class); + + @Override + public RemoteLogService create (Throwable throwable) { + log.error("日志服务调用失败:{}", throwable.getMessage(), throwable); + return new RemoteLogService() { + @Override + public Result saveLog (SysOperLog sysOperLog, String source) { + return Result.error("保存操作日志失败:" + throwable.getMessage()); + } + + @Override + public Result saveLogininfor (SysLogininfor sysLogininfor, String source) { + return Result.error("保存登录日志失败:" + throwable.getMessage()); + } + }; + + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java new file mode 100644 index 0000000..b7f4d60 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java @@ -0,0 +1,36 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.remote.RemoteUserService; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * 用户服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteUserFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class); + + @Override + public RemoteUserService create (Throwable throwable) { + log.error("用户服务调用失败:{}", throwable.getMessage()); + return new RemoteUserService() { + @Override + public Result getUserInfo (String username, String source) { + return Result.error("获取用户失败:" + throwable.getMessage()); + } + + @Override + public Result registerUserInfo (SysUser sysUser, String source) { + return Result.error("注册用户失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..b9a11b8 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.muyu.common.system.remote.factory.RemoteUserFallbackFactory +com.muyu.common.system.remote.factory.RemoteLogFallbackFactory +com.muyu.common.system.remote.factory.RemoteFileFallbackFactory diff --git a/cloud-common/cloud-common-xxl/pom.xml b/cloud-common/cloud-common-xxl/pom.xml new file mode 100644 index 0000000..4af9214 --- /dev/null +++ b/cloud-common/cloud-common-xxl/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-xxl + + XllJob定时任务 + + + 17 + 17 + UTF-8 + + + + + + + com.muyu + cloud-common-core + + + + + com.xuxueli + xxl-job-core + + + + \ No newline at end of file diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java new file mode 100644 index 0000000..0bf4362 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java @@ -0,0 +1,28 @@ +package com.muyu.common.xxl; + +import com.muyu.common.core.utils.StringUtils; +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class XXLJobConfig { + @Bean + public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties) { + if (StringUtils.isEmpty(xxlJobProperties.getAdminAddresses())){ + throw new RuntimeException("请在bootstrap.yml当中配置shared-configs项,xxl-job共享配置[application-xxl-config]"); + } + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses()); + xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppName()); + xxlJobSpringExecutor.setIp(xxlJobProperties.getIp()); + xxlJobSpringExecutor.setPort(xxlJobProperties.getPort()); + xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken()); + xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath()); + xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays()); + log.info(">>>>>>>>>>> xxl-job config init success."); + return xxlJobSpringExecutor; + } +} diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java new file mode 100644 index 0000000..c1428f7 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java @@ -0,0 +1,63 @@ +package com.muyu.common.xxl; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "xxl-job") +public class XxlJobProperties { + + /** + * 调度中心部署根地址 [选填]: + * 如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调"; + * 为空则关闭自动注册; + */ + private String adminAddresses; + + /** + * 执行器通讯TOKEN [选填]: + * 非空时启用; + */ + private String accessToken; + + /** + * 执行器AppName [选填]: + * 执行器心跳注册分组依据;为空则关闭自动注册 + */ + private String appName; + + /** + * 执行器注册 [选填]: + * 优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。 + * 从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 + */ + private String address; + + /** + * 执行器IP [选填]: + * 默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用; + * 地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + */ + private String ip; + + /** + * 执行器端口号 [选填]: + * 小于等于0则自动获取;默认端口为9999, + * 单机部署多个执行器时,注意要配置不同执行器端口; + */ + private int port; + + /** + * 执行器运行日志文件存储磁盘路径 [选填]: + * 需要对该路径拥有读写权限;为空则使用默认路径; + */ + private String logPath; + + /** + * 执行器日志文件保存天数 [选填]: + * 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + */ + private int logRetentionDays; +} diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java new file mode 100644 index 0000000..51cd2a4 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java @@ -0,0 +1,38 @@ +package com.muyu.common.xxl.demo; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class XxlJobDemoService { + + /** + * 无参测试 + */ + @XxlJob("xxl-job-demo-no-param") + public void xxlJobDemoNoParam(){ + log.info("调度名称:[{}] - 无参", "xxl-job-demo-no-param"); + } + + /** + * 有参测试 + */ + @XxlJob("xxl-job-demo-one-param") + public void xxlJobDemoOneParam(){ + String param = XxlJobHelper.getJobParam(); + log.info("调度名称:[{}] - 参数:[{}]", "xxl-job-demo-one-param", param); + } + /** + * 分片测试 + */ + @XxlJob("xxl-job-slice-demo") + public void xxlJobSliceDemo(){ + int shardTotal = XxlJobHelper.getShardTotal(); + int shardIndex = XxlJobHelper.getShardIndex(); + String param = XxlJobHelper.getJobParam(); + log.info("调度名称:[{}-[{}/{}(分片位置/分片总数)]] - 参数:[{}]", "xxl-job-slice-demo",shardIndex, shardTotal, param); + } +} diff --git a/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..1404a51 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.muyu.common.xxl.XXLJobConfig +com.muyu.common.xxl.XxlJobProperties +com.muyu.common.xxl.demo.XxlJobDemoService \ No newline at end of file diff --git a/cloud-common/pom.xml b/cloud-common/pom.xml new file mode 100644 index 0000000..d00dfc6 --- /dev/null +++ b/cloud-common/pom.xml @@ -0,0 +1,32 @@ + + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + + cloud-common-log + cloud-common-core + cloud-common-redis + cloud-common-seata + cloud-common-api-doc + cloud-common-security + cloud-common-datascope + cloud-common-datasource + cloud-common-system + cloud-common-xxl + cloud-common-rabbit + + + cloud-common + pom + + + cloud-common通用模块 + + + diff --git a/cloud-gateway/pom.xml b/cloud-gateway/pom.xml new file mode 100644 index 0000000..106c740 --- /dev/null +++ b/cloud-gateway/pom.xml @@ -0,0 +1,104 @@ + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + cloud-gateway + + + cloud-gateway网关模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + + com.alibaba.csp + sentinel-datasource-nacos + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springframework.cloud + spring-cloud-loadbalancer + + + + + pro.fessional + kaptcha + + + + + com.muyu + cloud-common-redis + + + + com.github.xiaoymin + knife4j-gateway-spring-boot-starter + 4.5.0 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java b/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java new file mode 100644 index 0000000..43c7ed9 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java @@ -0,0 +1,17 @@ +package com.muyu.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 网关启动程序 + * + * @author muyu + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudGatewayApplication { + public static void main (String[] args) { + SpringApplication.run(CloudGatewayApplication.class, args); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java new file mode 100644 index 0000000..557af8a --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java @@ -0,0 +1,82 @@ +package com.muyu.gateway.config; + +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author muyu + */ +@Configuration +public class CaptchaConfig { + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean () { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath () { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.muyu.gateway.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java new file mode 100644 index 0000000..bf004bf --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java @@ -0,0 +1,21 @@ +package com.muyu.gateway.config; + +import com.muyu.gateway.handler.SentinelFallbackHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +/** + * 网关限流配置 + * + * @author muyu + */ +@Configuration +public class GatewayConfig { + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SentinelFallbackHandler sentinelGatewayExceptionHandler () { + return new SentinelFallbackHandler(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7b0636d --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java @@ -0,0 +1,61 @@ +package com.muyu.gateway.config; + +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +import java.util.Random; + +/** + * 验证码文本生成器 + * + * @author muyu + */ +public class KaptchaTextCreator extends DefaultTextCreator { + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText () { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } else if (randomoperands == 1) { + if ((x != 0) && y % x == 0) { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } else { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } else if (randomoperands == 2) { + if (x >= y) { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } else { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } else { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java new file mode 100644 index 0000000..41b8e47 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java @@ -0,0 +1,29 @@ +package com.muyu.gateway.config; + +import com.muyu.gateway.handler.ValidateCodeHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; + +/** + * 路由配置信息 + * + * @author muyu + */ +@Configuration +public class RouterFunctionConfiguration { + @Autowired + private ValidateCodeHandler validateCodeHandler; + + @SuppressWarnings("rawtypes") + @Bean + public RouterFunction routerFunction () { + return RouterFunctions.route( + RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), + validateCodeHandler); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..60b1814 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java @@ -0,0 +1,41 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * 验证码配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.captcha") +public class CaptchaProperties { + /** + * 验证码开关 + */ + private Boolean enabled; + + /** + * 验证码类型(math 数组计算 char 字符) + */ + private String type; + + public Boolean getEnabled () { + return enabled; + } + + public void setEnabled (Boolean enabled) { + this.enabled = enabled; + } + + public String getType () { + return type; + } + + public void setType (String type) { + this.type = type; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 0000000..d5ea46c --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java @@ -0,0 +1,31 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * 放行白名单配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.ignore") +public class IgnoreWhiteProperties { + /** + * 放行白名单配置,网关不校验此处的白名单 + */ + private List whites = new ArrayList<>(); + + public List getWhites () { + return whites; + } + + public void setWhites (List whites) { + this.whites = whites; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..31dcc6a --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java @@ -0,0 +1,44 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * XSS跨站脚本配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.xss") +public class XssProperties { + /** + * Xss开关 + */ + private Boolean enabled; + + /** + * 排除路径 + */ + private List excludeUrls = new ArrayList<>(); + + public Boolean getEnabled () { + return enabled; + } + + public void setEnabled (Boolean enabled) { + this.enabled = enabled; + } + + public List getExcludeUrls () { + return excludeUrls; + } + + public void setExcludeUrls (List excludeUrls) { + this.excludeUrls = excludeUrls; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java new file mode 100644 index 0000000..9ceec12 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java @@ -0,0 +1,226 @@ +package com.muyu.gateway.filter; + +import cn.hutool.core.date.LocalDateTimeUtil; +import com.alibaba.nacos.common.utils.StringUtils; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.gateway.model.AccessLog; +import com.muyu.gateway.utils.WebFrameworkUtils; +import lombok.extern.log4j.Log4j2; +import org.reactivestreams.Publisher; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage; +import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; +import org.springframework.cloud.gateway.support.BodyInserterContext; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpOutputMessage; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.List; + + +/** + * 网关的访问日志过滤器 + *

+ *

+ * TODO 如果网关执行异常,不会记录访问日志,后续研究下 https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging + */ +@Log4j2 +@Component +public class AccessLogFilter implements GlobalFilter, Ordered { + + private final List> messageReaders = HandlerStrategies.withDefaults().messageReaders(); + + /** + * 打印日志 + * + * @param gatewayLog 网关日志 + */ + private void writeAccessLog(AccessLog gatewayLog) { + log.info("[网关日志:{}]", gatewayLog.toString()); + } + + @Override + public int getOrder() { + return -99; + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 将 Request 中可以直接获取到的参数,设置到网关日志 + ServerHttpRequest request = exchange.getRequest(); + // TODO traceId + AccessLog accessLog = AccessLog.builder() + .userId(request.getHeaders().getFirst(SecurityConstants.DETAILS_USER_ID)) + .route(WebFrameworkUtils.getGatewayRoute(exchange)) + .schema(request.getURI().getScheme()) + .requestMethod(request.getMethod().name()) + .requestUrl(request.getURI().getRawPath()) + .queryParams(request.getQueryParams()) + .requestHeaders(request.getHeaders()) + .startTime(LocalDateTime.now()) + .userIp(WebFrameworkUtils.getClientIP(exchange)) + .build(); + + // 继续 filter 过滤 + MediaType mediaType = request.getHeaders().getContentType(); + return MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) + ? + filterWithRequestBody(exchange, chain, accessLog) + : + filterWithoutRequestBody(exchange, chain, accessLog); + } + + private Mono filterWithoutRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog accessLog) { + // 包装 Response,用于记录 Response Body + ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog); + return chain.filter(exchange.mutate().response(decoratedResponse).build()) + .then(Mono.fromRunnable(() -> writeAccessLog(accessLog))); // 打印日志 + } + + /** + * 参考 {@link ModifyRequestBodyGatewayFilterFactory} 实现 + *

+ * 差别主要在于使用 modifiedBody 来读取 Request Body 数据 + */ + private Mono filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog gatewayLog) { + // 设置 Request Body 读取时,设置到网关日志 + ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders); + Mono modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> { + gatewayLog.setRequestBody(body); + return Mono.just(body); + }); + + // 创建 BodyInserter 对象 + BodyInserter, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class); + // 创建 CachedBodyOutputMessage 对象 + HttpHeaders headers = new HttpHeaders(); + headers.putAll(exchange.getRequest().getHeaders()); + // the new content type will be computed by bodyInserter + // and then set in the request decorator + headers.remove(HttpHeaders.CONTENT_LENGTH); // 移除 + CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers); + // 通过 BodyInserter 将 Request Body 写入到 CachedBodyOutputMessage 中 + return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> { + // 包装 Request,用于缓存 Request Body + ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage); + // 包装 Response,用于记录 Response Body + ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog); + // 记录普通的 + return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build()) + .then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); // 打印日志 + + })); + } + + /** + * 记录响应日志 + * 通过 DataBufferFactory 解决响应体分段传输问题。 + */ + private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, AccessLog gatewayLog) { + ServerHttpResponse response = exchange.getResponse(); + return new ServerHttpResponseDecorator(response) { + + @Override + public Mono writeWith(Publisher body) { + if (body instanceof Flux) { + DataBufferFactory bufferFactory = response.bufferFactory(); + // 计算执行时间 + gatewayLog.setEndTime(LocalDateTime.now()); + gatewayLog.setDuration((int) (LocalDateTimeUtil.between(gatewayLog.getStartTime(), + gatewayLog.getEndTime()).toMillis())); + // 设置其它字段 +// gatewayLog.setUserId(SecurityFrameworkUtils.getLoginUserId(exchange)); + gatewayLog.setResponseHeaders(response.getHeaders()); + gatewayLog.setHttpStatus(response.getStatusCode()); + + // 获取响应类型,如果是 json 就打印 + String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); + if (StringUtils.isNotBlank(originalResponseContentType) + && originalResponseContentType.contains("application/json")) { + Flux fluxBody = Flux.from(body); + return super.writeWith(fluxBody.buffer().map(dataBuffers -> { + // 设置 response body 到网关日志 + byte[] content = readContent(dataBuffers); + String responseResult = new String(content, StandardCharsets.UTF_8); + gatewayLog.setResponseBody(responseResult); + + // 响应 + return bufferFactory.wrap(content); + })); + } + } + // if body is not a flux. never got there. + return super.writeWith(body); + } + }; + } + + // ========== 参考 ModifyRequestBodyGatewayFilterFactory 中的方法 ========== + + /** + * 请求装饰器,支持重新计算 headers、body 缓存 + * + * @param exchange 请求 + * @param headers 请求头 + * @param outputMessage body 缓存 + * @return 请求装饰器 + */ + private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) { + return new ServerHttpRequestDecorator(exchange.getRequest()) { + + @Override + public HttpHeaders getHeaders() { + long contentLength = headers.getContentLength(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(super.getHeaders()); + if (contentLength > 0) { + httpHeaders.setContentLength(contentLength); + } else { + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + } + return httpHeaders; + } + + @Override + public Flux getBody() { + return outputMessage.getBody(); + } + }; + } + + // ========== 参考 ModifyResponseBodyGatewayFilterFactory 中的方法 ========== + + private byte[] readContent(List dataBuffers) { + // 合并多个流集合,解决返回体分段传输 + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + // 释放掉内存 + DataBufferUtils.release(join); + return content; + } + +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..47e073f --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java @@ -0,0 +1,120 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.HttpStatus; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.gateway.config.properties.IgnoreWhiteProperties; +import io.jsonwebtoken.Claims; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关鉴权 + * + * @author muyu + */ +@Component +public class AuthFilter implements GlobalFilter, Ordered { + private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); + + // 排除过滤的 uri 地址,nacos自行添加 + @Autowired + private IgnoreWhiteProperties ignoreWhite; + + @Autowired + private RedisService redisService; + + + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpRequest.Builder mutate = request.mutate(); + + String url = request.getURI().getPath(); + // 跳过不需要验证的路径 + if (StringUtils.matches(url, ignoreWhite.getWhites())) { + return chain.filter(exchange); + } + String token = getToken(request); + if (StringUtils.isEmpty(token)) { + return unauthorizedResponse(exchange, "令牌不能为空"); + } + Claims claims = JwtUtils.parseToken(token); + if (claims == null) { + return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); + } + String userkey = JwtUtils.getUserKey(claims); + boolean islogin = redisService.hasKey(getTokenKey(userkey)); + if (!islogin) { + return unauthorizedResponse(exchange, "登录状态已过期"); + } + String userid = JwtUtils.getUserId(claims); + String username = JwtUtils.getUserName(claims); + if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) { + return unauthorizedResponse(exchange, "令牌验证失败"); + } + + // 设置用户信息到请求 + addHeader(mutate, SecurityConstants.USER_KEY, userkey); + addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); + addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); + // 内部请求来源参数清除 + removeHeader(mutate, SecurityConstants.FROM_SOURCE); + return chain.filter(exchange.mutate().request(mutate.build()).build()); + } + + private void addHeader (ServerHttpRequest.Builder mutate, String name, Object value) { + if (value == null) { + return; + } + String valueStr = value.toString(); + String valueEncode = ServletUtils.urlEncode(valueStr); + mutate.header(name, valueEncode); + } + + private void removeHeader (ServerHttpRequest.Builder mutate, String name) { + mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); + } + + private Mono unauthorizedResponse (ServerWebExchange exchange, String msg) { + log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); + } + + /** + * 获取缓存key + */ + private String getTokenKey (String token) { + return CacheConstants.LOGIN_TOKEN_KEY + token; + } + + /** + * 获取请求token + */ + private String getToken (ServerHttpRequest request) { + String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { + token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); + } + return token; + } + + @Override + public int getOrder () { + return -200; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java new file mode 100644 index 0000000..0096d4c --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java @@ -0,0 +1,58 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.utils.ServletUtils; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 黑名单过滤器 + * + * @author muyu + */ +@Component +public class BlackListUrlFilter extends AbstractGatewayFilterFactory { + public BlackListUrlFilter () { + super(Config.class); + } + + @Override + public GatewayFilter apply (Config config) { + return (exchange, chain) -> { + + String url = exchange.getRequest().getURI().getPath(); + if (config.matchBlacklist(url)) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问"); + } + + return chain.filter(exchange); + }; + } + + public static class Config { + private List blacklistUrl; + + private List blacklistUrlPattern = new ArrayList<>(); + + public boolean matchBlacklist (String url) { + return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); + } + + public List getBlacklistUrl () { + return blacklistUrl; + } + + public void setBlacklistUrl (List blacklistUrl) { + this.blacklistUrl = blacklistUrl; + this.blacklistUrlPattern.clear(); + this.blacklistUrl.forEach(url -> { + this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); + }); + } + } + +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java new file mode 100644 index 0000000..3a09564 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java @@ -0,0 +1,75 @@ +package com.muyu.gateway.filter; + +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Collections; +import java.util.List; + +/** + * 获取body请求数据(解决流不能重复读取问题) + * + * @author muyu + */ +@Component +public class CacheRequestFilter extends AbstractGatewayFilterFactory { + public CacheRequestFilter () { + super(Config.class); + } + + @Override + public String name () { + return "CacheRequestFilter"; + } + + @Override + public GatewayFilter apply (Config config) { + CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter(); + Integer order = config.getOrder(); + if (order == null) { + return cacheRequestGatewayFilter; + } + return new OrderedGatewayFilter(cacheRequestGatewayFilter, order); + } + + @Override + public List shortcutFieldOrder () { + return Collections.singletonList("order"); + } + + public static class CacheRequestGatewayFilter implements GatewayFilter { + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + // GET DELETE 不过滤 + HttpMethod method = exchange.getRequest().getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { + if (serverHttpRequest == exchange.getRequest()) { + return chain.filter(exchange); + } + return chain.filter(exchange.mutate().request(serverHttpRequest).build()); + }); + } + } + + static class Config { + private Integer order; + + public Integer getOrder () { + return order; + } + + public void setOrder (Integer order) { + this.order = order; + } + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java new file mode 100644 index 0000000..c19c944 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java @@ -0,0 +1,69 @@ +package com.muyu.gateway.filter; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gateway.config.properties.CaptchaProperties; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; + +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 验证码过滤器 + * + * @author muyu + */ +@Component +public class ValidateCodeFilter extends AbstractGatewayFilterFactory { + private final static String[] VALIDATE_URL = new String[]{"/auth/login", "/auth/register"}; + private static final String CODE = "code"; + private static final String UUID = "uuid"; + @Autowired + private ValidateCodeService validateCodeService; + @Autowired + private CaptchaProperties captchaProperties; + + @Override + public GatewayFilter apply (Object config) { + return (exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + + // 非登录/注册请求或验证码关闭,不处理 + if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) { + return chain.filter(exchange); + } + + try { + String rspStr = resolveBodyFromRequest(request); + JSONObject obj = JSON.parseObject(rspStr); + validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID)); + } catch (Exception e) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage()); + } + return chain.filter(exchange); + }; + } + + private String resolveBodyFromRequest (ServerHttpRequest serverHttpRequest) { + // 获取请求体 + Flux body = serverHttpRequest.getBody(); + AtomicReference bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + }); + return bodyRef.get(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java new file mode 100644 index 0000000..4316d84 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java @@ -0,0 +1,114 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.html.EscapeUtil; +import com.muyu.gateway.config.properties.XssProperties; +import io.netty.buffer.ByteBufAllocator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +/** + * 跨站脚本过滤器 + * + * @author muyu + */ +@Component +@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") +public class XssFilter implements GlobalFilter, Ordered { + // 跨站脚本的 xss 配置,nacos自行添加 + @Autowired + private XssProperties xss; + + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + // xss开关未开启 或 通过nacos关闭,不过滤 + if (!xss.getEnabled()) { + return chain.filter(exchange); + } + // GET DELETE 不过滤 + HttpMethod method = request.getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + // 非json类型,不过滤 + if (!isJsonRequest(exchange)) { + return chain.filter(exchange); + } + // excludeUrls 不过滤 + String url = request.getURI().getPath(); + if (StringUtils.matches(url, xss.getExcludeUrls())) { + return chain.filter(exchange); + } + ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange); + return chain.filter(exchange.mutate().request(httpRequestDecorator).build()); + + } + + private ServerHttpRequestDecorator requestDecorator (ServerWebExchange exchange) { + ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) { + @Override + public Flux getBody () { + Flux body = super.getBody(); + return body.buffer().map(dataBuffers -> { + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + DataBufferUtils.release(join); + String bodyStr = new String(content, StandardCharsets.UTF_8); + // 防xss攻击过滤 + bodyStr = EscapeUtil.clean(bodyStr); + // 转成字节 + byte[] bytes = bodyStr.getBytes(StandardCharsets.UTF_8); + NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); + DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); + buffer.write(bytes); + return buffer; + }); + } + + @Override + public HttpHeaders getHeaders () { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(super.getHeaders()); + // 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length + httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + return httpHeaders; + } + + }; + return serverHttpRequestDecorator; + } + + /** + * 是否是Json请求 + * + * @param exchange HTTP请求 + */ + public boolean isJsonRequest (ServerWebExchange exchange) { + String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + + @Override + public int getOrder () { + return -100; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..ed5198e --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,48 @@ +package com.muyu.gateway.handler; + +import com.muyu.common.core.utils.ServletUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.cloud.gateway.support.NotFoundException; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关统一异常处理 + * + * @author muyu + */ +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); + + @Override + public Mono handle (ServerWebExchange exchange, Throwable ex) { + ServerHttpResponse response = exchange.getResponse(); + + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + + String msg; + + if (ex instanceof NotFoundException) { + msg = "服务未找到"; + } else if (ex instanceof ResponseStatusException) { + ResponseStatusException responseStatusException = (ResponseStatusException) ex; + msg = responseStatusException.getMessage(); + } else { + msg = "内部服务器错误"; + } + + log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return ServletUtils.webFluxResponseWriter(response, msg); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java new file mode 100644 index 0000000..d93866f --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java @@ -0,0 +1,35 @@ +package com.muyu.gateway.handler; + +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.muyu.common.core.utils.ServletUtils; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebExceptionHandler; +import reactor.core.publisher.Mono; + +/** + * 自定义限流异常处理 + * + * @author muyu + */ +public class SentinelFallbackHandler implements WebExceptionHandler { + private Mono writeResponse (ServerResponse response, ServerWebExchange exchange) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); + } + + @Override + public Mono handle (ServerWebExchange exchange, Throwable ex) { + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + if (!BlockException.isBlockException(ex)) { + return Mono.error(ex); + } + return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); + } + + private Mono handleBlockedRequest (ServerWebExchange exchange, Throwable throwable) { + return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java new file mode 100644 index 0000000..f3b3206 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java @@ -0,0 +1,37 @@ +package com.muyu.gateway.handler; + +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.domain.Result; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.HandlerFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.io.IOException; + +/** + * 验证码获取 + * + * @author muyu + */ +@Component +public class ValidateCodeHandler implements HandlerFunction { + @Autowired + private ValidateCodeService validateCodeService; + + @Override + public Mono handle (ServerRequest serverRequest) { + Result ajax; + try { + ajax = validateCodeService.createCaptcha(); + } catch (CaptchaException | IOException e) { + return Mono.error(e); + } + return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax)); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/model/AccessLog.java b/cloud-gateway/src/main/java/com/muyu/gateway/model/AccessLog.java new file mode 100644 index 0000000..b52eb3e --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/model/AccessLog.java @@ -0,0 +1,120 @@ +package com.muyu.gateway.model; + +import com.muyu.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.http.HttpStatusCode; +import org.springframework.util.MultiValueMap; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 网关的访问日志 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AccessLog { + + /** + * 链路追踪编号 + */ + private String traceId; + /** + * 用户编号 + */ + private String userId; + + /** + * 路由 + * + * 类似 ApiAccessLogCreateReqDTO 的 applicationName + */ + private Route route; + + /** + * 协议 + */ + private String schema; + /** + * 请求方法名 + */ + private String requestMethod; + /** + * 访问地址 + */ + private String requestUrl; + /** + * 查询参数 + */ + private MultiValueMap queryParams; + /** + * 请求体 + */ + private String requestBody; + /** + * 请求头 + */ + private MultiValueMap requestHeaders; + /** + * 用户 IP + */ + private String userIp; + + /** + * 响应体 + * + * 类似 ApiAccessLogCreateReqDTO 的 resultCode + resultMsg + */ + private String responseBody; + /** + * 响应头 + */ + private MultiValueMap responseHeaders; + /** + * 响应结果 + */ + private HttpStatusCode httpStatus; + + /** + * 开始请求时间 + */ + private LocalDateTime startTime; + /** + * 结束请求时间 + */ + private LocalDateTime endTime; + /** + * 执行时长,单位:毫秒 + */ + private Integer duration; + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("请求简略信息", + StringUtils.format("[userId:[{}]-userIp:[{}]-traceId:[{}]] ---结果--- {{}-{}}:{} ---> {}",userId, userIp, traceId, schema, requestMethod, requestUrl,httpStatus) + ) + .append("路由", route) + .append("查询参数", queryParams) + .append("请求体", requestBody) + .append("请求头", requestHeaders) + .append("响应体", responseBody) + .append("响应头", responseHeaders) + .append("耗时/时间", + StringUtils.format( + "{}MS-{}-{}", + duration, + startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), + endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))) + ) + .toString(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java b/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java new file mode 100644 index 0000000..bd12aad --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java @@ -0,0 +1,25 @@ +package com.muyu.gateway.model.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author DongZl + * @description: 验证码 + * @Date 2023-11-12 下午 03:36 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CaptchaCodeResp { + + private boolean captchaEnabled; + + private String uuid; + + private String img; + +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java b/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java new file mode 100644 index 0000000..25ed94e --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java @@ -0,0 +1,23 @@ +package com.muyu.gateway.service; + +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.domain.Result; + +import java.io.IOException; + +/** + * 验证码处理 + * + * @author muyu + */ +public interface ValidateCodeService { + /** + * 生成验证码 + */ + public Result createCaptcha () throws IOException, CaptchaException; + + /** + * 校验验证码 + */ + public void checkCaptcha (String key, String value) throws CaptchaException; +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java b/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java new file mode 100644 index 0000000..879d887 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java @@ -0,0 +1,109 @@ +package com.muyu.gateway.service.impl; + +import com.google.code.kaptcha.Producer; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.sign.Base64; +import com.muyu.common.core.utils.uuid.IdUtils; +import com.muyu.common.core.domain.Result; +import com.muyu.common.redis.service.RedisService; +import com.muyu.gateway.config.properties.CaptchaProperties; +import com.muyu.gateway.model.resp.CaptchaCodeResp; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.FastByteArrayOutputStream; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * 验证码实现处理 + * + * @author muyu + */ +@Service +public class ValidateCodeServiceImpl implements ValidateCodeService { + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisService redisService; + + @Autowired + private CaptchaProperties captchaProperties; + + /** + * 生成验证码 + */ + @Override + public Result createCaptcha () throws IOException, CaptchaException { + boolean captchaEnabled = captchaProperties.getEnabled(); + CaptchaCodeResp.CaptchaCodeRespBuilder respBuilder = CaptchaCodeResp.builder() + .captchaEnabled(captchaEnabled); + if (!captchaEnabled) { + return Result.success(respBuilder); + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + String captchaType = captchaProperties.getType(); + // 生成验证码 + if ("math".equals(captchaType)) { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } else if ("char".equals(captchaType)) { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try { + ImageIO.write(image, "jpg", os); + } catch (IOException e) { + return Result.error(e.getMessage()); + } + + CaptchaCodeResp captchaCodeResp = respBuilder.uuid(uuid) + .img(Base64.encode(os.toByteArray())) + .build(); + return Result.success(captchaCodeResp); + } + + /** + * 校验验证码 + */ + @Override + public void checkCaptcha (String code, String uuid) throws CaptchaException { + if (StringUtils.isEmpty(code)) { + throw new CaptchaException("验证码不能为空"); + } + if (StringUtils.isEmpty(uuid)) { + throw new CaptchaException("验证码已失效"); + } + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + String captcha = redisService.getCacheObject(verifyKey); + redisService.deleteObject(verifyKey); + + if (!code.equalsIgnoreCase(captcha)) { + throw new CaptchaException("验证码错误"); + } + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java b/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java new file mode 100644 index 0000000..2443e13 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java @@ -0,0 +1,113 @@ +package com.muyu.gateway.utils; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.ArrayUtil; +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.log4j.Log4j2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Web 工具类 + * + * + */ +@Log4j2 +public class WebFrameworkUtils { + + private static final String HEADER_TENANT_ID = "tenant-id"; + + private WebFrameworkUtils() {} + + /** + * 将 Gateway 请求中的 header,设置到 HttpHeaders 中 + * + * @param tenantId 租户编号 + * @param httpHeaders WebClient 的请求 + */ + public static void setTenantIdHeader(Long tenantId, HttpHeaders httpHeaders) { + if (tenantId == null) { + return; + } + httpHeaders.set(HEADER_TENANT_ID, String.valueOf(tenantId)); + } + + public static Long getTenantId(ServerWebExchange exchange) { + String tenantId = exchange.getRequest().getHeaders().getFirst(HEADER_TENANT_ID); + return tenantId != null ? Long.parseLong(tenantId) : null; + } + + /** + * 返回 JSON 字符串 + * + * @param exchange 响应 + * @param object 对象,会序列化成 JSON 字符串 + */ + @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 + public static Mono writeJSON(ServerWebExchange exchange, Object object) { + // 设置 header + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + // 设置 body + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + try { + return bufferFactory.wrap(JSONObject.toJSONString(object).getBytes()); + } catch (Exception ex) { + ServerHttpRequest request = exchange.getRequest(); + log.error("[writeJSON][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex); + return bufferFactory.wrap(new byte[0]); + } + })); + } + + /** + * 获得客户端 IP + * + * + * @param exchange 请求 + * @param otherHeaderNames 其它 header 名字的数组 + * @return 客户端 IP + */ + public static String getClientIP(ServerWebExchange exchange, String... otherHeaderNames) { + String[] headers = { "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" }; + if (ArrayUtil.isNotEmpty(otherHeaderNames)) { + headers = ArrayUtil.addAll(headers, otherHeaderNames); + } + // 方式一,通过 header 获取 + String ip; + for (String header : headers) { + ip = exchange.getRequest().getHeaders().getFirst(header); + if (!NetUtil.isUnknown(ip)) { + return NetUtil.getMultistageReverseProxyIp(ip); + } + } + + // 方式二,通过 remoteAddress 获取 + if (exchange.getRequest().getRemoteAddress() == null) { + return null; + } + ip = exchange.getRequest().getRemoteAddress().getHostString(); + return NetUtil.getMultistageReverseProxyIp(ip); + } + + /** + * 获得请求匹配的 Route 路由 + * + * @param exchange 请求 + * @return 路由 + */ + public static Route getGatewayRoute(ServerWebExchange exchange) { + return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); + } + +} diff --git a/cloud-gateway/src/main/resources/banner.txt b/cloud-gateway/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-gateway/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-gateway/src/main/resources/bootstrap.yml b/cloud-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..d942aa8 --- /dev/null +++ b/cloud-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,75 @@ +# Tomcat +server: + port: 8080 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud + +# Spring +spring: + application: + # 应用名称 + name: cloud-gateway + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + sentinel: + # 取消控制台懒加载 + eager: true + transport: + # 控制台地址 + dashboard: 127.0.0.1:8718 + # nacos配置持久化 + datasource: + ds1: + nacos: + server-addr: ${nacos.addr} + dataId: sentinel-cloud-gateway + groupId: DEFAULT_GROUP + namespace: ${nacos.namespace} + data-type: json + rule-type: gw-flow +knife4j: + gateway: + enabled: true + # 指定服务发现的模式聚合微服务文档,并且是默认`default`分组 + strategy: discover + discover: + enabled: true + # 指定版本号(Swagger2|OpenAPI3) + version : openapi3 + # 需要排除的微服务(eg:网关服务) + excluded-services: + - cloud-monitor diff --git a/cloud-gateway/src/main/resources/logback/dev.xml b/cloud-gateway/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..5ac21d0 --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-gateway/src/main/resources/logback/prod.xml b/cloud-gateway/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-gateway/src/main/resources/logback/test.xml b/cloud-gateway/src/main/resources/logback/test.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/pom.xml b/cloud-modules/cloud-modules-file/pom.xml new file mode 100644 index 0000000..e151176 --- /dev/null +++ b/cloud-modules/cloud-modules-file/pom.xml @@ -0,0 +1,82 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-file + + + cloud-modules-file文件服务 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.minio + minio + ${minio.version} + + + + + com.muyu + cloud-common-system + + + + + com.muyu + cloud-common-api-doc + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java new file mode 100644 index 0000000..4e1c628 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java @@ -0,0 +1,17 @@ +package com.muyu.file; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 文件服务 + * + * @author muyu + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudFileApplication { + public static void main (String[] args) { + SpringApplication.run(CloudFileApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java new file mode 100644 index 0000000..af8c1bb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java @@ -0,0 +1,72 @@ +package com.muyu.file.config; + +import io.minio.MinioClient; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Minio 配置信息 + * + * @author muyu + */ +@Configuration +@ConfigurationProperties(prefix = "minio") +public class MinioConfig { + /** + * 服务地址 + */ + private String url; + + /** + * 用户名 + */ + private String accessKey; + + /** + * 密码 + */ + private String secretKey; + + /** + * 存储桶名称 + */ + private String bucketName; + + public String getUrl () { + return url; + } + + public void setUrl (String url) { + this.url = url; + } + + public String getAccessKey () { + return accessKey; + } + + public void setAccessKey (String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey () { + return secretKey; + } + + public void setSecretKey (String secretKey) { + this.secretKey = secretKey; + } + + public String getBucketName () { + return bucketName; + } + + public void setBucketName (String bucketName) { + this.bucketName = bucketName; + } + + @Bean + public MinioClient getMinioClient () { + return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java new file mode 100644 index 0000000..78a8be0 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java @@ -0,0 +1,48 @@ +package com.muyu.file.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; + +/** + * 通用映射配置 + * + * @author muyu + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + @Override + public void addResourceHandlers (ResourceHandlerRegistry registry) { + /** 本地文件上传路径 */ + registry.addResourceHandler(localFilePrefix + "/**") + .addResourceLocations("file:" + localFilePath + File.separator); + } + + /** + * 开启跨域 + */ + @Override + public void addCorsMappings (CorsRegistry registry) { + // 设置允许跨域的路由 + registry.addMapping(localFilePrefix + "/**") + // 设置允许跨域请求的域名 + .allowedOrigins("*") + // 设置允许的方法 + .allowedMethods("GET"); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java new file mode 100644 index 0000000..ed04d11 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java @@ -0,0 +1,44 @@ +package com.muyu.file.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.file.FileUtils; +import com.muyu.file.service.ISysFileService; +import com.muyu.common.system.domain.SysFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件请求处理 + * + * @author muyu + */ +@RestController +public class SysFileController { + private static final Logger log = LoggerFactory.getLogger(SysFileController.class); + + @Autowired + private ISysFileService sysFileService; + + /** + * 文件上传请求 + */ + @PostMapping("/upload") + public Result upload (@RequestPart(value = "file") MultipartFile file) { + try { + // 上传并返回访问地址 + String url = sysFileService.uploadFile(file); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + return Result.success(sysFile); + } catch (Exception e) { + log.error("上传文件失败", e); + return Result.error(e.getMessage()); + } + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java new file mode 100644 index 0000000..416c26c --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java @@ -0,0 +1,21 @@ +package com.muyu.file.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传接口 + * + * @author muyu + */ +public interface ISysFileService { + /** + * 文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + public String uploadFile (MultipartFile file) throws Exception; +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java new file mode 100644 index 0000000..f3ce089 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.muyu.file.service; + +import com.muyu.file.utils.FileUploadUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * 本地文件存储 + * + * @author muyu + */ +@Primary +@Service +public class LocalSysFileServiceImpl implements ISysFileService { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + @Value("${file.domain}") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String name = FileUploadUtils.upload(localFilePath, file); + String url = domain + localFilePrefix + name; + return url; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java new file mode 100644 index 0000000..9a50de4 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.muyu.file.service; + +import com.alibaba.nacos.common.utils.IoUtils; +import com.muyu.file.config.MinioConfig; +import com.muyu.file.utils.FileUploadUtils; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * Minio 文件存储 + * + * @author muyu + */ +@Service +public class MinioSysFileServiceImpl implements ISysFileService { + @Autowired + private MinioConfig minioConfig; + + @Autowired + private MinioClient client; + + /** + * Minio文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String fileName = FileUploadUtils.extractFilename(file); + InputStream inputStream = file.getInputStream(); + PutObjectArgs args = PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(fileName) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build(); + client.putObject(args); + IoUtils.closeQuietly(inputStream); + return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java new file mode 100644 index 0000000..0a48cbf --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java @@ -0,0 +1,163 @@ +package com.muyu.file.utils; + +import com.muyu.common.core.exception.file.FileException; +import com.muyu.common.core.exception.file.FileNameLengthLimitExceededException; +import com.muyu.common.core.exception.file.FileSizeLimitExceededException; +import com.muyu.common.core.exception.file.InvalidExtensionException; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.MimeTypeUtils; +import com.muyu.common.core.utils.uuid.Seq; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +/** + * 文件上传工具类 + * + * @author muyu + */ +public class FileUploadUtils { + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * + * @return 文件名称 + * + * @throws IOException + */ + public static final String upload (String baseDir, MultipartFile file) throws IOException { + try { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (FileException fe) { + throw new IOException(fe.getDefaultMessage(), fe); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * + * @return 返回上传成功的文件名 + * + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload (String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename (MultipartFile file) { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), FileTypeUtils.getExtension(file)); + } + + private static final File getAbsoluteFile (String uploadDir, String fileName) throws IOException { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) { + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + } + return desc.isAbsolute() ? desc : desc.getAbsoluteFile(); + } + + private static final String getPathFileName (String fileName) throws IOException { + String pathFileName = "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final void assertAllowed (MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = FileTypeUtils.getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } else { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension 上传文件类型 + * @param allowedExtension 允许上传文件类型 + * + * @return true/false + */ + public static final boolean isAllowedExtension (String extension, String[] allowedExtension) { + for (String str : allowedExtension) { + if (str.equalsIgnoreCase(extension)) { + return true; + } + } + return false; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/resources/banner.txt b/cloud-modules/cloud-modules-file/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-file/src/main/resources/bootstrap.yml b/cloud-modules/cloud-modules-file/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..5e13a37 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/bootstrap.yml @@ -0,0 +1,47 @@ +# Tomcat +server: + port: 9300 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud + +# Spring +spring: + application: + # 应用名称 + name: cloud-file + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..30606f7 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..151a3cb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml new file mode 100644 index 0000000..151a3cb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/pom.xml b/cloud-modules/cloud-modules-gen/pom.xml new file mode 100644 index 0000000..c5de3a6 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/pom.xml @@ -0,0 +1,93 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-gen + + + cloud-modules-gen代码生成 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.apache.velocity + velocity-engine-core + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-xxl + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java new file mode 100644 index 0000000..da3eb43 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java @@ -0,0 +1,20 @@ +package com.muyu.gen; + +import com.muyu.common.security.annotation.EnableCustomConfig; +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 代码生成 + * + * @author muyu + */ +@EnableCustomConfig +@EnableMyFeignClients +@SpringBootApplication +public class CloudGenApplication { + public static void main (String[] args) { + SpringApplication.run(CloudGenApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java new file mode 100644 index 0000000..65d9824 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java @@ -0,0 +1,65 @@ +package com.muyu.gen.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 代码生成相关配置 + * + * @author muyu + */ +@Component +@ConfigurationProperties(prefix = "gen") +public class GenConfig { + /** + * 作者 + */ + public static String author; + + /** + * 生成包路径 + */ + public static String packageName; + + /** + * 自动去除表前缀,默认是false + */ + public static boolean autoRemovePre; + + /** + * 表前缀(类名不会包含表前缀) + */ + public static String tablePrefix; + + public static String getAuthor () { + return author; + } + + public void setAuthor (String author) { + GenConfig.author = author; + } + + public static String getPackageName () { + return packageName; + } + + public void setPackageName (String packageName) { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre () { + return autoRemovePre; + } + + public void setAutoRemovePre (boolean autoRemovePre) { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix () { + return tablePrefix; + } + + public void setTablePrefix (String tablePrefix) { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java new file mode 100644 index 0000000..1dfe039 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java @@ -0,0 +1,193 @@ +package com.muyu.gen.controller; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.service.IGenTableColumnService; +import com.muyu.gen.service.IGenTableService; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author muyu + */ +@RequestMapping("/gen") +@RestController +public class GenController extends BaseController { + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @RequiresPermissions("tool:gen:list") + @GetMapping("/list") + public Result> genList (GenTable genTable) { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @RequiresPermissions("tool:gen:query") + @GetMapping(value = "/{tableId}") + public Result getInfo (@PathVariable("tableId") Long tableId) { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @RequiresPermissions("tool:gen:list") + @GetMapping("/db/list") + public Result> dataList (GenTable genTable) { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @GetMapping(value = "/column/{tableId}") + public Result> columnList (Long tableId) { + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + return Result.success( + TableDataInfo.builder() + .total(list.size()) + .rows(list) + .build() + ); + } + + /** + * 导入表结构(保存) + */ + @RequiresPermissions("tool:gen:import") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public Result importTableSave (String tables) { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return success(); + } + + /** + * 修改保存代码生成业务 + */ + @RequiresPermissions("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public Result editSave (@Validated @RequestBody GenTable genTable) { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @RequiresPermissions("tool:gen:remove") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public Result remove (@PathVariable("tableIds") Long[] tableIds) { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @RequiresPermissions("tool:gen:preview") + @GetMapping("/preview/{tableId}") + public Result preview (@PathVariable("tableId") Long tableId) throws IOException { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download (HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public Result genCode (@PathVariable("tableName") String tableName) { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @RequiresPermissions("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public Result synchDb (@PathVariable("tableName") String tableName) { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode (HttpServletResponse response, String tables) throws IOException { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode (HttpServletResponse response, byte[] data) throws IOException { + response.reset(); + response.setHeader("Content-Disposition", "attachment; filename=\"muyu.zip\""); + response.addHeader("Content-Length", String.valueOf(data.length)); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java new file mode 100644 index 0000000..edb6510 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java @@ -0,0 +1,376 @@ +package com.muyu.gen.domain; + +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.ArrayUtils; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import java.util.List; + + + + +/** + * 业务表 gen_table + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class GenTable extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + private Long tableId; + + /** + * 表名称 + */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 表描述 + */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** + * 关联父表的表名 + */ + private String subTableName; + + /** + * 本表关联父表的外键名 + */ + private String subTableFkName; + + /** + * 实体类名称(首字母大写) + */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** + * 使用的模板(crud单表操作 tree树表操作 sub主子表操作) + */ + private String tplCategory; + + /** + * 生成包路径 + */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** + * 生成模块名 + */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** + * 生成业务名 + */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** + * 生成功能名 + */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** + * 生成作者 + */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** + * 生成代码方式(0zip压缩包 1自定义路径) + */ + private String genType; + + /** + * 生成路径(不填默认项目路径) + */ + private String genPath; + + /** + * 主键信息 + */ + private GenTableColumn pkColumn; + + /** + * 子表信息 + */ + private GenTable subTable; + + /** + * 表列信息 + */ + @Valid + private List columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 树编码字段 + */ + private String treeCode; + + /** + * 树父编码字段 + */ + private String treeParentCode; + + /** + * 树名称字段 + */ + private String treeName; + + /** + * 上级菜单ID字段 + */ + private String parentMenuId; + + /** + * 上级菜单名称字段 + */ + private String parentMenuName; + + public static boolean isSub (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public static boolean isTree (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public static boolean isCrud (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public static boolean isSuperColumn (String tplCategory, String javaField) { + if (isTree(tplCategory)) { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } + + public Long getTableId () { + return tableId; + } + + public void setTableId (Long tableId) { + this.tableId = tableId; + } + + public String getTableName () { + return tableName; + } + + public void setTableName (String tableName) { + this.tableName = tableName; + } + + public String getTableComment () { + return tableComment; + } + + public void setTableComment (String tableComment) { + this.tableComment = tableComment; + } + + public String getSubTableName () { + return subTableName; + } + + public void setSubTableName (String subTableName) { + this.subTableName = subTableName; + } + + public String getSubTableFkName () { + return subTableFkName; + } + + public void setSubTableFkName (String subTableFkName) { + this.subTableFkName = subTableFkName; + } + + public String getClassName () { + return className; + } + + public void setClassName (String className) { + this.className = className; + } + + public String getTplCategory () { + return tplCategory; + } + + public void setTplCategory (String tplCategory) { + this.tplCategory = tplCategory; + } + + public String getPackageName () { + return packageName; + } + + public void setPackageName (String packageName) { + this.packageName = packageName; + } + + public String getModuleName () { + return moduleName; + } + + public void setModuleName (String moduleName) { + this.moduleName = moduleName; + } + + public String getBusinessName () { + return businessName; + } + + public void setBusinessName (String businessName) { + this.businessName = businessName; + } + + public String getFunctionName () { + return functionName; + } + + public void setFunctionName (String functionName) { + this.functionName = functionName; + } + + public String getFunctionAuthor () { + return functionAuthor; + } + + public void setFunctionAuthor (String functionAuthor) { + this.functionAuthor = functionAuthor; + } + + public String getGenType () { + return genType; + } + + public void setGenType (String genType) { + this.genType = genType; + } + + public String getGenPath () { + return genPath; + } + + public void setGenPath (String genPath) { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn () { + return pkColumn; + } + + public void setPkColumn (GenTableColumn pkColumn) { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable () { + return subTable; + } + + public void setSubTable (GenTable subTable) { + this.subTable = subTable; + } + + public List getColumns () { + return columns; + } + + public void setColumns (List columns) { + this.columns = columns; + } + + public String getOptions () { + return options; + } + + public void setOptions (String options) { + this.options = options; + } + + public String getTreeCode () { + return treeCode; + } + + public void setTreeCode (String treeCode) { + this.treeCode = treeCode; + } + + public String getTreeParentCode () { + return treeParentCode; + } + + public void setTreeParentCode (String treeParentCode) { + this.treeParentCode = treeParentCode; + } + + public String getTreeName () { + return treeName; + } + + public void setTreeName (String treeName) { + this.treeName = treeName; + } + + public String getParentMenuId () { + return parentMenuId; + } + + public void setParentMenuId (String parentMenuId) { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName () { + return parentMenuName; + } + + public void setParentMenuName (String parentMenuName) { + this.parentMenuName = parentMenuName; + } + + public boolean isSub () { + return isSub(this.tplCategory); + } + + public boolean isTree () { + return isTree(this.tplCategory); + } + + public boolean isCrud () { + return isCrud(this.tplCategory); + } + + public boolean isSuperColumn (String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java new file mode 100644 index 0000000..98e4c28 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java @@ -0,0 +1,358 @@ +package com.muyu.gen.domain; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class GenTableColumn extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + private Long columnId; + + /** + * 归属表编号 + */ + private Long tableId; + + /** + * 列名称 + */ + private String columnName; + + /** + * 列描述 + */ + private String columnComment; + + /** + * 列类型 + */ + private String columnType; + + /** + * JAVA类型 + */ + private String javaType; + + /** + * JAVA字段名 + */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** + * 是否主键(1是) + */ + private String isPk; + + /** + * 是否自增(1是) + */ + private String isIncrement; + + /** + * 是否必填(1是) + */ + private String isRequired; + + /** + * 是否为插入字段(1是) + */ + private String isInsert; + + /** + * 是否编辑字段(1是) + */ + private String isEdit; + + /** + * 是否列表字段(1是) + */ + private String isList; + + /** + * 是否查询字段(1是) + */ + private String isQuery; + + /** + * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) + */ + private String queryType; + + /** + * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) + */ + private String htmlType; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 排序 + */ + private Integer sort; + + public static boolean isSuperColumn (String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public static boolean isUsableColumn (String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public Long getColumnId () { + return columnId; + } + + public void setColumnId (Long columnId) { + this.columnId = columnId; + } + + public Long getTableId () { + return tableId; + } + + public void setTableId (Long tableId) { + this.tableId = tableId; + } + + public String getColumnName () { + return columnName; + } + + public void setColumnName (String columnName) { + this.columnName = columnName; + } + + public String getColumnComment () { + return columnComment; + } + + public void setColumnComment (String columnComment) { + this.columnComment = columnComment; + } + + public String getColumnType () { + return columnType; + } + + public void setColumnType (String columnType) { + this.columnType = columnType; + } + + public String getJavaType () { + return javaType; + } + + public void setJavaType (String javaType) { + this.javaType = javaType; + } + + public String getJavaField () { + return javaField; + } + + public void setJavaField (String javaField) { + this.javaField = javaField; + } + + public String getCapJavaField () { + return StringUtils.capitalize(javaField); + } + + public String getIsPk () { + return isPk; + } + + public void setIsPk (String isPk) { + this.isPk = isPk; + } + + public boolean isPk () { + return isPk(this.isPk); + } + + public boolean isPk (String isPk) { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement () { + return isIncrement; + } + + public void setIsIncrement (String isIncrement) { + this.isIncrement = isIncrement; + } + + public boolean isIncrement () { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement (String isIncrement) { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public String getIsRequired () { + return isRequired; + } + + public void setIsRequired (String isRequired) { + this.isRequired = isRequired; + } + + public boolean isRequired () { + return isRequired(this.isRequired); + } + + public boolean isRequired (String isRequired) { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public String getIsInsert () { + return isInsert; + } + + public void setIsInsert (String isInsert) { + this.isInsert = isInsert; + } + + public boolean isInsert () { + return isInsert(this.isInsert); + } + + public boolean isInsert (String isInsert) { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public String getIsEdit () { + return isEdit; + } + + public void setIsEdit (String isEdit) { + this.isEdit = isEdit; + } + + public boolean isEdit () { + return isInsert(this.isEdit); + } + + public boolean isEdit (String isEdit) { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public String getIsList () { + return isList; + } + + public void setIsList (String isList) { + this.isList = isList; + } + + public boolean isList () { + return isList(this.isList); + } + + public boolean isList (String isList) { + return isList != null && StringUtils.equals("1", isList); + } + + public String getIsQuery () { + return isQuery; + } + + public void setIsQuery (String isQuery) { + this.isQuery = isQuery; + } + + public boolean isQuery () { + return isQuery(this.isQuery); + } + + public boolean isQuery (String isQuery) { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public String getQueryType () { + return queryType; + } + + public void setQueryType (String queryType) { + this.queryType = queryType; + } + + public String getHtmlType () { + return htmlType; + } + + public void setHtmlType (String htmlType) { + this.htmlType = htmlType; + } + + public String getDictType () { + return dictType; + } + + public void setDictType (String dictType) { + this.dictType = dictType; + } + + public Integer getSort () { + return sort; + } + + public void setSort (Integer sort) { + this.sort = sort; + } + + public boolean isSuperColumn () { + return isSuperColumn(this.javaField); + } + + public boolean isUsableColumn () { + return isUsableColumn(javaField); + } + + public String readConverterExp () { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) { + for (String value : remarks.split(" ")) { + if (StringUtils.isNotEmpty(value)) { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..5825d59 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java @@ -0,0 +1,67 @@ +package com.muyu.gen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.gen.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 数据层 + * + * @author muyu + */ +public interface GenTableColumnMapper extends BaseMapper { + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * + * @return 列信息 + */ + List selectDbTableColumnsByName (String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId (Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + int insertGenTableColumn (GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + int updateGenTableColumn (GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * + * @return 结果 + */ + int deleteGenTableColumns (List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteGenTableColumnByIds (Long[] ids); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java new file mode 100644 index 0000000..66858cb --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java @@ -0,0 +1,92 @@ +package com.muyu.gen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.gen.domain.GenTable; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author muyu + */ +public interface GenTableMapper extends BaseMapper { + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * + * @return 业务集合 + */ + List selectGenTableList (GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * + * @return 数据库表集合 + */ + List selectDbTableList (GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * + * @return 数据库表集合 + */ + List selectDbTableListByNames (String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll (); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * + * @return 业务信息 + */ + GenTable selectGenTableById (Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * + * @return 业务信息 + */ + GenTable selectGenTableByName (String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * + * @return 结果 + */ + int insertGenTable (GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * + * @return 结果 + */ + int updateGenTable (GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteGenTableByIds (Long[] ids); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..895b1ed --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java @@ -0,0 +1,68 @@ +package com.muyu.gen.service; + +import com.muyu.common.core.text.Convert; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.mapper.GenTableColumnMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 业务字段 服务层实现 + * + * @author muyu + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService { + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId (Long tableId) { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + @Override + public int insertGenTableColumn (GenTableColumn genTableColumn) { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + @Override + public int updateGenTableColumn (GenTableColumn genTableColumn) { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds (String ids) { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java new file mode 100644 index 0000000..6273779 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java @@ -0,0 +1,465 @@ +package com.muyu.gen.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.CharsetKit; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.mapper.GenTableColumnMapper; +import com.muyu.gen.mapper.GenTableMapper; +import com.muyu.gen.util.GenUtils; +import com.muyu.gen.util.VelocityInitializer; +import com.muyu.gen.util.VelocityUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author muyu + */ +@Service +public class GenTableServiceImpl implements IGenTableService { + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * + * @return 生成地址 + */ + public static String getGenPath (GenTable table, String template) { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } + + /** + * 查询业务信息 + * + * @param id 业务ID + * + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById (Long id) { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * + * @return 业务集合 + */ + @Override + public List selectGenTableList (GenTable genTable) { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * + * @return 数据库表集合 + */ + @Override + public List selectDbTableList (GenTable genTable) { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames (String[] tableNames) { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll () { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void updateGenTable (GenTable genTable) { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.updateGenTableColumn(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteGenTableByIds (Long[] tableIds) { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void importGenTable (List tableList) { + String operName = SecurityUtils.getUsername(); + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } catch (Exception e) { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * + * @return 预览数据列表 + */ + @Override + public Map previewCode (Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * + * @return 数据 + */ + @Override + public byte[] downloadCode (String tableName) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode (String tableName) { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } catch (IOException e) { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void synchDb (String tableName) { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } else { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * + * @return 数据 + */ + @Override + public byte[] downloadCode (String[] tableNames) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode (String tableName, ZipOutputStream zip) { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit (GenTable genTable) { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) { + throw new ServiceException("树编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) { + throw new ServiceException("树父编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) { + throw new ServiceException("树名称字段不能为空"); + } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { + if (StringUtils.isEmpty(genTable.getSubTableName())) { + throw new ServiceException("关联子表的表名不能为空"); + } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn (GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) { + for (GenTableColumn column : table.getSubTable().getColumns()) { + if (column.isPk()) { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable (GenTable table) { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions (GenTable genTable) { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java new file mode 100644 index 0000000..b60ea57 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java @@ -0,0 +1,48 @@ +package com.muyu.gen.service; + +import com.muyu.gen.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 服务层 + * + * @author muyu + */ +public interface IGenTableColumnService { + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId (Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + int insertGenTableColumn (GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * + * @return 结果 + */ + int updateGenTableColumn (GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteGenTableColumnByIds (String ids); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java new file mode 100644 index 0000000..f34dc37 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java @@ -0,0 +1,131 @@ +package com.muyu.gen.service; + +import com.muyu.gen.domain.GenTable; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author muyu + */ +public interface IGenTableService { + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * + * @return 业务集合 + */ + List selectGenTableList (GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * + * @return 数据库表集合 + */ + List selectDbTableList (GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * + * @return 数据库表集合 + */ + List selectDbTableListByNames (String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll (); + + /** + * 查询业务信息 + * + * @param id 业务ID + * + * @return 业务信息 + */ + GenTable selectGenTableById (Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * + * @return 结果 + */ + void updateGenTable (GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * + * @return 结果 + */ + void deleteGenTableByIds (Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + void importGenTable (List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * + * @return 预览数据列表 + */ + Map previewCode (Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * + * @return 数据 + */ + byte[] downloadCode (String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * + * @return 数据 + */ + void generatorCode (String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + void synchDb (String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * + * @return 数据 + */ + byte[] downloadCode (String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + void validateEdit (GenTable genTable); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java new file mode 100644 index 0000000..5aea1f2 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java @@ -0,0 +1,229 @@ +package com.muyu.gen.util; + +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gen.config.GenConfig; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import org.apache.commons.lang3.RegExUtils; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author muyu + */ +public class GenUtils { + /** + * 初始化表信息 + */ + public static void initTable (GenTable genTable, String operName) { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField (GenTableColumn column, GenTable table) { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * + * @return 是否包含 + */ + public static boolean arraysContains (String[] arr, String targetValue) { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * + * @return 模块名 + */ + public static String getModuleName (String packageName) { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * + * @return 业务名 + */ + public static String getBusinessName (String tableName) { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * + * @return 类名 + */ + public static String convertClassName (String tableName) { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * + * @return + */ + public static String replaceFirst (String replacementm, String[] searchList) { + String text = replacementm; + for (String searchString : searchList) { + if (replacementm.startsWith(searchString)) { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * + * @return 替换后的名字 + */ + public static String replaceText (String text) { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * + * @return 截取后的列类型 + */ + public static String getDbType (String columnType) { + if (StringUtils.indexOf(columnType, "(") > 0) { + return StringUtils.substringBefore(columnType, "("); + } else { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * + * @return 截取后的列类型 + */ + public static Integer getColumnLength (String columnType) { + if (StringUtils.indexOf(columnType, "(") > 0) { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } else { + return 0; + } + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java new file mode 100644 index 0000000..cc171b4 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java @@ -0,0 +1,30 @@ +package com.muyu.gen.util; + +import com.muyu.common.core.constant.Constants; +import org.apache.velocity.app.Velocity; + +import java.util.Properties; + +/** + * VelocityEngine工厂 + * + * @author muyu + */ +public class VelocityInitializer { + /** + * 初始化vm方法 + */ + public static void initVelocity () { + Properties p = new Properties(); + try { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java new file mode 100644 index 0000000..9305ff7 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java @@ -0,0 +1,357 @@ +package com.muyu.gen.util; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import org.apache.velocity.VelocityContext; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 模板工具类 + * + * @author muyu + */ +public class VelocityUtils { + /** + * 项目空间路径 + */ + private static final String PROJECT_PATH = "main/java"; + + /** + * mybatis空间路径 + */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** + * 默认上级菜单,系统工具 + */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext (GenTable genTable) { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext (VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext (VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext (VelocityContext context, GenTable genTable) { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList (String tplCategory) { + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + } else if (GenConstants.TPL_TREE.equals(tplCategory)) { + templates.add("vm/vue/index-tree.vue.vm"); + } else if (GenConstants.TPL_SUB.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName (String template, GenTable genTable) { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } else if (template.contains("mapper.java.vm")) { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } else if (template.contains("service.java.vm")) { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } else if (template.contains("serviceImpl.java.vm")) { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } else if (template.contains("controller.java.vm")) { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } else if (template.contains("mapper.xml.vm")) { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } else if (template.contains("sql.vm")) { + fileName = businessName + "Menu.sql"; + } else if (template.contains("api.js.vm")) { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } else if (template.contains("index.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } else if (template.contains("index-tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * + * @return 包前缀名称 + */ + public static String getPackagePrefix (String packageName) { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList (GenTable genTable) { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * + * @return 返回字典组 + */ + public static String getDicts (GenTable genTable) { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts (Set dicts, List columns) { + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[]{GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX})) { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * + * @return 返回权限前缀 + */ + public static String getPermissionPrefix (String moduleName, String businessName) { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * + * @return 上级菜单ID字段 + */ + public static String getParentMenuId (JSONObject paramsObj) { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * + * @return 树编码 + */ + public static String getTreecode (JSONObject paramsObj) { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * + * @return 树父编码 + */ + public static String getTreeParentCode (JSONObject paramsObj) { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * + * @return 树名称 + */ + public static String getTreeName (JSONObject paramsObj) { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * + * @return 展开按钮列序号 + */ + public static int getExpandColumn (GenTable genTable) { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) { + if (column.isList()) { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) { + break; + } + } + } + return num; + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt b/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/bootstrap.yml b/cloud-modules/cloud-modules-gen/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..e9427c3 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/bootstrap.yml @@ -0,0 +1,51 @@ +# Tomcat +server: + port: 9202 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud + +# Spring +spring: + main: + allow-bean-definition-overriding: true + application: + # 应用名称 + name: cloud-gen + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # xxl-job 配置文件 + - application-xxl-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..0b3964c --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..4a9d602 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml new file mode 100644 index 0000000..4a9d602 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..fb9800e --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time, + update_by, + update_time + from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..42ab2cf --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, + table_name, + table_comment, + sub_table_name, + sub_table_fk_name, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + options, + create_by, + create_time, + update_by, + update_time, + remark + from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..1df5be0 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,116 @@ +package ${packageName}.controller; + +import java.util.List; +import java.io.IOException; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.muyu.common.core.web.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${businessName}") +public class ${ClassName}Controller extends BaseController +{ + @Autowired + private I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @RequiresPermissions("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud || $table.sub) + public Result list(${ClassName} ${className}) + { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } +#elseif($table.tree) + public Result list(${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @RequiresPermissions("${permissionPrefix}:export") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @RequiresPermissions("${permissionPrefix}:query") + @GetMapping(value = "/{${pkColumn.javaField}}") + public Result getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) + { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @RequiresPermissions("${permissionPrefix}:add") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public Result add(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @RequiresPermissions("${permissionPrefix}:edit") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @RequiresPermissions("${permissionPrefix}:remove") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public Result remove(@PathVariable("${pkColumn.javaField}s") ${pkColumn.javaType}[] ${pkColumn.javaField}s) + { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..1292991 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,103 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import com.muyu.common.core.annotation.Excel; +#if($table.crud || $table.sub) +import com.muyu.common.core.web.domain.BaseEntity; +#elseif($table.tree) +import com.muyu.common.core.web.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..7e7d7c2 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..264882b --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..3904af5 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.muyu.common.core.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.muyu.common.core.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..a5c170c --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,79 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.web.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..a4c64a0 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..6296014 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..7bbd2fc --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..8b25665 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 0000000..9f60284 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +���ʹ�õ���cloud-Cloud-Vue3ǰ�ˣ���ô��Ҫ����һ�´�Ŀ¼��ģ��index.vue.vm��index-tree.vue.vm�ļ����ϼ�vueĿ¼�� diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..0ceb3d8 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/cloud-modules/cloud-modules-system/pom.xml b/cloud-modules/cloud-modules-system/pom.xml new file mode 100644 index 0000000..4116679 --- /dev/null +++ b/cloud-modules/cloud-modules-system/pom.xml @@ -0,0 +1,103 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-system + + + cloud-modules-system系统模块 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-datasource + + + + + com.muyu + cloud-common-datascope + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-xxl + + + + com.muyu + cloud-common-rabbit + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java new file mode 100644 index 0000000..b01e5db --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java @@ -0,0 +1,21 @@ +package com.muyu.system; + +import com.muyu.common.security.annotation.EnableCustomConfig; +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 系统模块 + * + * @author muyu + */ +@EnableCustomConfig +//@EnableCustomSwagger2 +@EnableMyFeignClients +@SpringBootApplication +public class CloudSystemApplication { + public static void main (String[] args) { + SpringApplication.run(CloudSystemApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java new file mode 100644 index 0000000..3bced52 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java @@ -0,0 +1,122 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysConfig; +import com.muyu.system.service.SysConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/config") +@Tag(name = "SysConfigController", description = "系统参数配置") +public class SysConfigController extends BaseController { + + @Autowired + private SysConfigService configService; + + /** + * 获取参数配置列表 + */ + @RequiresPermissions("system:config:list") + @GetMapping("/list") + @Operation(summary = "查询集合", description = "更新水果信息") + public Result> list (SysConfig config) { + startPage(); + List list = configService.pageQuery(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:config:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysConfig config) { + List list = configService.pageQuery(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @GetMapping(value = "/{configId}") + public Result getInfo (@PathVariable("configId") Long configId) { + return success(configService.getById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public Result getConfigKey (@PathVariable("configKey") String configKey) { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @RequiresPermissions("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysConfig config) { + if (!configService.checkConfigKeyUnique(config)) { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(SecurityUtils.getUsername()); + return toAjax(configService.save(config)); + } + + /** + * 修改参数配置 + */ + @RequiresPermissions("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysConfig config) { + if (!configService.checkConfigKeyUnique(config)) { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(configService.updateById(config)); + } + + /** + * 删除参数配置 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public Result remove (@PathVariable("configIds") Long[] configIds) { + configService.removeBatchByIds(Arrays.asList(configIds)); + return success(); + } + + /** + * 刷新参数缓存 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public Result refreshCache () { + configService.resetConfigCache(); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java new file mode 100644 index 0000000..37e48fc --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java @@ -0,0 +1,113 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.service.SysDeptService; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dept") +public class SysDeptController extends BaseController { + + @Autowired + private SysDeptService deptService; + + /** + * 获取部门列表 + */ + @RequiresPermissions("system:dept:list") + @GetMapping("/list") + public Result list (SysDept dept) { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @RequiresPermissions("system:dept:list") + @GetMapping("/list/exclude/{deptId}") + public Result excludeChild (@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @RequiresPermissions("system:dept:query") + @GetMapping(value = "/{deptId}") + public Result getInfo (@PathVariable("deptId") Long deptId) { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @RequiresPermissions("system:dept:add") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDept dept) { + if (!deptService.checkDeptNameUnique(dept)) { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(SecurityUtils.getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @RequiresPermissions("system:dept:edit") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysDept dept) { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } else if (dept.getParentId().equals(deptId)) { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @RequiresPermissions("system:dept:remove") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public Result remove (@PathVariable("deptId") Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java new file mode 100644 index 0000000..f9862d9 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java @@ -0,0 +1,107 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.system.service.SysDictDataService; +import com.muyu.system.service.SysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dict/data") +public class SysDictDataController extends BaseController { + @Autowired + private SysDictDataService dictDataService; + + @Autowired + private SysDictTypeService dictTypeService; + + @RequiresPermissions("system:dict:list") + @GetMapping("/list") + public Result> list (SysDictData dictData) { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysDictData dictData) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @RequiresPermissions("system:dict:query") + @GetMapping(value = "/{dictCode}") + public Result getInfo (@PathVariable("dictCode") Long dictCode) { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public Result dictType (@PathVariable("dictType") String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @RequiresPermissions("system:dict:add") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDictData dict) { + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @RequiresPermissions("system:dict:edit") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysDictData dict) { + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public Result remove (@PathVariable("dictCode") Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java new file mode 100644 index 0000000..c4a7b9f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java @@ -0,0 +1,115 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDictType; +import com.muyu.system.service.SysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 数据字典信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dict/type") +public class SysDictTypeController extends BaseController { + @Autowired + private SysDictTypeService dictTypeService; + + @RequiresPermissions("system:dict:list") + @GetMapping("/list") + public Result> list (SysDictType dictType) { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysDictType dictType) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @RequiresPermissions("system:dict:query") + @GetMapping(value = "/{dictId}") + public Result getInfo (@PathVariable("dictId") Long dictId) { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @RequiresPermissions("system:dict:add") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDictType dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @RequiresPermissions("system:dict:edit") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysDictType dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public Result remove (@PathVariable("dictIds") Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public Result refreshCache () { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public Result optionselect () { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java new file mode 100644 index 0000000..c1e92b7 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java @@ -0,0 +1,80 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.system.service.SysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 系统访问记录 + * + * @author muyu + */ +@RestController +@RequestMapping("/logininfor") +public class SysLogininforController extends BaseController { + @Autowired + private SysLogininforService logininforService; + + @Autowired + private RedisService redisService; + + @RequiresPermissions("system:logininfor:list") + @GetMapping("/list") + public Result> list (SysLogininfor logininfor) { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:logininfor:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysLogininfor logininfor) { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @RequiresPermissions("system:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public Result remove (@PathVariable("infoIds") Long[] infoIds) { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @RequiresPermissions("system:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/clean") + public Result clean () { + logininforService.cleanLogininfor(); + return success(); + } + + @RequiresPermissions("system:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public Result unlock (@PathVariable("userName") String userName) { + redisService.deleteObject(CacheConstants.PWD_ERR_CNT_KEY + userName); + return success(); + } + + @InnerAuth + @PostMapping + public Result add (@RequestBody SysLogininfor logininfor) { + return toAjax(logininforService.insertLogininfor(logininfor)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java new file mode 100644 index 0000000..e5ae348 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java @@ -0,0 +1,137 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.resp.RoleMenuTreeResp; +import com.muyu.system.service.SysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 菜单信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/menu") +public class SysMenuController extends BaseController { + @Autowired + private SysMenuService menuService; + + /** + * 获取菜单列表 + */ + @RequiresPermissions("system:menu:list") + @GetMapping("/list") + public Result list (SysMenu menu) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @RequiresPermissions("system:menu:query") + @GetMapping(value = "/{menuId}") + public Result getInfo (@PathVariable("menuId") Long menuId) { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public Result treeselect (SysMenu menu) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public Result roleMenuTreeselect (@PathVariable("roleId") Long roleId) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(userId); + return Result.success( + RoleMenuTreeResp.builder() + .menus(menuService.buildMenuTreeSelect(menus)) + .checkedKeys(menuService.selectMenuListByRoleId(roleId)) + .build() + ); + } + + /** + * 新增菜单 + */ + @RequiresPermissions("system:menu:add") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysMenu menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(SecurityUtils.getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @RequiresPermissions("system:menu:edit") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysMenu menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @RequiresPermissions("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public Result remove (@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public Result getRouters () { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return success(menuService.buildMenus(menus)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java new file mode 100644 index 0000000..e6b9ede --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java @@ -0,0 +1,80 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysNotice; +import com.muyu.system.service.SysNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公告 信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/notice") +public class SysNoticeController extends BaseController { + @Autowired + private SysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @RequiresPermissions("system:notice:list") + @GetMapping("/list") + public Result> list (SysNotice notice) { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @RequiresPermissions("system:notice:query") + @GetMapping(value = "/{noticeId}") + public Result getInfo (@PathVariable("noticeId") Long noticeId) { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @RequiresPermissions("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysNotice notice) { + notice.setCreateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @RequiresPermissions("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysNotice notice) { + notice.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @RequiresPermissions("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public Result remove (@PathVariable("noticeIds") Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java new file mode 100644 index 0000000..bb00d1d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java @@ -0,0 +1,67 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.system.service.SysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author muyu + */ +@RestController +@RequestMapping("/operlog") +public class SysOperlogController extends BaseController { + @Autowired + private SysOperLogService operLogService; + + @RequiresPermissions("system:operlog:list") + @GetMapping("/list") + public Result> list (SysOperLog operLog) { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:operlog:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysOperLog operLog) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @RequiresPermissions("system:operlog:remove") + @DeleteMapping("/{operIds}") + public Result remove (@PathVariable("operIds") Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @RequiresPermissions("system:operlog:remove") + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public Result clean () { + operLogService.cleanOperLog(); + return success(); + } + + @InnerAuth + @PostMapping + public Result add (@RequestBody SysOperLog operLog) { + return toAjax(operLogService.insertOperlog(operLog)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java new file mode 100644 index 0000000..79f3f47 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java @@ -0,0 +1,110 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysPost; +import com.muyu.system.service.SysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/post") +public class SysPostController extends BaseController { + @Autowired + private SysPostService postService; + + /** + * 获取岗位列表 + */ + @RequiresPermissions("system:post:list") + @GetMapping("/list") + public Result> list (SysPost post) { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:post:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysPost post) { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @RequiresPermissions("system:post:query") + @GetMapping(value = "/{postId}") + public Result getInfo (@PathVariable("postId") Long postId) { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @RequiresPermissions("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysPost post) { + if (!postService.checkPostNameUnique(post)) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(SecurityUtils.getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @RequiresPermissions("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysPost post) { + if (!postService.checkPostNameUnique(post)) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @RequiresPermissions("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public Result remove (@PathVariable("postIds") Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public Result optionselect () { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java new file mode 100644 index 0000000..ae49255 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java @@ -0,0 +1,134 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.MimeTypeUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.remote.RemoteFileService; +import com.muyu.common.system.domain.SysFile; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.resp.ProfileResp; +import com.muyu.system.service.SysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; + +/** + * 个人信息 业务处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/user/profile") +public class SysProfileController extends BaseController { + @Autowired + private SysUserService userService; + + @Autowired + private TokenService tokenService; + + @Autowired + private RemoteFileService remoteFileService; + + /** + * 个人信息 + */ + @GetMapping + public Result profile () { + String username = SecurityUtils.getUsername(); + SysUser user = userService.selectUserByUserName(username); + return Result.success( + ProfileResp.builder() + .roleGroup( userService.selectUserRoleGroup(username) ) + .postGroup( userService.selectUserPostGroup(username) ) + .sysUser(user) + .build() + ); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public Result updateProfile (@RequestBody SysUser user) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser currentUser = loginUser.getSysUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public Result updatePwd (String oldPassword, String newPassword) { + String username = SecurityUtils.getUsername(); + SysUser user = userService.selectUserByUserName(username); + String password = user.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(username, SecurityUtils.encryptPassword(newPassword)) > 0) { + // 更新缓存用户密码 + LoginUser loginUser = SecurityUtils.getLoginUser(); + loginUser.getSysUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public Result avatar (@RequestParam("avatarfile") MultipartFile file) { + if (!file.isEmpty()) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + String extension = FileTypeUtils.getExtension(file); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return error("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + Result fileResult = remoteFileService.upload(file); + if (StringUtils.isNull(fileResult) || StringUtils.isNull(fileResult.getData())) { + return error("文件服务异常,请联系管理员"); + } + String url = fileResult.getData().getUrl(); + if (userService.updateUserAvatar(loginUser.getUsername(), url)) { + // 更新缓存用户头像 + loginUser.getSysUser().setAvatar(url); + tokenService.setLoginUser(loginUser); + return Result.success(url); + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java new file mode 100644 index 0000000..f8b05ad --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java @@ -0,0 +1,215 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.domain.resp.DeptTreeResp; +import com.muyu.system.service.SysDeptService; +import com.muyu.system.service.SysRoleService; +import com.muyu.system.service.SysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 角色信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/role") +public class SysRoleController extends BaseController { + @Autowired + private SysRoleService roleService; + + @Autowired + private SysUserService userService; + + @Autowired + private SysDeptService deptService; + + @RequiresPermissions("system:role:list") + @GetMapping("/list") + public Result> list (SysRole role) { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:role:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysRole role) { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @RequiresPermissions("system:role:query") + @GetMapping(value = "/{roleId}") + public Result getInfo (@PathVariable("roleId") Long roleId) { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @RequiresPermissions("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysRole role) { + if (!roleService.checkRoleNameUnique(role)) { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(SecurityUtils.getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRole(role)); + } + + /** + * 修改保存数据权限 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public Result dataScope (@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public Result changeStatus (@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @RequiresPermissions("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public Result remove (@PathVariable("roleIds") Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @RequiresPermissions("system:role:query") + @GetMapping("/optionselect") + public Result optionselect () { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @GetMapping("/authUser/allocatedList") + public Result> allocatedList (SysUser user) { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @GetMapping("/authUser/unallocatedList") + public Result> unallocatedList (SysUser user) { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public Result cancelAuthUser (@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public Result cancelAuthUserAll (Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public Result selectAuthUserAll (Long roleId, Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @RequiresPermissions("system:role:query") + @GetMapping(value = "/deptTree/{roleId}") + public Result deptTree (@PathVariable("roleId") Long roleId) { + return Result.success( + DeptTreeResp.builder() + .depts(deptService.selectDeptTreeList(new SysDept())) + .checkedKeys(deptService.selectDeptListByRoleId(roleId)) + .build() + ); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java new file mode 100644 index 0000000..bc0b5fe --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java @@ -0,0 +1,293 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.resp.AuthRoleResp; +import com.muyu.system.domain.resp.UserDetailInfoResp; +import com.muyu.system.domain.resp.UserInfoResp; +import com.muyu.system.service.*; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/user") +public class SysUserController extends BaseController { + @Autowired + private SysUserService userService; + + @Autowired + private SysRoleService roleService; + + @Autowired + private SysDeptService deptService; + + @Autowired + private SysPostService postService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private SysConfigService configService; + + /** + * 获取用户列表 + */ + @RequiresPermissions("system:user:list") + @GetMapping("/list") + public Result> list (SysUser user) { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:user:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysUser user) { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @RequiresPermissions("system:user:import") + @PostMapping("/importData") + public Result importData (MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = SecurityUtils.getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate (HttpServletResponse response) throws IOException { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 获取当前用户信息 + */ + @InnerAuth + @GetMapping("/info/{username}") + public Result info (@PathVariable("username") String username) { + SysUser sysUser = userService.selectUserByUserName(username); + if (StringUtils.isNull(sysUser)) { + return Result.error("用户名或密码错误"); + } + // 角色集合 + Set roles = permissionService.getRolePermission(sysUser); + // 权限集合 + Set permissions = permissionService.getMenuPermission(sysUser); + LoginUser sysUserVo = new LoginUser(); + sysUserVo.setSysUser(sysUser); + sysUserVo.setRoles(roles); + sysUserVo.setPermissions(permissions); + return Result.success(sysUserVo); + } + + /** + * 注册用户信息 + */ + @InnerAuth + @PostMapping("/register") + public Result register (@RequestBody SysUser sysUser) { + String username = sysUser.getUserName(); + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) { + return Result.error("当前系统没有开启注册功能!"); + } + if (!userService.checkUserNameUnique(sysUser)) { + return Result.error("保存用户'" + username + "'失败,注册账号已存在"); + } + return Result.success(userService.registerUser(sysUser)); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public Result getInfo () { + SysUser user = userService.selectUserById(SecurityUtils.getUserId()); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + + return Result.success( + UserInfoResp.builder() + .user(user) + .roles(roles) + .permissions(permissions) + .build() + ); + } + + /** + * 根据用户编号获取详细信息 + */ + @RequiresPermissions("system:user:query") + @GetMapping(value = {"/", "/{userId}"}) + public Result getInfo (@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + UserDetailInfoResp.UserDetailInfoRespBuilder builder = UserDetailInfoResp.builder(); + List roles = roleService.selectRoleAll(); + builder.roles( + SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()) + ) + .posts( postService.selectPostAll() ); + if (StringUtils.isNotNull(userId)) { + SysUser sysUser = userService.selectUserById(userId); + builder.sysUser(sysUser) + .postIds(postService.selectPostListByUserId(userId)) + .roleIds(sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())) + .build(); + } + return Result.success(builder.build()); + } + + /** + * 新增用户 + */ + @RequiresPermissions("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysUser user) { + if (!userService.checkUserNameUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(SecurityUtils.getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @RequiresPermissions("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public Result remove (@PathVariable("userIds") Long[] userIds) { + if (ArrayUtils.contains(userIds, SecurityUtils.getUserId())) { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public Result resetPwd (@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public Result changeStatus (@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @RequiresPermissions("system:user:query") + @GetMapping("/authRole/{userId}") + public Result authRole (@PathVariable("userId") Long userId) { + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + return Result.success( + AuthRoleResp.builder() + .roles(SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())) + .user(user) + .build() + ); + } + + /** + * 用户授权角色 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public Result insertAuthRole (Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @RequiresPermissions("system:user:list") + @GetMapping("/deptTree") + public Result deptTree (SysDept dept) { + return success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java new file mode 100644 index 0000000..5b78721 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java @@ -0,0 +1,69 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; +import com.muyu.system.service.SysUserOnlineService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * 在线用户监控 + * + * @author muyu + */ +@RestController +@RequestMapping("/online") +public class SysUserOnlineController extends BaseController { + @Autowired + private SysUserOnlineService userOnlineService; + + @Autowired + private RedisService redisService; + + @RequiresPermissions("monitor:online:list") + @GetMapping("/list") + public Result> list (String ipaddr, String userName) { + Collection keys = redisService.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) { + LoginUser user = redisService.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } else if (StringUtils.isNotEmpty(ipaddr)) { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } else if (StringUtils.isNotEmpty(userName)) { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } else { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @RequiresPermissions("monitor:online:forceLogout") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public Result forceLogout (@PathVariable("tokenId") String tokenId) { + redisService.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java new file mode 100644 index 0000000..6f7e8bf --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java @@ -0,0 +1,81 @@ +package com.muyu.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 参数配置表 sys_config + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_config") +public class SysConfig extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 参数主键 + */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + @TableId( type = IdType.AUTO) + private Long configId; + + /** + * 参数名称 + */ + @Excel(name = "参数名称") + private String configName; + + /** + * 参数键名 + */ + @Excel(name = "参数键名") + private String configKey; + + /** + * 参数键值 + */ + @Excel(name = "参数键值") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName () { + return configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey () { + return configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue () { + return configValue; + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java new file mode 100644 index 0000000..e4ad4e4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java @@ -0,0 +1,138 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysMenu extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 父菜单名称 + */ + private String parentName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String query; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 类型(M目录 C菜单 F按钮) + */ + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限字符串 + */ + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 子菜单 + */ + @Builder.Default + private List children = new ArrayList(); + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName () { + return menuName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum () { + return orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath () { + return path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent () { + return component; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType () { + return menuType; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms () { + return perms; + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java new file mode 100644 index 0000000..a0c30e0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java @@ -0,0 +1,58 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.core.xss.Xss; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 通知公告表 sys_notice + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysNotice extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 公告ID + */ + private Long noticeId; + + /** + * 公告标题 + */ + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle () { + return noticeTitle; + } + } diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java new file mode 100644 index 0000000..326987c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java @@ -0,0 +1,78 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + * 岗位表 sys_post + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysPost extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 岗位序号 + */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + private Long postId; + + /** + * 岗位编码 + */ + @Excel(name = "岗位编码") + private String postCode; + + /** + * 岗位名称 + */ + @Excel(name = "岗位名称") + private String postName; + + /** + * 岗位排序 + */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 用户是否存在此岗位标识 默认不存在 + */ + @Builder.Default + private boolean flag = false; + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode () { + return postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName () { + return postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort () { + return postSort; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java new file mode 100644 index 0000000..ef9f13c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java @@ -0,0 +1,27 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 角色和部门关联 sys_role_dept + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysRoleDept { + /** + * 角色ID + */ + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..fe4074b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java @@ -0,0 +1,28 @@ +package com.muyu.system.domain; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysRoleMenu { + /** + * 角色ID + */ + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java new file mode 100644 index 0000000..23d1979 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java @@ -0,0 +1,52 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 当前在线会话 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserOnline { + /** + * 会话编号 + */ + private String tokenId; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java new file mode 100644 index 0000000..229dc70 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java @@ -0,0 +1,28 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户和岗位关联 sys_user_post + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserPost { + /** + * 用户ID + */ + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java new file mode 100644 index 0000000..ee9c945 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java @@ -0,0 +1,27 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户和角色关联 sys_user_role + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserRole { + /** + * 用户ID + */ + private Long userId; + + /** + * 角色ID + */ + private Long roleId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java new file mode 100644 index 0000000..69294b6 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java @@ -0,0 +1,33 @@ +package com.muyu.system.domain.resp; + + +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 授权角色返回结果集 + * @Date 2023-6-19 下午 02:50 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class AuthRoleResp { + + /** + * 用户信息 + */ + private SysUser user; + + /** + * 角色集合 + */ + private List roles; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java new file mode 100644 index 0000000..cd72807 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java @@ -0,0 +1,32 @@ +package com.muyu.system.domain.resp; + + +import com.muyu.system.domain.vo.TreeSelect; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 部门树返回结果集 + * @Date 2023-6-19 下午 02:52 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class DeptTreeResp { + + /** + * 授权的ID + */ + private List checkedKeys; + + /** + * 部门树 + */ + private List depts; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java new file mode 100644 index 0000000..21d9808 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java @@ -0,0 +1,34 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @author DongZl + * @description: 个人信息模型对象 + * @Date 2023-6-19 下午 02:05 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class ProfileResp { + + /** + * 系统用户 + */ + private SysUser sysUser; + + /** + * 用户权限组 + */ + private String roleGroup; + + /** + * 用户岗位组 + */ + private String postGroup; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java new file mode 100644 index 0000000..696763e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java @@ -0,0 +1,31 @@ +package com.muyu.system.domain.resp; + +import com.muyu.system.domain.vo.TreeSelect; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 角色返回菜单树 + * @Date 2023-6-19 下午 02:40 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class RoleMenuTreeResp { + + /** + * 拥有的菜单权限 + */ + private List checkedKeys; + + /** + * 系统所有的菜单 + */ + private List menus; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java new file mode 100644 index 0000000..4c61832 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java @@ -0,0 +1,47 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 用户详细信息 + * @Date 2023-6-19 下午 02:45 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class UserDetailInfoResp { + + /** + * 角色权限集合 + */ + private List roles; + + /** + * 岗位集合 + */ + private List posts; + + /** + * 用户信息 + */ + private SysUser sysUser; + + /** + * 用户又有的职位ID + */ + private List postIds; + + /** + * 用户拥有的角色ID + */ + private List roleIds; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java new file mode 100644 index 0000000..d15a760 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java @@ -0,0 +1,36 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Set; + +/** + * @author DongZl + * @description: 用户信息结果集 + * @Date 2023-6-19 下午 02:42 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoResp { + + /** + * 用户 + */ + private SysUser user; + + /** + * 角色集合 + */ + private Set roles; + + /** + * 权限集合 + */ + private Set permissions; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..c7d5f25 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java @@ -0,0 +1,54 @@ +package com.muyu.system.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 路由显示信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MetaVo { + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo (String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo (String title, String icon, boolean noCache) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo (String title, String icon, String link) { + this.title = title; + this.icon = icon; + this.link = link; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..6ea8d3f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java @@ -0,0 +1,62 @@ +package com.muyu.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.Setter; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author muyu + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java new file mode 100644 index 0000000..d1ef75f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java @@ -0,0 +1,56 @@ +package com.muyu.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.domain.SysMenu; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect树结构实体类 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TreeSelect implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 节点ID + */ + private Long id; + + /** + * 节点名称 + */ + private String label; + + /** + * 子节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + + public TreeSelect (SysDept dept) { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect (SysMenu menu) { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..ced239a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java @@ -0,0 +1,12 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysConfig; + +/** + * @author DongZl + * @description: 配置mybatis配置 + * @Date 2023-11-13 上午 10:05 + */ +public interface SysConfigMapper extends BaseMapper { +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..5ef9ab4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java @@ -0,0 +1,131 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDept; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author muyu + */ +public interface SysDeptMapper extends BaseMapper { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + public List selectDeptList (SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * + * @return 选中部门列表 + */ + public List selectDeptListByRoleId (@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * + * @return 部门信息 + */ + public SysDept selectDeptById (Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * + * @return 部门列表 + */ + public List selectChildrenDeptById (Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * + * @return 子部门数 + */ + public int selectNormalChildrenDeptById (Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public int hasChildByDeptId (Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public int checkDeptExistUser (Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * + * @return 结果 + */ + public SysDept checkDeptNameUnique (@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + public int insertDept (SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + public int updateDept (SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal (Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * + * @return 结果 + */ + public int updateDeptChildren (@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public int deleteDeptById (Long deptId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..1e87a7e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java @@ -0,0 +1,106 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDictData; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author muyu + */ +public interface SysDictDataMapper extends BaseMapper { + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + public List selectDictDataList (SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + public List selectDictDataByType (String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * + * @return 字典标签 + */ + public String selectDictLabel (@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * + * @return 字典数据 + */ + public SysDictData selectDictDataById (Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据 + */ + public int countDictDataByType (String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * + * @return 结果 + */ + public int deleteDictDataById (Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * + * @return 结果 + */ + public int deleteDictDataByIds (Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + public int insertDictData (SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + public int updateDictData (SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * + * @return 结果 + */ + public int updateDictDataType (@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..0a4ffe6 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,92 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDictType; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author muyu + */ +public interface SysDictTypeMapper extends BaseMapper { + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeList (SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll (); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * + * @return 字典类型 + */ + public SysDictType selectDictTypeById (Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * + * @return 字典类型 + */ + public SysDictType selectDictTypeByType (String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * + * @return 结果 + */ + public int deleteDictTypeById (Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * + * @return 结果 + */ + public int deleteDictTypeByIds (Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + public int insertDictType (SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + public int updateDictType (SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * + * @return 结果 + */ + public SysDictType checkDictTypeUnique (String dictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..0f1f86a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java @@ -0,0 +1,45 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 数据层 + * + * @author muyu + */ +public interface SysLogininforMapper extends BaseMapper { + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public int insertLogininfor (SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + public List selectLogininforList (SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * + * @return 结果 + */ + public int deleteLogininforByIds (Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor (); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..8ceaaa0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java @@ -0,0 +1,138 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author muyu + */ +public interface SysMenuMapper extends BaseMapper { + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + public List selectMenuList (SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms (); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + public List selectMenuListByUserId (SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * + * @return 权限列表 + */ + public List selectMenuPermsByRoleId (Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + public List selectMenuPermsByUserId (Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll (); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + public List selectMenuTreeByUserId (Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId (@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * + * @return 菜单信息 + */ + public SysMenu selectMenuById (Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + public int hasChildByMenuId (Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public int insertMenu (SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public int updateMenu (SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + public int deleteMenuById (Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * + * @return 结果 + */ + public SysMenu checkMenuNameUnique (@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..fee9930 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java @@ -0,0 +1,67 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysNotice; + +import java.util.List; + +/** + * 通知公告表 数据层 + * + * @author muyu + */ +public interface SysNoticeMapper extends BaseMapper { + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + public SysNotice selectNoticeById (Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + public List selectNoticeList (SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + public int insertNotice (SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + public int updateNotice (SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * + * @return 结果 + */ + public int deleteNoticeById (Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * + * @return 结果 + */ + public int deleteNoticeByIds (Long[] noticeIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..6ea1bcd --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java @@ -0,0 +1,52 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 数据层 + * + * @author muyu + */ +public interface SysOperLogMapper extends BaseMapper { + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public int insertOperlog (SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + public List selectOperLogList (SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + public int deleteOperLogByIds (Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById (Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog (); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..4bfd66d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java @@ -0,0 +1,110 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author muyu + */ +public interface SysPostMapper extends BaseMapper { + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * + * @return 岗位数据集合 + */ + public List selectPostList (SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll (); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * + * @return 角色对象信息 + */ + public SysPost selectPostById (Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId (Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * + * @return 结果 + */ + public List selectPostsByUserName (String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * + * @return 结果 + */ + public int deletePostById (Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * + * @return 结果 + */ + public int deletePostByIds (Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public int updatePost (SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public int insertPost (SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * + * @return 结果 + */ + public SysPost checkPostNameUnique (String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * + * @return 结果 + */ + public SysPost checkPostCodeUnique (String postCode); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..0e6dd4d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,50 @@ +package com.muyu.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysRoleDept; + +import java.util.List; + +/** + * 角色与部门关联表 数据层 + * + * @author muyu + */ +public interface SysRoleDeptMapper extends BaseMapper { + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int deleteRoleDeptByRoleId (Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + public int deleteRoleDept (Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public int selectCountRoleDeptByDeptId (Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * + * @return 结果 + */ + public int batchRoleDept (List roleDeptList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..2b01dc3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java @@ -0,0 +1,119 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysRole; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author muyu + */ +public interface SysRoleMapper extends BaseMapper { + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + public List selectRoleList (SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + public List selectRolePermissionByUserId (Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll (); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId (Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * + * @return 角色对象信息 + */ + public SysRole selectRoleById (Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * + * @return 角色列表 + */ + public List selectRolesByUserName (String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * + * @return 角色信息 + */ + public SysRole checkRoleNameUnique (String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique (String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int updateRole (SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int insertRole (SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int deleteRoleById (Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + public int deleteRoleByIds (Long[] roleIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..664ce26 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,50 @@ +package com.muyu.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysRoleMenu; + +import java.util.List; + +/** + * 角色与菜单关联表 数据层 + * + * @author muyu + */ +public interface SysRoleMenuMapper extends BaseMapper { + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + public int checkMenuExistRole (Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int deleteRoleMenuByRoleId (Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + public int deleteRoleMenu (Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * + * @return 结果 + */ + public int batchRoleMenu (List roleMenuList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..e037b34 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java @@ -0,0 +1,142 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysUser; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author muyu + */ +public interface SysUserMapper extends BaseMapper { + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectUserList (SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectAllocatedList (SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectUnallocatedList (SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * + * @return 用户对象信息 + */ + public SysUser selectUserByUserName (String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * + * @return 用户对象信息 + */ + public SysUser selectUserById (Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int insertUser (SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int updateUser (SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * + * @return 结果 + */ + public int updateUserAvatar (@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * + * @return 结果 + */ + public int resetUserPwd (@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + public int deleteUserById (Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + public int deleteUserByIds (Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * + * @return 结果 + */ + public SysUser checkUserNameUnique (String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * + * @return 结果 + */ + public SysUser checkPhoneUnique (String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * + * @return 结果 + */ + public SysUser checkEmailUnique (String email); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..8d09a47 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java @@ -0,0 +1,49 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysUserPost; + +import java.util.List; + +/** + * 用户与岗位关联表 数据层 + * + * @author muyu + */ +public interface SysUserPostMapper extends BaseMapper { + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * + * @return 结果 + */ + public int deleteUserPostByUserId (Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * + * @return 结果 + */ + public int countUserPostById (Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + public int deleteUserPost (Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * + * @return 结果 + */ + public int batchUserPost (List userPostList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..512adb8 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,69 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysUserRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author muyu + */ +public interface SysUserRoleMapper extends BaseMapper { + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * + * @return 结果 + */ + public int deleteUserRoleByUserId (Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + public int deleteUserRole (Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int countUserRoleByRoleId (Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * + * @return 结果 + */ + public int batchUserRole (List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * + * @return 结果 + */ + public int deleteUserRoleInfo (SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * + * @return 结果 + */ + public int deleteUserRoleInfos (@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java new file mode 100644 index 0000000..791711f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java @@ -0,0 +1,53 @@ +package com.muyu.system.rabbit; + +import com.alibaba.fastjson2.JSONObject; +import com.muyu.system.domain.SysConfig; +import jakarta.annotation.PostConstruct; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Log4j2 +@RestController +@RequestMapping("/rabbit/test") +public class RabbitTest { + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Bean + public Queue initQueue(){ + return new Queue("rabbit.test.init"); + } + + @RabbitListener(queues = "rabbit.test.init") + public void msg(SysConfig sysConfig){ + log.info("消息队列:[{}], 消息内容:[{}]", "rabbit.test.init", JSONObject.toJSONString(sysConfig)); + } + + @PostConstruct + public void init(){ + new Thread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + SysConfig sysConfig = SysConfig.builder() + .configId(1L) + .configKey("ceshi-key") + .configName("测试名称") + .configType("测试类型") + .configValue("测试值") + .build(); + rabbitTemplate.convertAndSend("rabbit.test.init",sysConfig); + }).start(); + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java new file mode 100644 index 0000000..f23d67e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java @@ -0,0 +1,44 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysConfig; + +import java.util.List; + +/** + * @author DongZl + * @description: 配置plus业务层 + * @Date 2023-11-13 上午 10:06 + */ +public interface SysConfigService extends IService { + List pageQuery (SysConfig config); + + /** + * 通过Key进行查询值 + * @param configKey + * @return + */ + String selectConfigByKey (String configKey); + + /** + * 检测参数是否唯一 + * @param config + * @return + */ + boolean checkConfigKeyUnique (SysConfig config); + + /** + * 刷新缓存 + */ + void resetConfigCache (); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache (); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache (); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java new file mode 100644 index 0000000..140cf91 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java @@ -0,0 +1,138 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.domain.vo.TreeSelect; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author muyu + */ +public interface SysDeptService extends IService { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + public List selectDeptList (SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * + * @return 部门树信息集合 + */ + public List selectDeptTreeList (SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * + * @return 树结构列表 + */ + public List buildDeptTree (List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect (List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * + * @return 选中部门列表 + */ + public List selectDeptListByRoleId (Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * + * @return 部门信息 + */ + public SysDept selectDeptById (Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * + * @return 子部门数 + */ + public int selectNormalChildrenDeptById (Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public boolean hasChildByDeptId (Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser (Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * + * @return 结果 + */ + public boolean checkDeptNameUnique (SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope (Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + public int insertDept (SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + public int updateDept (SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * + * @return 结果 + */ + public int deleteDeptById (Long deptId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java new file mode 100644 index 0000000..47aae4b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java @@ -0,0 +1,66 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDictData; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author muyu + */ +public interface SysDictDataService extends IService { + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + public List selectDictDataList (SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * + * @return 字典标签 + */ + public String selectDictLabel (String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * + * @return 字典数据 + */ + public SysDictData selectDictDataById (Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds (Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + public int insertDictData (SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + public int updateDictData (SysDictData dictData); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java new file mode 100644 index 0000000..1b3212d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java @@ -0,0 +1,106 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.common.system.domain.SysDictType; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author muyu + */ +public interface SysDictTypeService extends IService { + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeList (SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll (); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + public List selectDictDataByType (String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * + * @return 字典类型 + */ + public SysDictType selectDictTypeById (Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * + * @return 字典类型 + */ + public SysDictType selectDictTypeByType (String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds (Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache (); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache (); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache (); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + public int insertDictType (SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + public int updateDictType (SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * + * @return 结果 + */ + public boolean checkDictTypeUnique (SysDictType dictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java new file mode 100644 index 0000000..bb3031e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java @@ -0,0 +1,43 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author muyu + */ +public interface SysLogininforService extends IService { + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public int insertLogininfor (SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + public List selectLogininforList (SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * + * @return 结果 + */ + public int deleteLogininforByIds (Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor (); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java new file mode 100644 index 0000000..29bfb07 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java @@ -0,0 +1,161 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.vo.RouterVo; +import com.muyu.system.domain.vo.TreeSelect; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author muyu + */ +public interface SysMenuService extends IService { + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + public List selectMenuList (Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * + * @return 菜单列表 + */ + public List selectMenuList (SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + public Set selectMenuPermsByUserId (Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId (Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + public List selectMenuTreeByUserId (Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId (Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * + * @return 路由列表 + */ + public List buildMenus (List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * + * @return 树结构列表 + */ + public List buildMenuTree (List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect (List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * + * @return 菜单信息 + */ + public SysMenu selectMenuById (Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId (Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole (Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public int insertMenu (SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public int updateMenu (SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + public int deleteMenuById (Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public boolean checkMenuNameUnique (SysMenu menu); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java new file mode 100644 index 0000000..a1afe02 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java @@ -0,0 +1,67 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysNotice; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author muyu + */ +public interface SysNoticeService extends IService { + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + public SysNotice selectNoticeById (Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + public List selectNoticeList (SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + public int insertNotice (SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + public int updateNotice (SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * + * @return 结果 + */ + public int deleteNoticeById (Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * + * @return 结果 + */ + public int deleteNoticeByIds (Long[] noticeIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java new file mode 100644 index 0000000..70b63ee --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java @@ -0,0 +1,54 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author muyu + */ +public interface SysOperLogService extends IService { + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * + * @return 结果 + */ + public int insertOperlog (SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + public List selectOperLogList (SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + public int deleteOperLogByIds (Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById (Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog (); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java new file mode 100644 index 0000000..02e9d14 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java @@ -0,0 +1,30 @@ +package com.muyu.system.service; + +import com.muyu.common.system.domain.SysUser; + +import java.util.Set; + +/** + * 权限信息 服务层 + * + * @author muyu + */ +public interface SysPermissionService { + /** + * 获取角色数据权限 + * + * @param userId 用户Id + * + * @return 角色权限信息 + */ + public Set getRolePermission (SysUser user); + + /** + * 获取菜单数据权限 + * + * @param userId 用户Id + * + * @return 菜单权限信息 + */ + public Set getMenuPermission (SysUser user); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java new file mode 100644 index 0000000..fbd8677 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java @@ -0,0 +1,110 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author muyu + */ +public interface SysPostService extends IService { + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * + * @return 岗位列表 + */ + public List selectPostList (SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll (); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * + * @return 角色对象信息 + */ + public SysPost selectPostById (Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId (Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public boolean checkPostNameUnique (SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public boolean checkPostCodeUnique (SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * + * @return 结果 + */ + public int countUserPostById (Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * + * @return 结果 + */ + public int deletePostById (Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * + * @return 结果 + */ + public int deletePostByIds (Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public int insertPost (SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + public int updatePost (SysPost post); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java new file mode 100644 index 0000000..47009f8 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java @@ -0,0 +1,191 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysRole; +import com.muyu.system.domain.SysUserRole; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author muyu + */ +public interface SysRoleService extends IService { + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + public List selectRoleList (SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + public List selectRolesByUserId (Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + public Set selectRolePermissionByUserId (Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll (); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId (Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * + * @return 角色对象信息 + */ + public SysRole selectRoleById (Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + public boolean checkRoleNameUnique (SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + public boolean checkRoleKeyUnique (SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed (SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + public void checkRoleDataScope (Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int countUserRoleByRoleId (Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int insertRole (SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int updateRole (SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int updateRoleStatus (SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + public int authDataScope (SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * + * @return 结果 + */ + public int deleteRoleById (Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + public int deleteRoleByIds (Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * + * @return 结果 + */ + public int deleteAuthUser (SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * + * @return 结果 + */ + public int deleteAuthUsers (Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * + * @return 结果 + */ + public int insertAuthUsers (Long roleId, Long[] userIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java new file mode 100644 index 0000000..531735b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java @@ -0,0 +1,52 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author muyu + */ +public interface SysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr (String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName (String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo (String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline (LoginUser user); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java new file mode 100644 index 0000000..f4c6167 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java @@ -0,0 +1,228 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysUser; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author muyu + */ +public interface SysUserService extends IService { + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectUserList (SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectAllocatedList (SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + public List selectUnallocatedList (SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * + * @return 用户对象信息 + */ + public SysUser selectUserByUserName (String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * + * @return 用户对象信息 + */ + public SysUser selectUserById (Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * + * @return 结果 + */ + public String selectUserRoleGroup (String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * + * @return 结果 + */ + public String selectUserPostGroup (String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + public boolean checkUserNameUnique (SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + public boolean checkPhoneUnique (SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + public boolean checkEmailUnique (SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed (SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope (Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int insertUser (SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public boolean registerUser (SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int updateUser (SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth (Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int updateUserStatus (SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int updateUserProfile (SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * + * @return 结果 + */ + public boolean updateUserAvatar (String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * + * @return 结果 + */ + public int resetPwd (SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * + * @return 结果 + */ + public int resetUserPwd (String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + public int deleteUserById (Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + public int deleteUserByIds (Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * + * @return 结果 + */ + public String importUser (List userList, Boolean isUpdateSupport, String operName); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..bc07cc8 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,114 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.system.domain.SysConfig; +import com.muyu.system.mapper.SysConfigMapper; +import com.muyu.system.service.SysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author DongZl + * @description: 配置plus业务实现层 + * @Date 2023-11-13 上午 10:06 + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl + implements SysConfigService { + + @Autowired + private RedisService redisService; + + @Override + public List pageQuery (SysConfig config) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StringUtils.isNotEmpty(config.getConfigName())){ + queryWrapper.like(SysConfig::getConfigName, config.getConfigName()); + } + if (StringUtils.isNotEmpty(config.getConfigType())){ + queryWrapper.like(SysConfig::getConfigType, config.getConfigType()); + } + if (StringUtils.isNotEmpty(config.getConfigKey())){ + queryWrapper.like(SysConfig::getConfigKey, config.getConfigKey()); + } + Object beginTime = config.getParams().get("beginTime"); + if (Objects.nonNull(beginTime) && beginTime instanceof Date beginDate){ + queryWrapper.gt(SysConfig::getCreateTime, beginDate); + } + Object endTime = config.getParams().get("endTime"); + if (Objects.nonNull(endTime) && endTime instanceof Date endDate){ + queryWrapper.lt(SysConfig::getCreateTime, endDate); + } + return this.list(queryWrapper); + } + + /** + * 通过Key进行查询值 + * + * @param configKey + * + * @return + */ + @Override + public String selectConfigByKey (String configKey) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + Assert.notNull(configKey, "key不可为空"); + queryWrapper.eq(SysConfig::getConfigKey, configKey); + SysConfig sysConfig = this.getOne(queryWrapper); + return sysConfig.getConfigValue(); + } + + @Override + public boolean checkConfigKeyUnique (SysConfig config) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysConfig::getConfigKey, config.getConfigKey()); + return this.count(queryWrapper) > 0; + } + + @Override + public void resetConfigCache () { + this.clearConfigCache(); + this.loadingConfigCache(); + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache () { + Collection keys = redisService.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisService.deleteObject(keys); + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache () { + List configsList = this.list(); + for (SysConfig config : configsList) { + redisService.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * + * @return 缓存键key + */ + private String getCacheKey (String configKey) { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..a55301b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,318 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.vo.TreeSelect; +import com.muyu.system.mapper.SysDeptMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.service.SysDeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 部门管理 服务实现 + * + * @author muyu + */ +@Service +public class SysDeptServiceImpl extends ServiceImpl implements SysDeptService { + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList (SysDept dept) { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList (SysDept dept) { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * + * @return 树结构列表 + */ + @Override + public List buildDeptTree (List depts) { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect (List depts) { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId (Long roleId) { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * + * @return 部门信息 + */ + @Override + public SysDept selectDeptById (Long deptId) { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById (Long deptId) { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * + * @return 结果 + */ + @Override + public boolean hasChildByDeptId (Long deptId) { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser (Long deptId) { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique (SysDept dept) { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope (Long deptId) { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + @Override + public int insertDept (SysDept dept) { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + @Override + public int updateDept (SysDept dept) { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal (SysDept dept) { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren (Long deptId, String newAncestors, String oldAncestors) { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * + * @return 结果 + */ + @Override + public int deleteDeptById (Long deptId) { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn (List list, SysDept t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList (List list, SysDept t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild (List list, SysDept t) { + return getChildList(list, t).size() > 0 ? true : false; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..777ffbd --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,108 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.security.utils.DictUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.system.mapper.SysDictDataMapper; +import com.muyu.system.service.SysDictDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author muyu + */ +@Service +public class SysDictDataServiceImpl extends ServiceImpl implements SysDictDataService { + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList (SysDictData dictData) { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * + * @return 字典标签 + */ + @Override + public String selectDictLabel (String dictType, String dictValue) { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById (Long dictCode) { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds (Long[] dictCodes) { + for (Long dictCode : dictCodes) { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * + * @return 结果 + */ + @Override + public int insertDictData (SysDictData data) { + int row = dictDataMapper.insertDictData(data); + if (row > 0) { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * + * @return 结果 + */ + @Override + public int updateDictData (SysDictData data) { + int row = dictDataMapper.updateDictData(data); + if (row > 0) { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..4117411 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,210 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.DictUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.common.system.domain.SysDictType; +import com.muyu.system.mapper.SysDictDataMapper; +import com.muyu.system.mapper.SysDictTypeMapper; +import com.muyu.system.service.SysDictTypeService; +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.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 字典 业务层处理 + * + * @author muyu + */ +@Service +public class SysDictTypeServiceImpl extends ServiceImpl implements SysDictTypeService { + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init () { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList (SysDictType dictType) { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll () { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType (String dictType) { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById (Long dictId) { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType (String dictType) { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds (Long[] dictIds) { + for (Long dictId : dictIds) { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache () { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache () { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache () { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * + * @return 结果 + */ + @Override + public int insertDictType (SysDictType dict) { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateDictType (SysDictType dict) { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique (SysDictType dict) { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..1a7c7a0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,64 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.system.mapper.SysLogininforMapper; +import com.muyu.system.service.SysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author muyu + */ +@Service +public class SysLogininforServiceImpl extends ServiceImpl implements SysLogininforService { + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public int insertLogininfor (SysLogininfor logininfor) { + return logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + @Override + public List selectLogininforList (SysLogininfor logininfor) { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * + * @return 结果 + */ + @Override + public int deleteLogininforByIds (Long[] infoIds) { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor () { + logininforMapper.cleanLogininfor(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..d23ecdb --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,501 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.vo.MetaVo; +import com.muyu.system.domain.vo.RouterVo; +import com.muyu.system.domain.vo.TreeSelect; +import com.muyu.system.mapper.SysMenuMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.mapper.SysRoleMenuMapper; +import com.muyu.system.service.SysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单 业务层处理 + * + * @author muyu + */ +@Service +public class SysMenuServiceImpl extends ServiceImpl implements SysMenuService { + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + @Override + public List selectMenuList (Long userId) { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + @Override + public List selectMenuList (SysMenu menu, Long userId) { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) { + menuList = menuMapper.selectMenuList(menu); + } else { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId (Long userId) { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId (Long roleId) { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId (Long userId) { + List menus = null; + if (SecurityUtils.isAdmin(userId)) { + menus = menuMapper.selectMenuTreeAll(); + } else { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId (Long roleId) { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * + * @return 路由列表 + */ + @Override + public List buildMenus (List menus) { + List routers = new LinkedList(); + for (SysMenu menu : menus) { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta( + MetaVo.builder() + .title(menu.getMenuName()) + .icon(menu.getIcon()) + .noCache(StringUtils.equals("1", menu.getIsCache())) + .link(StringUtils.ishttp(menu.getPath())? menu.getPath():null) + .build() + ); + List cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (isMenuFrame(menu)) { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta( + MetaVo.builder() + .title(menu.getMenuName()) + .icon(menu.getIcon()) + .noCache(StringUtils.equals("1", menu.getIsCache())) + .link(StringUtils.ishttp(menu.getPath())? menu.getPath():null) + .build() + ); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * + * @return 树结构列表 + */ + @Override + public List buildMenuTree (List menus) { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator() ; iterator.hasNext() ; ) { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect (List menus) { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById (Long menuId) { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + @Override + public boolean hasChildByMenuId (Long menuId) { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + @Override + public boolean checkMenuExistRole (Long menuId) { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + @Override + public int insertMenu (SysMenu menu) { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + @Override + public int updateMenu (SysMenu menu) { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + @Override + public int deleteMenuById (Long menuId) { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique (SysMenu menu) { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * + * @return 路由名称 + */ + public String getRouteName (SysMenu menu) { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * + * @return 路由地址 + */ + public String getRouterPath (SysMenu menu) { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * + * @return 组件信息 + */ + public String getComponent (SysMenu menu) { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) { + component = menu.getComponent(); + } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + component = UserConstants.INNER_LINK; + } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public boolean isMenuFrame (SysMenu menu) { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public boolean isInnerLink (SysMenu menu) { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + public boolean isParentView (SysMenu menu) { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * + * @return String + */ + public List getChildPerms (List list, int parentId) { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator() ; iterator.hasNext() ; ) { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn (List list, SysMenu t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList (List list, SysMenu t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild (List list, SysMenu t) { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach (String path) { + return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"}, + new String[]{"", "", "", "/", "/"}); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..8c0c8d5 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,93 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.system.domain.SysNotice; +import com.muyu.system.mapper.SysNoticeMapper; +import com.muyu.system.service.SysNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author muyu + */ +@Service +public class SysNoticeServiceImpl extends ServiceImpl implements SysNoticeService { + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById (Long noticeId) { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + @Override + public List selectNoticeList (SysNotice notice) { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + @Override + public int insertNotice (SysNotice notice) { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + @Override + public int updateNotice (SysNotice notice) { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * + * @return 结果 + */ + @Override + public int deleteNoticeById (Long noticeId) { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * + * @return 结果 + */ + @Override + public int deleteNoticeByIds (Long[] noticeIds) { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..24e7d39 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,77 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.system.mapper.SysOperLogMapper; +import com.muyu.system.service.SysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 操作日志 服务层处理 + * + * @author muyu + */ +@Service +public class SysOperLogServiceImpl extends ServiceImpl implements SysOperLogService { + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * + * @return 结果 + */ + @Override + public int insertOperlog (SysOperLog operLog) { + return operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + @Override + public List selectOperLogList (SysOperLog operLog) { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + @Override + public int deleteOperLogByIds (Long[] operIds) { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById (Long operId) { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog () { + operLogMapper.cleanOperLog(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 0000000..a8641a1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,77 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.service.SysMenuService; +import com.muyu.system.service.SysPermissionService; +import com.muyu.system.service.SysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 用户权限处理 + * + * @author muyu + */ +@Service +public class SysPermissionServiceImpl implements SysPermissionService { + @Autowired + private SysRoleService roleService; + + @Autowired + private SysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param userId 用户Id + * + * @return 角色权限信息 + */ + @Override + public Set getRolePermission (SysUser user) { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + roles.add("admin"); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param userId 用户Id + * + * @return 菜单权限信息 + */ + @Override + public Set getMenuPermission (SysUser user) { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + perms.add("*:*:*"); + } else { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } else { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..457ac5a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,174 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.system.domain.SysPost; +import com.muyu.system.mapper.SysPostMapper; +import com.muyu.system.mapper.SysUserPostMapper; +import com.muyu.system.service.SysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author muyu + */ +@Service +public class SysPostServiceImpl extends ServiceImpl implements SysPostService { + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * + * @return 岗位信息集合 + */ + @Override + public List selectPostList (SysPost post) { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll () { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById (Long postId) { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId (Long userId) { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * + * @return 结果 + */ + @Override + public boolean checkPostNameUnique (SysPost post) { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique (SysPost post) { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * + * @return 结果 + */ + @Override + public int countUserPostById (Long postId) { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * + * @return 结果 + */ + @Override + public int deletePostById (Long postId) { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * + * @return 结果 + */ + @Override + public int deletePostByIds (Long[] postIds) { + for (Long postId : postIds) { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + @Override + public int insertPost (SysPost post) { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + @Override + public int updatePost (SysPost post) { + return postMapper.updatePost(post); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..fa832da --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,399 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysRoleDept; +import com.muyu.system.domain.SysRoleMenu; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.mapper.SysRoleDeptMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.mapper.SysRoleMenuMapper; +import com.muyu.system.mapper.SysUserRoleMapper; +import com.muyu.system.service.SysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author muyu + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService { + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList (SysRole role) { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + @Override + public List selectRolesByUserId (Long userId) { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) { + for (SysRole userRole : userRoles) { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId (Long userId) { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) { + if (StringUtils.isNotNull(perm)) { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll () { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId (Long userId) { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById (Long roleId) { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique (SysRole role) { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique (SysRole role) { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed (SysRole role) { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope (Long roleId) { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * + * @return 结果 + */ + @Override + public int countUserRoleByRoleId (Long roleId) { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertRole (SysRole role) { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole (SysRole role) { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + public int updateRoleStatus (SysRole role) { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int authDataScope (SysRole role) { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu (SysRole role) { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept (SysRole role) { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleById (Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds (Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * + * @return 结果 + */ + @Override + public int deleteAuthUser (SysUserRole userRole) { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * + * @return 结果 + */ + @Override + public int deleteAuthUsers (Long roleId, Long[] userIds) { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * + * @return 结果 + */ + @Override + public int insertAuthUsers (Long roleId, Long[] userIds) { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..8ede754 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,85 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; +import com.muyu.system.service.SysUserOnlineService; +import org.springframework.stereotype.Service; + +/** + * 在线用户 服务层处理 + * + * @author muyu + */ +@Service +public class SysUserOnlineServiceImpl implements SysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr (String ipaddr, LoginUser user) { + if (StringUtils.equals(ipaddr, user.getIpaddr())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName (String userName, LoginUser user) { + if (StringUtils.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo (String ipaddr, String userName, LoginUser user) { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline (LoginUser user) { + if (StringUtils.isNull(user)) { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginTime(user.getLoginTime()); + return sysUserOnline; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..c1d3f18 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,503 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.bean.BeanValidators; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysPost; +import com.muyu.system.domain.SysUserPost; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.mapper.*; +import com.muyu.system.service.SysUserService; +import com.muyu.system.service.SysConfigService; +import jakarta.validation.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用户 业务层处理 + * + * @author muyu + */ +@Service +public class SysUserServiceImpl extends ServiceImpl implements SysUserService { + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + @Autowired + protected Validator validator; + @Autowired + private SysUserMapper userMapper; + @Autowired + private SysRoleMapper roleMapper; + @Autowired + private SysPostMapper postMapper; + @Autowired + private SysUserRoleMapper userRoleMapper; + @Autowired + private SysUserPostMapper userPostMapper; + @Autowired + private SysConfigService configService; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList (SysUser user) { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList (SysUser user) { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList (SysUser user) { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName (String userName) { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById (Long userId) { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * + * @return 结果 + */ + @Override + public String selectUserRoleGroup (String userName) { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * + * @return 结果 + */ + @Override + public String selectUserPostGroup (String userName) { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public boolean checkUserNameUnique (SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * + * @return + */ + @Override + public boolean checkPhoneUnique (SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * + * @return + */ + @Override + public boolean checkEmailUnique (SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed (SysUser user) { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope (Long userId) { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser (SysUser user) { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public boolean registerUser (SysUser user) { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser (SysUser user) { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void insertUserAuth (Long userId, Long[] roleIds) { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public int updateUserStatus (SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public int updateUserProfile (SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * + * @return 结果 + */ + @Override + public boolean updateUserAvatar (String userName, String avatar) { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public int resetPwd (SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * + * @return 结果 + */ + @Override + public int resetUserPwd (String userName, String password) { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole (SysUser user) { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost (SysUser user) { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) { + // 新增用户与岗位管理 + List list = new ArrayList(); + for (Long postId : posts) { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole (Long userId, Long[] roleIds) { + if (StringUtils.isNotEmpty(roleIds)) { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long roleId : roleIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById (Long userId) { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds (Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * + * @return 结果 + */ + @Override + public String importUser (List userList, Boolean isUpdateSupport, String operName) { + if (StringUtils.isNull(userList) || userList.size() == 0) { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) { + try { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) { + BeanValidators.validateWithException(validator, user); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } else if (isUpdateSupport) { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + user.setUserId(u.getUserId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/resources/banner.txt b/cloud-modules/cloud-modules-system/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-system/src/main/resources/bootstrap.yml b/cloud-modules/cloud-modules-system/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..b5e11d5 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/bootstrap.yml @@ -0,0 +1,60 @@ +# Tomcat +server: + port: 9701 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud +# SPRING_AMQP_DESERIALIZATION_TRUST_ALL=true spring.amqp.deserialization.trust.all +# Spring +spring: + amqp: + deserialization: + trust: + all: true + main: + allow-bean-definition-overriding: true + application: + # 应用名称 + name: cloud-system + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # xxl-job 配置文件 + - application-xxl-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # rabbit 配置文件 + - application-rabbit-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} +logging: + level: + com.muyu.system.mapper: DEBUG \ No newline at end of file diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..d1b5629 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..76a0d8f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml new file mode 100644 index 0000000..2cd69e4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..2c35243 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + select config_id, + config_name, + config_key, + config_value, + config_type, + create_by, + create_time, + update_by, + update_time, + remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete + from sys_config + where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..a571d8b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, + d.parent_id, + d.ancestors, + d.dept_name, + d.order_num, + d.leader, + d.phone, + d.email, + d.status, + d.del_flag, + d.create_by, + d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept + set del_flag = '2' + where dept_id = #{deptId} + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..5128c99 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + create_by, + create_time, + remark + from sys_dict_data + + + + + + + + + + + + + + delete + from sys_dict_data + where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data + set dict_type = #{newDictType} + where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..7c0ebd1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete + from sys_dict_type + where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..114599c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, msg, access_time) + values (#{userName}, #{status}, #{ipaddr}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..1dac5c4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, + menu_name, + parent_id, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + ifnull(perms, '') as perms, + icon, + create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete + from sys_menu + where menu_id = #{menuId} + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..0687403 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + select notice_id, + notice_title, + notice_type, + cast(notice_content as char) as notice_content, + status, + create_by, + create_time, + update_by, + update_time, + remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete + from sys_notice + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..1834ea1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, + title, + business_type, + method, + request_method, + operator_type, + oper_name, + dept_name, + oper_url, + oper_ip, + oper_param, + json_result, + status, + error_msg, + oper_time, + cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, + oper_url, oper_ip, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, + #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..63142c3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + select post_id, + post_code, + post_name, + post_sort, + status, + create_by, + create_time, + remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete + from sys_post + where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..1a9ab92 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + delete + from sys_role_dept + where role_id = #{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..f6b56b3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.menu_check_strictly, + r.dept_check_strictly, + r.status, + r.del_flag, + r.create_time, + r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role + set del_flag = '2' + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..22d2609 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + delete + from sys_role_menu + where role_id = #{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..6f633b7 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, + u.dept_id, + u.user_name, + u.nick_name, + u.email, + u.avatar, + u.phonenumber, + u.password, + u.sex, + u.status, + u.del_flag, + u.login_ip, + u.login_date, + u.create_by, + u.create_time, + u.remark, + d.dept_id, + d.parent_id, + d.ancestors, + d.dept_name, + d.order_num, + d.leader, + d.status as dept_status, + r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user + set status = #{status} + where user_id = #{userId} + + + + update sys_user + set avatar = #{avatar} + where user_name = #{userName} + + + + update sys_user + set password = #{password} + where user_name = #{userName} + + + + update sys_user + set del_flag = '2' + where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..30e4f06 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + delete + from sys_user_post + where user_id = #{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..b93bc72 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + delete + from sys_user_role + where user_id = #{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete + from sys_user_role + where user_id = #{userId} + and role_id = #{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + diff --git a/cloud-modules/pom.xml b/cloud-modules/pom.xml new file mode 100644 index 0000000..ba0d555 --- /dev/null +++ b/cloud-modules/pom.xml @@ -0,0 +1,24 @@ + + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + + cloud-modules-system + cloud-modules-gen + cloud-modules-file + + + cloud-modules + pom + + + cloud-modules业务模块 + + + diff --git a/cloud-visual/cloud-visual-monitor/pom.xml b/cloud-visual/cloud-visual-monitor/pom.xml new file mode 100644 index 0000000..c6b9e03 --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/pom.xml @@ -0,0 +1,75 @@ + + + com.muyu + cloud-visual + 3.6.3 + + 4.0.0 + + cloud-visual-monitor + + + cloud-visual-monitor监控中心 + + + + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/CloudMonitorApplication.java b/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/CloudMonitorApplication.java new file mode 100644 index 0000000..e230689 --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/CloudMonitorApplication.java @@ -0,0 +1,18 @@ +package com.muyu.modules.monitor; + +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 监控中心 + * + * @author muyu + */ +@EnableAdminServer +@SpringBootApplication +public class CloudMonitorApplication { + public static void main (String[] args) { + SpringApplication.run(CloudMonitorApplication.class, args); + } +} diff --git a/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/config/WebSecurityConfigurer.java b/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/config/WebSecurityConfigurer.java new file mode 100644 index 0000000..d1bc9f4 --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/config/WebSecurityConfigurer.java @@ -0,0 +1,37 @@ +package com.muyu.modules.monitor.config; + +import de.codecentric.boot.admin.server.config.AdminServerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; + +/** + * 监控权限配置 + * + * @author muyu + */ +@EnableWebSecurity +public class WebSecurityConfigurer { + private final String adminContextPath; + + public WebSecurityConfigurer (AdminServerProperties adminServerProperties) { + this.adminContextPath = adminServerProperties.getContextPath(); + } + + @Bean + public SecurityFilterChain filterChain (HttpSecurity httpSecurity) throws Exception { + SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); + successHandler.setTargetUrlParameter("redirectTo"); + successHandler.setDefaultTargetUrl(adminContextPath + "/"); + + return httpSecurity + .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated()) + .securityMatcher(adminContextPath + "/assets/**" + , adminContextPath + "/login" + , adminContextPath + "/actuator/**" + , adminContextPath + "/instances/**" + ).build(); + } +} diff --git a/cloud-visual/cloud-visual-monitor/src/main/resources/banner.txt b/cloud-visual/cloud-visual-monitor/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-visual/cloud-visual-monitor/src/main/resources/bootstrap.yml b/cloud-visual/cloud-visual-monitor/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..c98034e --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/resources/bootstrap.yml @@ -0,0 +1,47 @@ +# Tomcat +server: + port: 9100 + +# nacos线上地址 +nacos: + addr: nacos.muyu.icu:8848 + user-name: nacos + password: nacos + namespace: muyu-cloud + +# Spring +spring: + application: + # 应用名称 + name: cloud-monitor + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/cloud-visual/cloud-visual-monitor/src/main/resources/logback/dev.xml b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..44e127d --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-visual/cloud-visual-monitor/src/main/resources/logback/prod.xml b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..44e127d --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/prod.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-visual/cloud-visual-monitor/src/main/resources/logback/test.xml b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/test.xml new file mode 100644 index 0000000..44e127d --- /dev/null +++ b/cloud-visual/cloud-visual-monitor/src/main/resources/logback/test.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-visual/pom.xml b/cloud-visual/pom.xml new file mode 100644 index 0000000..7fc513e --- /dev/null +++ b/cloud-visual/pom.xml @@ -0,0 +1,22 @@ + + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + + cloud-visual-monitor + + + cloud-visual + pom + + + cloud-visual图形化管理模块 + + + diff --git a/init-file/cloud-seata.sql b/init-file/cloud-seata.sql new file mode 100644 index 0000000..30fb7fa --- /dev/null +++ b/init-file/cloud-seata.sql @@ -0,0 +1,118 @@ +/* + Navicat Premium Data Transfer + + Source Server : MySQL80-local + Source Server Type : MySQL + Source Server Version : 80401 (8.4.1) + Source Host : 10.100.1.2:3306 + Source Schema : cloud-seata + + Target Server Type : MySQL + Target Server Version : 80401 (8.4.1) + File Encoding : 65001 + + Date: 17/07/2024 09:02:12 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for branch_table +-- ---------------------------- +DROP TABLE IF EXISTS `branch_table`; +CREATE TABLE `branch_table` ( + `branch_id` bigint NOT NULL, + `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `transaction_id` bigint DEFAULT NULL, + `resource_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `branch_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `status` tinyint DEFAULT NULL, + `client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `gmt_create` datetime(6) DEFAULT NULL, + `gmt_modified` datetime(6) DEFAULT NULL, + PRIMARY KEY (`branch_id`) USING BTREE, + KEY `idx_xid` (`xid`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of branch_table +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for global_table +-- ---------------------------- +DROP TABLE IF EXISTS `global_table`; +CREATE TABLE `global_table` ( + `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `transaction_id` bigint DEFAULT NULL, + `status` tinyint NOT NULL, + `application_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `transaction_service_group` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `transaction_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `timeout` int DEFAULT NULL, + `begin_time` bigint DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `gmt_create` datetime DEFAULT NULL, + `gmt_modified` datetime DEFAULT NULL, + PRIMARY KEY (`xid`) USING BTREE, + KEY `idx_gmt_modified_status` (`gmt_modified`,`status`) USING BTREE, + KEY `idx_transaction_id` (`transaction_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of global_table +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for lock_table +-- ---------------------------- +DROP TABLE IF EXISTS `lock_table`; +CREATE TABLE `lock_table` ( + `row_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `xid` varchar(96) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `transaction_id` bigint DEFAULT NULL, + `branch_id` bigint NOT NULL, + `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `table_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `pk` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `gmt_create` datetime DEFAULT NULL, + `gmt_modified` datetime DEFAULT NULL, + PRIMARY KEY (`row_key`) USING BTREE, + KEY `idx_branch_id` (`branch_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of lock_table +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `branch_id` bigint NOT NULL COMMENT 'branch transaction id', + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'global transaction id', + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'undo_log context,such as serialization', + `rollback_info` longblob NOT NULL COMMENT 'rollback info', + `log_status` int NOT NULL COMMENT '0:normal status,1:defense status', + `log_created` datetime(6) NOT NULL COMMENT 'create datetime', + `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime', + UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='AT transaction mode undo table'; + +-- ---------------------------- +-- Records of undo_log +-- ---------------------------- +BEGIN; +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/init-file/cloud-system.sql b/init-file/cloud-system.sql new file mode 100644 index 0000000..3322763 --- /dev/null +++ b/init-file/cloud-system.sql @@ -0,0 +1,682 @@ +/* + Navicat Premium Data Transfer + + Source Server : MySQL80-local + Source Server Type : MySQL + Source Server Version : 80401 (8.4.1) + Source Host : 10.100.1.2:3306 + Source Schema : cloud-system + + Target Server Type : MySQL + Target Server Version : 80401 (8.4.1) + File Encoding : 65001 + + Date: 17/07/2024 09:01:52 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for gen_table +-- ---------------------------- +DROP TABLE IF EXISTS `gen_table`; +CREATE TABLE `gen_table` ( + `table_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', + `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '表名称', + `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '表描述', + `sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联子表的表名', + `sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '子表关联的外键名', + `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '实体类名称', + `tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'crud' COMMENT '使用的模板(crud单表操作 tree树表操作)', + `package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成包路径', + `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成模块名', + `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成业务名', + `function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成功能名', + `function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成功能作者', + `gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '生成代码方式(0zip压缩包 1自定义路径)', + `gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '/' COMMENT '生成路径(不填默认项目路径)', + `options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '其它生成选项', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`table_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='代码生成业务表'; + +-- ---------------------------- +-- Records of gen_table +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for gen_table_column +-- ---------------------------- +DROP TABLE IF EXISTS `gen_table_column`; +CREATE TABLE `gen_table_column` ( + `column_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', + `table_id` bigint DEFAULT NULL COMMENT '归属表编号', + `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '列名称', + `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '列描述', + `column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '列类型', + `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'JAVA类型', + `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'JAVA字段名', + `is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否主键(1是)', + `is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否自增(1是)', + `is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否必填(1是)', + `is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否为插入字段(1是)', + `is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否编辑字段(1是)', + `is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否列表字段(1是)', + `is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '是否查询字段(1是)', + `query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)', + `html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典类型', + `sort` int DEFAULT NULL COMMENT '排序', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`column_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='代码生成业务表字段'; + +-- ---------------------------- +-- Records of gen_table_column +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `config_id` int NOT NULL AUTO_INCREMENT COMMENT '参数主键', + `config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '参数名称', + `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '参数键名', + `config_value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '参数键值', + `config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'N' COMMENT '系统内置(Y是 N否)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`config_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='参数配置表'; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +BEGIN; +INSERT INTO `sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', '2024-05-23 15:08:18', '', NULL, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow'); +INSERT INTO `sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2024-05-23 15:08:18', '', NULL, '初始化密码 123456'); +INSERT INTO `sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', '2024-05-23 15:08:18', '', NULL, '深色主题theme-dark,浅色主题theme-light'); +INSERT INTO `sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', '2024-05-23 15:08:18', '', NULL, '是否开启注册用户功能(true开启,false关闭)'); +INSERT INTO `sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (5, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', '2024-05-23 15:08:18', '', NULL, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dept`; +CREATE TABLE `sys_dept` ( + `dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id', + `parent_id` bigint DEFAULT '0' COMMENT '父部门id', + `ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '祖级列表', + `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '部门名称', + `order_num` int DEFAULT '0' COMMENT '显示顺序', + `leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '负责人', + `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '联系电话', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '邮箱', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '部门状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`dept_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='部门表'; + +-- ---------------------------- +-- Records of sys_dept +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +INSERT INTO `sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dict_data +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_data`; +CREATE TABLE `sys_dict_data` ( + `dict_code` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码', + `dict_sort` int DEFAULT '0' COMMENT '字典排序', + `dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典标签', + `dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典键值', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典类型', + `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '样式属性(其他样式扩展)', + `list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '表格回显样式', + `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'N' COMMENT '是否默认(Y是 N否)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`dict_code`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='字典数据表'; + +-- ---------------------------- +-- Records of sys_dict_data +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '性别男'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '性别女'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '性别未知'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '显示菜单'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '隐藏菜单'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '正常状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '停用状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '正常状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '停用状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '默认分组'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '系统分组'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '系统默认是'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '系统默认否'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '通知'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '公告'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '正常状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '关闭状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '其他操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '新增操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '修改操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '删除操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '授权操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '导出操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '导入操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '强退操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '生成操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '清空操作'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '正常状态'); +INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '停用状态'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dict_type +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_type`; +CREATE TABLE `sys_dict_type` ( + `dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键', + `dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典名称', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '字典类型', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`dict_id`) USING BTREE, + UNIQUE KEY `dict_type` (`dict_type`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='字典类型表'; + +-- ---------------------------- +-- Records of sys_dict_type +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '用户性别', 'sys_user_sex', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '用户性别列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '菜单状态', 'sys_show_hide', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '菜单状态列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '系统开关', 'sys_normal_disable', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '系统开关列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, '任务状态', 'sys_job_status', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '任务状态列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (5, '任务分组', 'sys_job_group', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '任务分组列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (6, '系统是否', 'sys_yes_no', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '系统是否列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (7, '通知类型', 'sys_notice_type', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '通知类型列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (8, '通知状态', 'sys_notice_status', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '通知状态列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (9, '操作类型', 'sys_oper_type', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '操作类型列表'); +INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (10, '系统状态', 'sys_common_status', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '登录状态列表'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_logininfor +-- ---------------------------- +DROP TABLE IF EXISTS `sys_logininfor`; +CREATE TABLE `sys_logininfor` ( + `info_id` bigint NOT NULL AUTO_INCREMENT COMMENT '访问ID', + `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用户账号', + `ipaddr` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '登录IP地址', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '登录状态(0成功 1失败)', + `msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '提示信息', + `access_time` datetime DEFAULT NULL COMMENT '访问时间', + PRIMARY KEY (`info_id`) USING BTREE, + KEY `idx_sys_logininfor_s` (`status`) USING BTREE, + KEY `idx_sys_logininfor_lt` (`access_time`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='系统访问记录'; + +-- ---------------------------- +-- Records of sys_logininfor +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu`; +CREATE TABLE `sys_menu` ( + `menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID', + `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单名称', + `parent_id` bigint DEFAULT '0' COMMENT '父菜单ID', + `order_num` int DEFAULT '0' COMMENT '显示顺序', + `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '路由地址', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '组件路径', + `query` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '路由参数', + `is_frame` int DEFAULT '1' COMMENT '是否为外链(0是 1否)', + `is_cache` int DEFAULT '0' COMMENT '是否缓存(0缓存 1不缓存)', + `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)', + `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '菜单状态(0正常 1停用)', + `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '权限标识', + `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '#' COMMENT '菜单图标', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '备注', + PRIMARY KEY (`menu_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=2000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='菜单权限表'; + +-- ---------------------------- +-- Records of sys_menu +-- ---------------------------- +BEGIN; +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '系统管理', 0, 1, 'system', NULL, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', '2024-05-23 15:08:18', '', NULL, '系统管理目录'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '系统监控', 0, 2, 'monitor', NULL, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', '2024-05-23 15:08:18', '', NULL, '系统监控目录'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '系统工具', 0, 3, 'tool', NULL, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', '2024-05-23 15:08:18', '', NULL, '系统工具目录'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, '若依官网', 0, 4, 'http://muyu.vip', NULL, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', '2024-05-23 15:08:18', '', NULL, '若依官网地址'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (100, '用户管理', 1, 1, 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', '2024-05-23 15:08:18', '', NULL, '用户管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (101, '角色管理', 1, 2, 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', '2024-05-23 15:08:18', '', NULL, '角色管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (102, '菜单管理', 1, 3, 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', '2024-05-23 15:08:18', '', NULL, '菜单管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (103, '部门管理', 1, 4, 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', '2024-05-23 15:08:18', '', NULL, '部门管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (104, '岗位管理', 1, 5, 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', '2024-05-23 15:08:18', '', NULL, '岗位管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (105, '字典管理', 1, 6, 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', '2024-05-23 15:08:18', '', NULL, '字典管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (106, '参数设置', 1, 7, 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', '2024-05-23 15:08:18', '', NULL, '参数设置菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (107, '通知公告', 1, 8, 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', '2024-05-23 15:08:18', '', NULL, '通知公告菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (108, '日志管理', 1, 9, 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', '2024-05-23 15:08:18', '', NULL, '日志管理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (109, '在线用户', 2, 1, 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', '2024-05-23 15:08:18', '', NULL, '在线用户菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (110, '定时任务', 2, 2, 'http://admin.xxl.muyu.icu/toLogin', 'monitor/job/index', '', 0, 0, 'M', '0', '0', 'monitor:job:list', 'job', 'admin', '2024-05-23 15:08:18', 'admin', '2024-07-15 12:50:44', '定时任务菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (111, 'Sentinel控制台', 2, 3, 'http://localhost:8718', '', '', 0, 0, 'C', '0', '0', 'monitor:sentinel:list', 'sentinel', 'admin', '2024-05-23 15:08:18', '', NULL, '流量控制菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (112, 'Nacos控制台', 2, 4, 'http://localhost:8848/nacos', '', '', 0, 0, 'C', '0', '0', 'monitor:nacos:list', 'nacos', 'admin', '2024-05-23 15:08:18', '', NULL, '服务治理菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (113, 'Admin控制台', 2, 5, 'http://localhost:9100/login', '', '', 0, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', '2024-05-23 15:08:18', '', NULL, '服务监控菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (114, '表单构建', 3, 1, 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', '2024-05-23 15:08:18', '', NULL, '表单构建菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (115, '代码生成', 3, 2, 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', '2024-05-23 15:08:18', '', NULL, '代码生成菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (116, '系统接口', 3, 3, 'http://localhost:8080/doc.html', '', '', 0, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', '2024-05-23 15:08:18', '', NULL, '系统接口菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (500, '操作日志', 108, 1, 'operlog', 'system/operlog/index', '', 1, 0, 'C', '0', '0', 'system:operlog:list', 'form', 'admin', '2024-05-23 15:08:18', '', NULL, '操作日志菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (501, '登录日志', 108, 2, 'logininfor', 'system/logininfor/index', '', 1, 0, 'C', '0', '0', 'system:logininfor:list', 'logininfor', 'admin', '2024-05-23 15:08:18', '', NULL, '登录日志菜单'); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1000, '用户查询', 100, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1001, '用户新增', 100, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1002, '用户修改', 100, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1003, '用户删除', 100, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1004, '用户导出', 100, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1005, '用户导入', 100, 6, '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1006, '重置密码', 100, 7, '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1007, '角色查询', 101, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1008, '角色新增', 101, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1009, '角色修改', 101, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1010, '角色删除', 101, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1011, '角色导出', 101, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1012, '菜单查询', 102, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1013, '菜单新增', 102, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1014, '菜单修改', 102, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1015, '菜单删除', 102, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1016, '部门查询', 103, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1017, '部门新增', 103, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1018, '部门修改', 103, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1019, '部门删除', 103, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1020, '岗位查询', 104, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1021, '岗位新增', 104, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1022, '岗位修改', 104, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1023, '岗位删除', 104, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1024, '岗位导出', 104, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1025, '字典查询', 105, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1026, '字典新增', 105, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1027, '字典修改', 105, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1028, '字典删除', 105, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1029, '字典导出', 105, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1030, '参数查询', 106, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1031, '参数新增', 106, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1032, '参数修改', 106, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1033, '参数删除', 106, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1034, '参数导出', 106, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1035, '公告查询', 107, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1036, '公告新增', 107, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1037, '公告修改', 107, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1038, '公告删除', 107, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1039, '操作查询', 500, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:operlog:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1040, '操作删除', 500, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:operlog:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1041, '日志导出', 500, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:operlog:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1042, '登录查询', 501, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:logininfor:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1043, '登录删除', 501, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:logininfor:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1044, '日志导出', 501, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:logininfor:export', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1045, '账户解锁', 501, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:logininfor:unlock', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1046, '在线查询', 109, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1047, '批量强退', 109, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1048, '单条强退', 109, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1055, '生成查询', 115, 1, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1056, '生成修改', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1057, '生成删除', 115, 3, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1058, '导入代码', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1059, '预览代码', 115, 4, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1060, '生成代码', 115, 5, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_notice +-- ---------------------------- +DROP TABLE IF EXISTS `sys_notice`; +CREATE TABLE `sys_notice` ( + `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID', + `notice_title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告标题', + `notice_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告类型(1通知 2公告)', + `notice_content` longblob COMMENT '公告内容', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`notice_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='通知公告表'; + +-- ---------------------------- +-- Records of sys_notice +-- ---------------------------- +BEGIN; +INSERT INTO `sys_notice` (`notice_id`, `notice_title`, `notice_type`, `notice_content`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '温馨提醒:2018-07-01 若依新版本发布啦', '2', 0xE696B0E78988E69CACE58685E5AEB9, '0', 'admin', '2024-05-23 15:08:18', '', NULL, '管理员'); +INSERT INTO `sys_notice` (`notice_id`, `notice_title`, `notice_type`, `notice_content`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '维护通知:2018-07-01 若依系统凌晨维护', '1', 0xE7BBB4E68AA4E58685E5AEB9, '0', 'admin', '2024-05-23 15:08:18', '', NULL, '管理员'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_oper_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_oper_log`; +CREATE TABLE `sys_oper_log` ( + `oper_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键', + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '模块标题', + `business_type` int DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)', + `method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '方法名称', + `request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '请求方式', + `operator_type` int DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)', + `oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '操作人员', + `dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '部门名称', + `oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '请求URL', + `oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '主机地址', + `oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '操作地点', + `oper_param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '请求参数', + `json_result` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '返回参数', + `status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)', + `error_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '错误消息', + `oper_time` datetime DEFAULT NULL COMMENT '操作时间', + `cost_time` bigint DEFAULT '0' COMMENT '消耗时间', + PRIMARY KEY (`oper_id`) USING BTREE, + KEY `idx_sys_oper_log_bt` (`business_type`) USING BTREE, + KEY `idx_sys_oper_log_s` (`status`) USING BTREE, + KEY `idx_sys_oper_log_ot` (`oper_time`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='操作日志记录'; + +-- ---------------------------- +-- Records of sys_oper_log +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_post +-- ---------------------------- +DROP TABLE IF EXISTS `sys_post`; +CREATE TABLE `sys_post` ( + `post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID', + `post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位编码', + `post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位名称', + `post_sort` int NOT NULL COMMENT '显示顺序', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`post_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='岗位信息表'; + +-- ---------------------------- +-- Records of sys_post +-- ---------------------------- +BEGIN; +INSERT INTO `sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, 'ceo', '董事长', 1, '0', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, 'se', '项目经理', 2, '0', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, 'hr', '人力资源', 3, '0', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +INSERT INTO `sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, 'user', '普通员工', 4, '0', 'admin', '2024-05-23 15:08:18', '', NULL, ''); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID', + `role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称', + `role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色权限字符串', + `role_sort` int NOT NULL COMMENT '显示顺序', + `data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + `menu_check_strictly` tinyint(1) DEFAULT '1' COMMENT '菜单树选择项是否关联显示', + `dept_check_strictly` tinyint(1) DEFAULT '1' COMMENT '部门树选择项是否关联显示', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`role_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='角色信息表'; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role` (`role_id`, `role_name`, `role_key`, `role_sort`, `data_scope`, `menu_check_strictly`, `dept_check_strictly`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', '2024-05-23 15:08:18', '', NULL, '超级管理员'); +INSERT INTO `sys_role` (`role_id`, `role_name`, `role_key`, `role_sort`, `data_scope`, `menu_check_strictly`, `dept_check_strictly`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '普通角色', 'common', 2, '2', 1, 1, '0', '0', 'admin', '2024-05-23 15:08:18', 'admin', '2024-07-15 12:42:34', '普通角色'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role_dept`; +CREATE TABLE `sys_role_dept` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `dept_id` bigint NOT NULL COMMENT '部门ID', + PRIMARY KEY (`role_id`,`dept_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='角色和部门关联表'; + +-- ---------------------------- +-- Records of sys_role_dept +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role_dept` (`role_id`, `dept_id`) VALUES (2, 100); +INSERT INTO `sys_role_dept` (`role_id`, `dept_id`) VALUES (2, 101); +INSERT INTO `sys_role_dept` (`role_id`, `dept_id`) VALUES (2, 105); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role_menu`; +CREATE TABLE `sys_role_menu` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `menu_id` bigint NOT NULL COMMENT '菜单ID', + PRIMARY KEY (`role_id`,`menu_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='角色和菜单关联表'; + +-- ---------------------------- +-- Records of sys_role_menu +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 2); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 3); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 4); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 100); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 101); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 102); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 103); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 104); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 105); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 106); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 107); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 108); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 109); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 111); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 112); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 113); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 114); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 115); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 116); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 500); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 501); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1000); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1001); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1002); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1003); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1004); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1005); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1006); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1007); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1008); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1009); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1010); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1011); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1012); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1013); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1014); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1015); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1016); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1017); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1018); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1019); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1020); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1021); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1022); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1023); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1024); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1025); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1026); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1027); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1028); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1029); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1030); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1031); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1032); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1033); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1034); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1035); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1036); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1037); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1038); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1039); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1040); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1041); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1042); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1043); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1044); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1045); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1046); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1047); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1048); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1055); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1056); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1057); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1058); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1059); +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (2, 1060); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `dept_id` bigint DEFAULT NULL COMMENT '部门ID', + `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号', + `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户昵称', + `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '00' COMMENT '用户类型(00系统用户)', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用户邮箱', + `phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手机号码', + `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', + `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '头像地址', + `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '密码', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '最后登录IP', + `login_date` datetime DEFAULT NULL COMMENT '最后登录时间', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`user_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户信息表'; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user` (`user_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2024-05-23 15:08:18', 'admin', '2024-05-23 15:08:18', '', NULL, '管理员'); +INSERT INTO `sys_user` (`user_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2024-05-23 15:08:18', 'admin', '2024-05-23 15:08:18', '', NULL, '测试员'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user_post +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_post`; +CREATE TABLE `sys_user_post` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `post_id` bigint NOT NULL COMMENT '岗位ID', + PRIMARY KEY (`user_id`,`post_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户与岗位关联表'; + +-- ---------------------------- +-- Records of sys_user_post +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user_post` (`user_id`, `post_id`) VALUES (1, 1); +INSERT INTO `sys_user_post` (`user_id`, `post_id`) VALUES (2, 2); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_role`; +CREATE TABLE `sys_user_role` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `role_id` bigint NOT NULL COMMENT '角色ID', + PRIMARY KEY (`user_id`,`role_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户和角色关联表'; + +-- ---------------------------- +-- Records of sys_user_role +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (1, 1); +INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (2, 2); +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/init-file/nacos_config.zip b/init-file/nacos_config.zip new file mode 100644 index 0000000..a3e3c5b Binary files /dev/null and b/init-file/nacos_config.zip differ diff --git a/init-file/xxl-init.sql b/init-file/xxl-init.sql new file mode 100644 index 0000000..f7e9974 --- /dev/null +++ b/init-file/xxl-init.sql @@ -0,0 +1,122 @@ +# +# XXL-JOB v2.4.2-SNAPSHOT +# Copyright (c) 2015-present, xuxueli. + +CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4; +use `xxl_job`; + +SET NAMES utf8mb4; + +CREATE TABLE `xxl_job_info` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_desc` varchar(255) NOT NULL, + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `author` varchar(64) DEFAULT NULL COMMENT '作者', + `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', + `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型', + `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型', + `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略', + `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', + `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', + `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', + `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', + `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', + `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', + `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', + `trigger_code` int(11) NOT NULL COMMENT '调度-结果', + `trigger_msg` text COMMENT '调度-日志', + `handle_time` datetime DEFAULT NULL COMMENT '执行-时间', + `handle_code` int(11) NOT NULL COMMENT '执行-状态', + `handle_msg` text COMMENT '执行-日志', + `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', + PRIMARY KEY (`id`), + KEY `I_trigger_time` (`trigger_time`), + KEY `I_handle_code` (`handle_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log_report` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', + `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', + `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', + `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_logglue` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_registry` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `registry_group` varchar(50) NOT NULL, + `registry_key` varchar(255) NOT NULL, + `registry_value` varchar(255) NOT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_group` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `app_name` varchar(64) NOT NULL COMMENT '执行器AppName', + `title` varchar(12) NOT NULL COMMENT '执行器名称', + `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', + `address_list` text COMMENT '执行器地址列表,多地址逗号分隔', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(50) NOT NULL COMMENT '账号', + `password` varchar(50) NOT NULL COMMENT '密码', + `role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', + `permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', + PRIMARY KEY (`id`), + UNIQUE KEY `i_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_lock` ( + `lock_name` varchar(50) NOT NULL COMMENT '锁名称', + PRIMARY KEY (`lock_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL, '2018-11-03 22:21:31' ); +INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', ''); +INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL); +INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock'); + +commit; + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b30f0df --- /dev/null +++ b/pom.xml @@ -0,0 +1,344 @@ + + + 4.0.0 + + com.muyu + cloud-server + 3.6.3 + + muyu + http://www.muyu.vip + 若依微服务系统 + + + 3.6.3 + UTF-8 + UTF-8 + 17 + 3.2.6 + 2023.0.2 + 2023.0.1.0 + 3.2.3 + 3.0.0 + 1.6.2 + 1.27.2 + 2.3.3 + 1.4.7 + 1.2.23 + 4.3.0 + 2.13.0 + 2.3 + 2.0.41 + 0.12.5 + 8.5.10 + 4.1.2 + 2.14.3 + 9.2.0 + 9.2.0 + 6.1.0-M2 + 1.3.2 + 5.8.27 + 4.1.0 + 2.4.1 + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + 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.apache.skywalking + apm-toolkit-trace + ${skywalking-toolkit.version} + + + + + org.apache.skywalking + apm-toolkit-logback-1.x + ${apm-logback.version} + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j-openapi3.version} + + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.version} + + + + + javax.annotation + javax.annotation-api + ${javax.annotation.version} + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + io.swagger + swagger-models + ${swagger.core.version} + + + io.swagger + swagger-annotations + ${swagger.core.version} + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + com.alibaba + transmittable-thread-local + ${transmittable-thread-local.version} + + + + + com.muyu + cloud-common-core + ${muyu.version} + + + + + com.muyu + cloud-common-api-doc + ${muyu.version} + + + + + com.muyu + cloud-common-security + ${muyu.version} + + + + + com.muyu + cloud-common-datascope + ${muyu.version} + + + + + com.muyu + cloud-common-datasource + ${muyu.version} + + + + + com.muyu + cloud-common-seata + ${muyu.version} + + + + + com.muyu + cloud-common-log + ${muyu.version} + + + + + com.muyu + cloud-common-redis + ${muyu.version} + + + + + com.muyu + cloud-common-system + ${muyu.version} + + + + + com.xuxueli + xxl-job-core + ${xxl-job-core.version} + + + + + com.muyu + cloud-common-xxl + ${muyu.version} + + + + + com.muyu + cloud-common-rabbit + ${muyu.version} + + + + + + cloud-auth + cloud-gateway + cloud-visual + cloud-modules + cloud-common + + pom + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + diff --git a/skywalking/agent/LICENSE b/skywalking/agent/LICENSE new file mode 100644 index 0000000..a5c6f78 --- /dev/null +++ b/skywalking/agent/LICENSE @@ -0,0 +1,233 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================= +Apache SkyWalking Subcomponents: + +The Apache SkyWalking project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + +======================================================================== +Apache 2.0 licenses +======================================================================== + +The following components are provided under the Apache License. See project link for details. +The text of each license is the standard Apache 2.0 license. + + raphw (byte-buddy) 1.14.9: http://bytebuddy.net/ , Apache 2.0 + Google: grpc-java 1.53.0: https://github.com/grpc/grpc-java, Apache 2.0 + Google: gson 2.8.9: https://github.com/google/gson , Apache 2.0 + Google: proto-google-common-protos 2.0.1: https://github.com/googleapis/googleapis , Apache 2.0 + Google: jsr305 3.0.2: http://central.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.0/jsr305-3.0.0.pom , Apache 2.0 + Google: guava 32.0.1: https://github.com/google/guava , Apache 2.0 + netty 4.1.100: https://github.com/netty/netty/blob/4.1/LICENSE.txt, Apache 2.0 + +======================================================================== +BSD licenses +======================================================================== + +The following components are provided under a BSD license. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + asm 9.2:https://gitlab.ow2.org , BSD-3-Clause diff --git a/skywalking/agent/NOTICE b/skywalking/agent/NOTICE new file mode 100644 index 0000000..f95d82a --- /dev/null +++ b/skywalking/agent/NOTICE @@ -0,0 +1,299 @@ +Apache SkyWalking +Copyright 2017-2024 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +======================================================================== + +grpc-java NOTICE + +======================================================================== +Copyright 2014, gRPC Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +----------------------------------------------------------------------- + +This product contains a modified portion of 'OkHttp', an open source +HTTP & SPDY client for Android and Java applications, which can be obtained +at: + + * LICENSE: + * okhttp/third_party/okhttp/LICENSE (Apache License 2.0) + * HOMEPAGE: + * https://github.com/square/okhttp + * LOCATION_IN_GRPC: + * okhttp/third_party/okhttp + +This product contains a modified portion of 'Netty', an open source +networking library, which can be obtained at: + + * LICENSE: + * netty/third_party/netty/LICENSE.txt (Apache License 2.0) + * HOMEPAGE: + * https://netty.io + * LOCATION_IN_GRPC: + * netty/third_party/netty + +======================================================================== + +grpc NOTICE + +======================================================================== + +Copyright 2014 gRPC authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +======================================================================== + +netty NOTICE + +======================================================================== + + + The Netty Project + ================= + +Please visit the Netty web site for more information: + + * http://netty.io/ + +Copyright 2014 The Netty Project + +The Netty Project licenses this file to you under the Apache License, +version 2.0 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. + +Also, please refer to each LICENSE..txt file, which is located in +the 'license' directory of the distribution file, for the license terms of the +components that this product depends on. + +------------------------------------------------------------------------------- +This product contains the extensions to Java Collections Framework which has +been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: + + * LICENSE: + * license/LICENSE.jsr166y.txt (Public Domain) + * HOMEPAGE: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ + * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ + +This product contains a modified version of Robert Harder's Public Domain +Base64 Encoder and Decoder, which can be obtained at: + + * LICENSE: + * license/LICENSE.base64.txt (Public Domain) + * HOMEPAGE: + * http://iharder.sourceforge.net/current/java/base64/ + +This product contains a modified portion of 'Webbit', an event based +WebSocket and HTTP server, which can be obtained at: + + * LICENSE: + * license/LICENSE.webbit.txt (BSD License) + * HOMEPAGE: + * https://github.com/joewalnes/webbit + +This product contains a modified portion of 'SLF4J', a simple logging +facade for Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.slf4j.txt (MIT License) + * HOMEPAGE: + * http://www.slf4j.org/ + +This product contains a modified portion of 'Apache Harmony', an open source +Java SE, which can be obtained at: + + * NOTICE: + * license/NOTICE.harmony.txt + * LICENSE: + * license/LICENSE.harmony.txt (Apache License 2.0) + * HOMEPAGE: + * http://archive.apache.org/dist/harmony/ + +This product contains a modified portion of 'jbzip2', a Java bzip2 compression +and decompression library written by Matthew J. Francis. It can be obtained at: + + * LICENSE: + * license/LICENSE.jbzip2.txt (MIT License) + * HOMEPAGE: + * https://code.google.com/p/jbzip2/ + +This product contains a modified portion of 'libdivsufsort', a C API library to construct +the suffix array and the Burrows-Wheeler transformed string for any input string of +a constant-size alphabet written by Yuta Mori. It can be obtained at: + + * LICENSE: + * license/LICENSE.libdivsufsort.txt (MIT License) + * HOMEPAGE: + * https://github.com/y-256/libdivsufsort + +This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, + which can be obtained at: + + * LICENSE: + * license/LICENSE.jctools.txt (ASL2 License) + * HOMEPAGE: + * https://github.com/JCTools/JCTools + +This product optionally depends on 'JZlib', a re-implementation of zlib in +pure Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.jzlib.txt (BSD style License) + * HOMEPAGE: + * http://www.jcraft.com/jzlib/ + +This product optionally depends on 'Compress-LZF', a Java library for encoding and +decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: + + * LICENSE: + * license/LICENSE.compress-lzf.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/ning/compress + +This product optionally depends on 'lz4', a LZ4 Java compression +and decompression library written by Adrien Grand. It can be obtained at: + + * LICENSE: + * license/LICENSE.lz4.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/jpountz/lz4-java + +This product optionally depends on 'lzma-java', a LZMA Java compression +and decompression library, which can be obtained at: + + * LICENSE: + * license/LICENSE.lzma-java.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/jponge/lzma-java + +This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression +and decompression library written by William Kinney. It can be obtained at: + + * LICENSE: + * license/LICENSE.jfastlz.txt (MIT License) + * HOMEPAGE: + * https://code.google.com/p/jfastlz/ + +This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data +interchange format, which can be obtained at: + + * LICENSE: + * license/LICENSE.protobuf.txt (New BSD License) + * HOMEPAGE: + * https://github.com/google/protobuf + +This product optionally depends on 'Bouncy Castle Crypto APIs' to generate +a temporary self-signed X.509 certificate when the JVM does not provide the +equivalent functionality. It can be obtained at: + + * LICENSE: + * license/LICENSE.bouncycastle.txt (MIT License) + * HOMEPAGE: + * http://www.bouncycastle.org/ + +This product optionally depends on 'Snappy', a compression library produced +by Google Inc, which can be obtained at: + + * LICENSE: + * license/LICENSE.snappy.txt (New BSD License) + * HOMEPAGE: + * https://github.com/google/snappy + +This product optionally depends on 'JBoss Marshalling', an alternative Java +serialization API, which can be obtained at: + + * LICENSE: + * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) + * HOMEPAGE: + * http://www.jboss.org/jbossmarshalling + +This product optionally depends on 'Caliper', Google's micro- +benchmarking framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.caliper.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/google/caliper + +This product optionally depends on 'Apache Commons Logging', a logging +framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-logging.txt (Apache License 2.0) + * HOMEPAGE: + * http://commons.apache.org/logging/ + +This product optionally depends on 'Apache Log4J', a logging framework, which +can be obtained at: + + * LICENSE: + * license/LICENSE.log4j.txt (Apache License 2.0) + * HOMEPAGE: + * http://logging.apache.org/log4j/ + +This product optionally depends on 'Aalto XML', an ultra-high performance +non-blocking XML processor, which can be obtained at: + + * LICENSE: + * license/LICENSE.aalto-xml.txt (Apache License 2.0) + * HOMEPAGE: + * http://wiki.fasterxml.com/AaltoHome + +This product contains a modified version of 'HPACK', a Java implementation of +the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: + + * LICENSE: + * license/LICENSE.hpack.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/twitter/hpack + +This product contains a modified portion of 'Apache Commons Lang', a Java library +provides utilities for the java.lang API, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-lang.txt (Apache License 2.0) + * HOMEPAGE: + * https://commons.apache.org/proper/commons-lang/ + + +This product contains the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build. + + * LICENSE: + * license/LICENSE.mvn-wrapper.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/takari/maven-wrapper + diff --git a/skywalking/agent/activations/apm-toolkit-kafka-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-kafka-activation-9.2.0.jar new file mode 100644 index 0000000..4e5913d Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-kafka-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-log4j-1.x-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-log4j-1.x-activation-9.2.0.jar new file mode 100644 index 0000000..b7ba6bf Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-log4j-1.x-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-log4j-2.x-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-log4j-2.x-activation-9.2.0.jar new file mode 100644 index 0000000..49f30f8 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-log4j-2.x-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-logback-1.x-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-logback-1.x-activation-9.2.0.jar new file mode 100644 index 0000000..ac90705 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-logback-1.x-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-logging-common-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-logging-common-9.2.0.jar new file mode 100644 index 0000000..741fcac Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-logging-common-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-meter-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-meter-activation-9.2.0.jar new file mode 100644 index 0000000..aefab9f Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-meter-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-micrometer-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-micrometer-activation-9.2.0.jar new file mode 100644 index 0000000..03e0201 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-micrometer-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-opentracing-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-opentracing-activation-9.2.0.jar new file mode 100644 index 0000000..23bd130 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-opentracing-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-trace-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-trace-activation-9.2.0.jar new file mode 100644 index 0000000..fb7b764 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-trace-activation-9.2.0.jar differ diff --git a/skywalking/agent/activations/apm-toolkit-webflux-activation-9.2.0.jar b/skywalking/agent/activations/apm-toolkit-webflux-activation-9.2.0.jar new file mode 100644 index 0000000..cf447c5 Binary files /dev/null and b/skywalking/agent/activations/apm-toolkit-webflux-activation-9.2.0.jar differ diff --git a/skywalking/agent/bootstrap-plugins/apm-jdk-forkjoinpool-plugin-9.2.0.jar b/skywalking/agent/bootstrap-plugins/apm-jdk-forkjoinpool-plugin-9.2.0.jar new file mode 100644 index 0000000..6134cb7 Binary files /dev/null and b/skywalking/agent/bootstrap-plugins/apm-jdk-forkjoinpool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/bootstrap-plugins/apm-jdk-http-plugin-9.2.0.jar b/skywalking/agent/bootstrap-plugins/apm-jdk-http-plugin-9.2.0.jar new file mode 100644 index 0000000..35d7264 Binary files /dev/null and b/skywalking/agent/bootstrap-plugins/apm-jdk-http-plugin-9.2.0.jar differ diff --git a/skywalking/agent/bootstrap-plugins/apm-jdk-threading-plugin-9.2.0.jar b/skywalking/agent/bootstrap-plugins/apm-jdk-threading-plugin-9.2.0.jar new file mode 100644 index 0000000..c9d98ae Binary files /dev/null and b/skywalking/agent/bootstrap-plugins/apm-jdk-threading-plugin-9.2.0.jar differ diff --git a/skywalking/agent/bootstrap-plugins/apm-jdk-threadpool-plugin-9.2.0.jar b/skywalking/agent/bootstrap-plugins/apm-jdk-threadpool-plugin-9.2.0.jar new file mode 100644 index 0000000..48e603e Binary files /dev/null and b/skywalking/agent/bootstrap-plugins/apm-jdk-threadpool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/config/agent.config b/skywalking/agent/config/agent.config new file mode 100644 index 0000000..61db0af --- /dev/null +++ b/skywalking/agent/config/agent.config @@ -0,0 +1,306 @@ +# UI中的服务名称 +# ${service name} = [${group name }::]${logic name} +# 组名仅是可选的。 +agent.service_name=${SW_AGENT_NAME:Your_ApplicationName} +agent.service_name#length=${SW_AGENT_NAME_MAX_LENGTH:50} + +# 代理命名空间 +agent.namespace=${SW_AGENT_NAMESPACE:} + +# 代理集群 +agent.cluster=${SW_AGENT_CLUSTER:} + +# 每3秒采样的轨迹数 +# 负或零表示关闭,默认情况下 +agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:-1} + +# 身份验证活动基于后端设置,请参阅application.yml了解更多细节。 +agent.authentication=${SW_AGENT_AUTHENTICATION:} + +# 单个跨度中TraceSegmentRef的最大数量,以保持内存成本可估算。 +agent.trace_segment_ref_limit_per_span=${SW_TRACE_SEGMENT_LIMIT:500} + +# 单个段中跨度的最大数量。 +# 通过这个配置项,SkyWalking保持你的应用内存成本估计。 +agent.span_limit_per_segment=${SW_AGENT_SPAN_LIMIT:300} + +#如果第一个span的操作名包含在此集合中,则应忽略此段。多个值应该用‘,’隔开。 +agent.ignore_suffix=${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg} + +# 如果为true,则SkyWalking代理将所有检测的类文件保存在 “/debugging” 文件夹中。 +# SkyWalking团队可能会要求这些文件以解决兼容问题。 +agent.is_open_debugging_class=${SW_AGENT_OPEN_DEBUG:false} + +# 实例名是实例的标识,在服务中唯一。如果空着,空中行走代理将 +# 生成32位uuid。默认情况下,SkyWalking使用UUID @ hostname作为实例名称。最大长度为50(UTF-8字符) +agent.instance_name=${SW_AGENT_INSTANCE_NAME:} +agent.instance_name#length=${SW_AGENT_INSTANCE_NAME_MAX_LENGTH:50} + +# json格式的服务实例属性。例如agent.instance_properties_json = {"org": "apache-skywalking"} +agent.instance_properties_json=${SW_INSTANCE_PROPERTIES_JSON:} + +# 代理的深度如何,记录时都会导致异常。 +agent.cause_exception_depth=${SW_AGENT_CAUSE_EXCEPTION_DEPTH:5} + +# 强制grpc的重连接周期,基于grpc_channel_check_interval。 +agent.force_reconnection_period=${SW_AGENT_FORCE_RECONNECTION_PERIOD:1} + +# operationName最大长度 +# 注意,在目前的实践中,我们不建议长度超过190。 +agent.operation_name_threshold=${SW_AGENT_OPERATION_NAME_THRESHOLD:150} + +# 如果此值为true,即使后端不可用,也要继续跟踪。 +agent.keep_tracing=${SW_AGENT_KEEP_TRACING:false} + +# 代理默认使用gRPC纯文本。 +# 如果为true,即使没有检测到CA文件,SkyWalking代理也使用TLS。 +agent.force_tls=${SW_AGENT_FORCE_TLS:false} + +# gRPC SSL可信ca文件。 +agent.ssl_trusted_ca_path=${SW_AGENT_SSL_TRUSTED_CA_PATH:/ca/ca.crt} + +# 当存在ssl_key_path和ssl_cert_chain_path时,启用mTLS。 +agent.ssl_key_path=${SW_AGENT_SSL_KEY_PATH:} + +agent.ssl_cert_chain_path=${SW_AGENT_SSL_CERT_CHAIN_PATH:} + +# 启用代理内核服务和检测。 +agent.enable=${SW_AGENT_ENABLE:true} + +# 限制ipv4列表大小的长度。 +osinfo.ipv4_list_size=${SW_AGENT_OSINFO_IPV4_LIST_SIZE:10} + +# grpc信道状态检查间隔。 +collector.grpc_channel_check_interval=${SW_AGENT_COLLECTOR_GRPC_CHANNEL_CHECK_INTERVAL:30} +# 代理心跳报告期。第二小队。 +collector.heartbeat_period=${SW_AGENT_COLLECTOR_HEARTBEAT_PERIOD:10} +# agent将实例属性发送到后端 +# collector.heartbeat_period * collector.properties_report_period_factor秒 +collector.properties_report_period_factor=${SW_AGENT_COLLECTOR_PROPERTIES_REPORT_PERIOD_FACTOR:5} +# 后端服务地址。 +collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.138.130:11800} +# grpc客户端向上游发送数据的超时时间。单位是秒。 +collector.grpc_upstream_timeout=${SW_AGENT_COLLECTOR_GRPC_UPSTREAM_TIMEOUT:30} +# 嗅探器获取配置文件任务列表间隔。 +collector.get_profile_task_interval=${SW_AGENT_COLLECTOR_GET_PROFILE_TASK_INTERVAL:20} +# 嗅探器获取代理动态配置间隔。 +collector.get_agent_dynamic_config_interval=${SW_AGENT_COLLECTOR_GET_AGENT_DYNAMIC_CONFIG_INTERVAL:20} +# 如果为true,skywalking代理将启用定期解析DNS以更新接收方服务地址。 +collector.is_resolve_dns_periodically=${SW_AGENT_COLLECTOR_IS_RESOLVE_DNS_PERIODICALLY:false} + +# Logging level +logging.level=${SW_LOGGING_LEVEL:INFO} +# Logging file_name +logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log} +# Log output. Default is FILE. Use CONSOLE means output to stdout. +logging.output=${SW_LOGGING_OUTPUT:FILE} +# Log files directory. Default is blank string, meaning use "{theSkywalkingAgentJarDir}/logs " to output logs. +# {theSkywalkingAgentJarDir} is the directory where the skywalking agent jar file is located +logging.dir=${SW_LOGGING_DIR:} +# Logger resolver: PATTERN or JSON. The default is PATTERN, which uses logging.pattern to print traditional text logs. +# JSON resolver prints logs in JSON format. +logging.resolver=${SW_LOGGING_RESOLVER:PATTERN} +# Logging format. There are all conversion specifiers: +# * %level means log level. +# * %timestamp means now of time with format yyyy-MM-dd HH:mm:ss:SSS. +# * %thread means name of current thread. +# * %msg means some message which user logged. +# * %class means SimpleName of TargetClass. +# * %throwable means a throwable which user called. +# * %agent_name means agent.service_name. Only apply to the PatternLogger. +logging.pattern=${SW_LOGGING_PATTERN:%level %timestamp %thread %class : %msg %throwable} +# Logging max_file_size, default: 300 * 1024 * 1024 = 314572800 +logging.max_file_size=${SW_LOGGING_MAX_FILE_SIZE:314572800} +# The max history log files. When rollover happened, if log files exceed this number, +# then the oldest file will be delete. Negative or zero means off, by default. +logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:-1} + +# Listed exceptions would not be treated as an error. Because in some codes, the exception is being used as a way of controlling business flow. +# Besides, the annotation named IgnoredException in the trace toolkit is another way to configure ignored exceptions. +statuscheck.ignored_exceptions=${SW_STATUSCHECK_IGNORED_EXCEPTIONS:} +# The max recursive depth when checking the exception traced by the agent. Typically, we don't recommend setting this more than 10, which could cause a performance issue. Negative value and 0 would be ignored, which means all exceptions would make the span tagged in error status. +statuscheck.max_recursive_depth=${SW_STATUSCHECK_MAX_RECURSIVE_DEPTH:1} + +# Max element count in the correlation context +correlation.element_max_number=${SW_CORRELATION_ELEMENT_MAX_NUMBER:3} + +# Max value length of each element. +correlation.value_max_length=${SW_CORRELATION_VALUE_MAX_LENGTH:128} +# Tag the span by the key/value in the correlation context, when the keys listed here exist. +correlation.auto_tag_keys=${SW_CORRELATION_AUTO_TAG_KEYS:} +# The buffer size of collected JVM info. +jvm.buffer_size=${SW_JVM_BUFFER_SIZE:600} +# The period in seconds of JVM metrics collection. Unit is second. +jvm.metrics_collect_period=${SW_JVM_METRICS_COLLECT_PERIOD:1} +# The buffer channel size. +buffer.channel_size=${SW_BUFFER_CHANNEL_SIZE:5} +# The buffer size. +buffer.buffer_size=${SW_BUFFER_BUFFER_SIZE:300} +# If true, skywalking agent will enable profile when user create a new profile task. Otherwise disable profile. +profile.active=${SW_AGENT_PROFILE_ACTIVE:true} +# Parallel monitor endpoint thread count +profile.max_parallel=${SW_AGENT_PROFILE_MAX_PARALLEL:5} +# Max monitoring sub-tasks count of one single endpoint access +profile.max_accept_sub_parallel=${SW_AGENT_PROFILE_MAX_ACCEPT_SUB_PARALLEL:5} +# Max monitor segment time(minutes), if current segment monitor time out of limit, then stop it. +profile.duration=${SW_AGENT_PROFILE_DURATION:10} +# Max dump thread stack depth +profile.dump_max_stack_depth=${SW_AGENT_PROFILE_DUMP_MAX_STACK_DEPTH:500} +# Snapshot transport to backend buffer size +profile.snapshot_transport_buffer_size=${SW_AGENT_PROFILE_SNAPSHOT_TRANSPORT_BUFFER_SIZE:4500} +# If true, the agent collects and reports metrics to the backend. +meter.active=${SW_METER_ACTIVE:true} +# Report meters interval. The unit is second +meter.report_interval=${SW_METER_REPORT_INTERVAL:20} +# Max size of the meter pool +meter.max_meter_size=${SW_METER_MAX_METER_SIZE:500} +# The max size of message to send to server.Default is 10 MB +log.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760} + +# Mount the specific folders of the plugins. Plugins in mounted folders would work. +plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations} +# Peer maximum description limit. +plugin.peer_max_length=${SW_PLUGIN_PEER_MAX_LENGTH:200} +# Exclude some plugins define in plugins dir.Plugin names is defined in [Agent plugin list](Plugin-list.md) +plugin.exclude_plugins=${SW_EXCLUDE_PLUGINS:} +# If true, trace all the parameters in MongoDB access, default is false. Only trace the operation, not include parameters. +plugin.mongodb.trace_param=${SW_PLUGIN_MONGODB_TRACE_PARAM:false} +# If set to positive number, the `WriteRequest.params` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. +plugin.mongodb.filter_length_limit=${SW_PLUGIN_MONGODB_FILTER_LENGTH_LIMIT:256} +# If true, trace all the DSL(Domain Specific Language) in ElasticSearch access, default is false. +plugin.elasticsearch.trace_dsl=${SW_PLUGIN_ELASTICSEARCH_TRACE_DSL:false} +# If true, the fully qualified method name will be used as the endpoint name instead of the request URL, default is false. +plugin.springmvc.use_qualified_name_as_endpoint_name=${SW_PLUGIN_SPRINGMVC_USE_QUALIFIED_NAME_AS_ENDPOINT_NAME:false} +# If true, the fully qualified method name will be used as the operation name instead of the given operation name, default is false. +plugin.toolkit.use_qualified_name_as_operation_name=${SW_PLUGIN_TOOLKIT_USE_QUALIFIED_NAME_AS_OPERATION_NAME:false} +# If set to true, the parameters of the sql (typically `java.sql.PreparedStatement`) would be collected. +plugin.jdbc.trace_sql_parameters=${SW_JDBC_TRACE_SQL_PARAMETERS:false} +# If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. +plugin.jdbc.sql_parameters_max_length=${SW_PLUGIN_JDBC_SQL_PARAMETERS_MAX_LENGTH:512} +# If set to positive number, the `db.statement` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. +plugin.jdbc.sql_body_max_length=${SW_PLUGIN_JDBC_SQL_BODY_MAX_LENGTH:2048} +# If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, default is false. +plugin.solrj.trace_statement=${SW_PLUGIN_SOLRJ_TRACE_STATEMENT:false} +# If true, trace all the operation parameters in Solr request, default is false. +plugin.solrj.trace_ops_params=${SW_PLUGIN_SOLRJ_TRACE_OPS_PARAMS:false} +# If true, trace all middleware/business handlers that are part of the Light4J handler chain for a request. +plugin.light4j.trace_handler_chain=${SW_PLUGIN_LIGHT4J_TRACE_HANDLER_CHAIN:false} +# If true, the transaction definition name will be simplified. +plugin.springtransaction.simplify_transaction_definition_name=${SW_PLUGIN_SPRINGTRANSACTION_SIMPLIFY_TRANSACTION_DEFINITION_NAME:false} +# Threading classes (`java.lang.Runnable` and `java.util.concurrent.Callable`) and their subclasses, including anonymous inner classes whose name match any one of the `THREADING_CLASS_PREFIXES` (splitted by `,`) will be instrumented, make sure to only specify as narrow prefixes as what you're expecting to instrument, (`java.` and `javax.` will be ignored due to safety issues) +plugin.jdkthreading.threading_class_prefixes=${SW_PLUGIN_JDKTHREADING_THREADING_CLASS_PREFIXES:} +# This config item controls that whether the Tomcat plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. +plugin.tomcat.collect_http_params=${SW_PLUGIN_TOMCAT_COLLECT_HTTP_PARAMS:false} +# This config item controls that whether the SpringMVC plugin should collect the parameters of the request, when your Spring application is based on Tomcat, consider only setting either `plugin.tomcat.collect_http_params` or `plugin.springmvc.collect_http_params`. Also, activate implicitly in the profiled trace. +plugin.springmvc.collect_http_params=${SW_PLUGIN_SPRINGMVC_COLLECT_HTTP_PARAMS:false} +# This config item controls that whether the HttpClient plugin should collect the parameters of the request +plugin.httpclient.collect_http_params=${SW_PLUGIN_HTTPCLIENT_COLLECT_HTTP_PARAMS:false} +# When `COLLECT_HTTP_PARAMS` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete parameters, NB. this config item is added for the sake of performance. +plugin.http.http_params_length_threshold=${SW_PLUGIN_HTTP_HTTP_PARAMS_LENGTH_THRESHOLD:1024} +# When `include_http_headers` declares header names, this threshold controls the length limitation of all header values. use negative values to keep and send the complete headers. Note. this config item is added for the sake of performance. +plugin.http.http_headers_length_threshold=${SW_PLUGIN_HTTP_HTTP_HEADERS_LENGTH_THRESHOLD:2048} +# Set the header names, which should be collected by the plugin. Header name must follow `jakarta.servlet.http` definition. Multiple names should be split by comma. +plugin.http.include_http_headers=${SW_PLUGIN_HTTP_INCLUDE_HTTP_HEADERS:} +# This config item controls that whether the Feign plugin should collect the http body of the request. +plugin.feign.collect_request_body=${SW_PLUGIN_FEIGN_COLLECT_REQUEST_BODY:false} +# When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. +plugin.feign.filter_length_limit=${SW_PLUGIN_FEIGN_FILTER_LENGTH_LIMIT:1024} +# When `COLLECT_REQUEST_BODY` is enabled and content-type start with SUPPORTED_CONTENT_TYPES_PREFIX, collect the body of the request , multiple paths should be separated by `,` +plugin.feign.supported_content_types_prefix=${SW_PLUGIN_FEIGN_SUPPORTED_CONTENT_TYPES_PREFIX:application/json,text/} +# If true, trace all the influxql(query and write) in InfluxDB access, default is true. +plugin.influxdb.trace_influxql=${SW_PLUGIN_INFLUXDB_TRACE_INFLUXQL:true} +# Apache Dubbo consumer collect `arguments` in RPC call, use `Object#toString` to collect `arguments`. +plugin.dubbo.collect_consumer_arguments=${SW_PLUGIN_DUBBO_COLLECT_CONSUMER_ARGUMENTS:false} +# When `plugin.dubbo.collect_consumer_arguments` is `true`, Arguments of length from the front will to the OAP backend +plugin.dubbo.consumer_arguments_length_threshold=${SW_PLUGIN_DUBBO_CONSUMER_ARGUMENTS_LENGTH_THRESHOLD:256} +# Apache Dubbo provider collect `arguments` in RPC call, use `Object#toString` to collect `arguments`. +plugin.dubbo.collect_provider_arguments=${SW_PLUGIN_DUBBO_COLLECT_PROVIDER_ARGUMENTS:false} +# When `plugin.dubbo.collect_provider_arguments` is `true`, Arguments of length from the front will to the OAP backend +plugin.dubbo.provider_arguments_length_threshold=${SW_PLUGIN_DUBBO_PROVIDER_ARGUMENTS_LENGTH_THRESHOLD:256} +# A list of host/port pairs to use for establishing the initial connection to the Kafka cluster. +plugin.kafka.bootstrap_servers=${SW_KAFKA_BOOTSTRAP_SERVERS:localhost:9092} +# Timeout period of reading topics from the Kafka server, the unit is second. +plugin.kafka.get_topic_timeout=${SW_GET_TOPIC_TIMEOUT:10} +# Kafka producer configuration. Read [producer configure](http://kafka.apache.org/24/documentation.html#producerconfigs) +# to get more details. Check document for more details and examples. +plugin.kafka.producer_config=${SW_PLUGIN_KAFKA_PRODUCER_CONFIG:} +# Configure Kafka Producer configuration in JSON format. Notice it will be overridden by plugin.kafka.producer_config[key], if the key duplication. +plugin.kafka.producer_config_json=${SW_PLUGIN_KAFKA_PRODUCER_CONFIG_JSON:} +# Specify which Kafka topic name for Meter System data to report to. +plugin.kafka.topic_meter=${SW_PLUGIN_KAFKA_TOPIC_METER:skywalking-meters} +# Specify which Kafka topic name for JVM metrics data to report to. +plugin.kafka.topic_metrics=${SW_PLUGIN_KAFKA_TOPIC_METRICS:skywalking-metrics} +# Specify which Kafka topic name for traces data to report to. +plugin.kafka.topic_segment=${SW_PLUGIN_KAFKA_TOPIC_SEGMENT:skywalking-segments} +# Specify which Kafka topic name for Thread Profiling snapshot to report to. +plugin.kafka.topic_profiling=${SW_PLUGIN_KAFKA_TOPIC_PROFILINGS:skywalking-profilings} +# Specify which Kafka topic name for the register or heartbeat data of Service Instance to report to. +plugin.kafka.topic_management=${SW_PLUGIN_KAFKA_TOPIC_MANAGEMENT:skywalking-managements} +# Specify which Kafka topic name for the logging data to report to. +plugin.kafka.topic_logging=${SW_PLUGIN_KAFKA_TOPIC_LOGGING:skywalking-logs} +# isolate multi OAP server when using same Kafka cluster (final topic name will append namespace before Kafka topics with `-` ). +plugin.kafka.namespace=${SW_KAFKA_NAMESPACE:} +# Specify which class to decode encoded configuration of kafka.You can set encoded information in `plugin.kafka.producer_config_json` or `plugin.kafka.producer_config` if you need. +plugin.kafka.decode_class=${SW_KAFKA_DECODE_CLASS:} +# Match spring beans with regular expression for the class name. Multiple expressions could be separated by a comma. This only works when `Spring annotation plugin` has been activated. +plugin.springannotation.classname_match_regex=${SW_SPRINGANNOTATION_CLASSNAME_MATCH_REGEX:} +# Whether or not to transmit logged data as formatted or un-formatted. +plugin.toolkit.log.transmit_formatted=${SW_PLUGIN_TOOLKIT_LOG_TRANSMIT_FORMATTED:true} +# If set to true, the parameters of Redis commands would be collected by Lettuce agent. +plugin.lettuce.trace_redis_parameters=${SW_PLUGIN_LETTUCE_TRACE_REDIS_PARAMETERS:false} +# If set to positive number and `plugin.lettuce.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length. +plugin.lettuce.redis_parameter_max_length=${SW_PLUGIN_LETTUCE_REDIS_PARAMETER_MAX_LENGTH:128} +# Specify which command should be converted to write operation +plugin.lettuce.operation_mapping_write=${SW_PLUGIN_LETTUCE_OPERATION_MAPPING_WRITE:getset,set,setbit,setex,setnx,setrange,strlen,mset,msetnx,psetex,incr,incrby,incrbyfloat,decr,decrby,append,hmset,hset,hsetnx,hincrby,hincrbyfloat,hdel,rpoplpush,rpush,rpushx,lpush,lpushx,lrem,ltrim,lset,brpoplpush,linsert,sadd,sdiff,sdiffstore,sinterstore,sismember,srem,sunion,sunionstore,sinter,zadd,zincrby,zinterstore,zrange,zrangebylex,zrangebyscore,zrank,zrem,zremrangebylex,zremrangebyrank,zremrangebyscore,zrevrange,zrevrangebyscore,zrevrank,zunionstore,xadd,xdel,del,xtrim} +# Specify which command should be converted to read operation +plugin.lettuce.operation_mapping_read=${SW_PLUGIN_LETTUCE_OPERATION_MAPPING_READ:getrange,getbit,mget,hvals,hkeys,hlen,hexists,hget,hgetall,hmget,blpop,brpop,lindex,llen,lpop,lrange,rpop,scard,srandmember,spop,sscan,smove,zlexcount,zscore,zscan,zcard,zcount,xget,get,xread,xlen,xrange,xrevrange} +# If set to true, the parameters of the cypher would be collected. +plugin.neo4j.trace_cypher_parameters=${SW_PLUGIN_NEO4J_TRACE_CYPHER_PARAMETERS:false} +# If set to positive number, the `db.cypher.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. +plugin.neo4j.cypher_parameters_max_length=${SW_PLUGIN_NEO4J_CYPHER_PARAMETERS_MAX_LENGTH:512} +# If set to positive number, the `db.statement` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. +plugin.neo4j.cypher_body_max_length=${SW_PLUGIN_NEO4J_CYPHER_BODY_MAX_LENGTH:2048} +# If set to a positive number and activate `trace sampler CPU policy plugin`, the trace would not be collected when agent process CPU usage percent is greater than `plugin.cpupolicy.sample_cpu_usage_percent_limit`. +plugin.cpupolicy.sample_cpu_usage_percent_limit=${SW_SAMPLE_CPU_USAGE_PERCENT_LIMIT:-1} +# This config item controls that whether the Micronaut http client plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. +plugin.micronauthttpclient.collect_http_params=${SW_PLUGIN_MICRONAUTHTTPCLIENT_COLLECT_HTTP_PARAMS:false} +# This config item controls that whether the Micronaut http server plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. +plugin.micronauthttpserver.collect_http_params=${SW_PLUGIN_MICRONAUTHTTPSERVER_COLLECT_HTTP_PARAMS:false} +# Specify which command should be converted to write operation +plugin.memcached.operation_mapping_write=${SW_PLUGIN_MEMCACHED_OPERATION_MAPPING_WRITE:set,add,replace,append,prepend,cas,delete,touch,incr,decr} +# Specify which command should be converted to read operation +plugin.memcached.operation_mapping_read=${SW_PLUGIN_MEMCACHED_OPERATION_MAPPING_READ:get,gets,getAndTouch,getKeys,getKeysWithExpiryCheck,getKeysNoDuplicateCheck} +# Specify which command should be converted to write operation +plugin.ehcache.operation_mapping_write=${SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE:tryRemoveImmediately,remove,removeAndReturnElement,removeAll,removeQuiet,removeWithWriter,put,putAll,replace,removeQuiet,removeWithWriter,removeElement,removeAll,putWithWriter,putQuiet,putIfAbsent,putIfAbsent} +# Specify which command should be converted to read operation +plugin.ehcache.operation_mapping_read=${SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ:get,getAll,getQuiet,getKeys,getKeysWithExpiryCheck,getKeysNoDuplicateCheck,releaseRead,tryRead,getWithLoader,getAll,loadAll,getAllWithLoader} +# Specify which command should be converted to write operation +plugin.guavacache.operation_mapping_write=${SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE:put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp} +# Specify which command should be converted to read operation +plugin.guavacache.operation_mapping_read=${SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ:getIfPresent,get,getAllPresent,size} +# If set to true, the parameters of Redis commands would be collected by Jedis agent. +plugin.jedis.trace_redis_parameters=${SW_PLUGIN_JEDIS_TRACE_REDIS_PARAMETERS:false} +# If set to positive number and plugin.jedis.trace_redis_parameters is set to true, Redis command parameters would be collected and truncated to this length. +plugin.jedis.redis_parameter_max_length=${SW_PLUGIN_JEDIS_REDIS_PARAMETER_MAX_LENGTH:128} +# Specify which command should be converted to write operation +plugin.jedis.operation_mapping_write=${SW_PLUGIN_JEDIS_OPERATION_MAPPING_WRITE:getset,set,setbit,setex,setnx,setrange,strlen,mset,msetnx,psetex,incr,incrby,incrbyfloat,decr,decrby,append,hmset,hset,hsetnx,hincrby,hincrbyfloat,hdel,rpoplpush,rpush,rpushx,lpush,lpushx,lrem,ltrim,lset,brpoplpush,linsert,sadd,sdiff,sdiffstore,sinterstore,sismember,srem,sunion,sunionstore,sinter,zadd,zincrby,zinterstore,zrange,zrangebylex,zrangebyscore,zrank,zrem,zremrangebylex,zremrangebyrank,zremrangebyscore,zrevrange,zrevrangebyscore,zrevrank,zunionstore,xadd,xdel,del,xtrim} +# Specify which command should be converted to read operation +plugin.jedis.operation_mapping_read=${SW_PLUGIN_JEDIS_OPERATION_MAPPING_READ:getrange,getbit,mget,hvals,hkeys,hlen,hexists,hget,hgetall,hmget,blpop,brpop,lindex,llen,lpop,lrange,rpop,scard,srandmember,spop,sscan,smove,zlexcount,zscore,zscan,zcard,zcount,xget,get,xread,xlen,xrange,xrevrange} +# If set to true, the parameters of Redis commands would be collected by Redisson agent. +plugin.redisson.trace_redis_parameters=${SW_PLUGIN_REDISSON_TRACE_REDIS_PARAMETERS:false} +# If set to positive number and plugin.redisson.trace_redis_parameters is set to true, Redis command parameters would be collected and truncated to this length. +plugin.redisson.redis_parameter_max_length=${SW_PLUGIN_REDISSON_REDIS_PARAMETER_MAX_LENGTH:128} +# Specify which command should be converted to write operation +plugin.redisson.operation_mapping_write=${SW_PLUGIN_REDISSON_OPERATION_MAPPING_WRITE:getset,set,setbit,setex,setnx,setrange,strlen,mset,msetnx,psetex,incr,incrby,incrbyfloat,decr,decrby,append,hmset,hset,hsetnx,hincrby,hincrbyfloat,hdel,rpoplpush,rpush,rpushx,lpush,lpushx,lrem,ltrim,lset,brpoplpush,linsert,sadd,sdiff,sdiffstore,sinterstore,sismember,srem,sunion,sunionstore,sinter,zadd,zincrby,zinterstore,zrange,zrangebylex,zrangebyscore,zrank,zrem,zremrangebylex,zremrangebyrank,zremrangebyscore,zrevrange,zrevrangebyscore,zrevrank,zunionstore,xadd,xdel,del,xtrim} +# Specify which command should be converted to read operation +plugin.redisson.operation_mapping_read=${SW_PLUGIN_REDISSON_OPERATION_MAPPING_READ:getrange,getbit,mget,hvals,hkeys,hlen,hexists,hget,hgetall,hmget,blpop,brpop,lindex,llen,lpop,lrange,rpop,scard,srandmember,spop,sscan,smove,zlexcount,zscore,zscan,zcard,zcount,xget,get,xread,xlen,xrange,xrevrange} +# This config item controls that whether the Netty-http plugin should collect the http body of the request. +plugin.nettyhttp.collect_request_body=${SW_PLUGIN_NETTYHTTP_COLLECT_REQUEST_BODY:false} +# When `HTTP_COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. +plugin.nettyhttp.filter_length_limit=${SW_PLUGIN_NETTYHTTP_FILTER_LENGTH_LIMIT:1024} +# When `HTTP_COLLECT_REQUEST_BODY` is enabled and content-type start with HTTP_SUPPORTED_CONTENT_TYPES_PREFIX, collect the body of the request , multiple paths should be separated by `,` +plugin.nettyhttp.supported_content_types_prefix=${SW_PLUGIN_NETTYHTTP_SUPPORTED_CONTENT_TYPES_PREFIX:application/json,text/} +# If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. +plugin.rocketmqclient.collect_message_keys=${SW_PLUGIN_ROCKETMQCLIENT_COLLECT_MESSAGE_KEYS:false} +# If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. +plugin.rocketmqclient.collect_message_tags=${SW_PLUGIN_ROCKETMQCLIENT_COLLECT_MESSAGE_TAGS:false} diff --git a/skywalking/agent/expired-plugins/apm-impala-jdbc-2.6.x-plugin-9.2.0.jar b/skywalking/agent/expired-plugins/apm-impala-jdbc-2.6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..0ec6c0b Binary files /dev/null and b/skywalking/agent/expired-plugins/apm-impala-jdbc-2.6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/licenses/LICENSE-asm.txt b/skywalking/agent/licenses/LICENSE-asm.txt new file mode 100644 index 0000000..afb064f --- /dev/null +++ b/skywalking/agent/licenses/LICENSE-asm.txt @@ -0,0 +1,26 @@ +Copyright (c) 2012 France Télécom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/skywalking/agent/optional-plugins/apm-customize-enhance-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-customize-enhance-plugin-9.2.0.jar new file mode 100644 index 0000000..95fea1f Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-customize-enhance-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-ehcache-2.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-ehcache-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7fcc331 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-ehcache-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-fastjson-1.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-fastjson-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..9e37426 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-fastjson-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-gson-2.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-gson-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..5a3efc8 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-gson-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-guava-cache-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-guava-cache-plugin-9.2.0.jar new file mode 100644 index 0000000..f99308c Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-guava-cache-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-jackson-2.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-jackson-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..9643e73 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-jackson-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-kotlin-coroutine-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-kotlin-coroutine-plugin-9.2.0.jar new file mode 100644 index 0000000..a9ab20b Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-kotlin-coroutine-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-mybatis-3.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-mybatis-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ae58435 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-mybatis-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-nacos-client-2.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-nacos-client-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..a022b1b Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-nacos-client-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-netty-http-4.1.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-netty-http-4.1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..cff0ce0 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-netty-http-4.1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-quartz-scheduler-2.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-quartz-scheduler-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..0c21737 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-quartz-scheduler-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-resttemplate-6.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-resttemplate-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ae5d24b Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-resttemplate-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-sentinel-1.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-sentinel-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..d131c0f Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-sentinel-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-shenyu-2.4.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-shenyu-2.4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..8810511 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-shenyu-2.4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-annotation-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-annotation-plugin-9.2.0.jar new file mode 100644 index 0000000..f619cc8 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-annotation-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.0.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.0.x-plugin-9.2.0.jar new file mode 100644 index 0000000..2929e10 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.0.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.1.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f09d37b Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-3.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f3d5c87 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-4.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ddd167c Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-cloud-gateway-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-tx-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-tx-plugin-9.2.0.jar new file mode 100644 index 0000000..5f1b322 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-tx-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-webflux-5.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-webflux-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..733277b Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-webflux-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-spring-webflux-6.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-spring-webflux-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..114dfe3 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-spring-webflux-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-springmvc-annotation-6.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-springmvc-annotation-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ee096aa Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-springmvc-annotation-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-trace-ignore-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-trace-ignore-plugin-9.2.0.jar new file mode 100644 index 0000000..b20b690 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-trace-ignore-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/apm-zookeeper-3.4.x-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/apm-zookeeper-3.4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..3e65818 Binary files /dev/null and b/skywalking/agent/optional-plugins/apm-zookeeper-3.4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-plugins/trace-sampler-cpu-policy-plugin-9.2.0.jar b/skywalking/agent/optional-plugins/trace-sampler-cpu-policy-plugin-9.2.0.jar new file mode 100644 index 0000000..5763e14 Binary files /dev/null and b/skywalking/agent/optional-plugins/trace-sampler-cpu-policy-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-reporter-plugins/kafka-reporter-plugin-9.2.0.jar b/skywalking/agent/optional-reporter-plugins/kafka-reporter-plugin-9.2.0.jar new file mode 100644 index 0000000..60949eb Binary files /dev/null and b/skywalking/agent/optional-reporter-plugins/kafka-reporter-plugin-9.2.0.jar differ diff --git a/skywalking/agent/optional-reporter-plugins/lz4-java-1.6.0.jar b/skywalking/agent/optional-reporter-plugins/lz4-java-1.6.0.jar new file mode 100644 index 0000000..ad5de82 Binary files /dev/null and b/skywalking/agent/optional-reporter-plugins/lz4-java-1.6.0.jar differ diff --git a/skywalking/agent/optional-reporter-plugins/snappy-java-1.1.7.3.jar b/skywalking/agent/optional-reporter-plugins/snappy-java-1.1.7.3.jar new file mode 100644 index 0000000..afda191 Binary files /dev/null and b/skywalking/agent/optional-reporter-plugins/snappy-java-1.1.7.3.jar differ diff --git a/skywalking/agent/optional-reporter-plugins/zstd-jni-1.4.3-1.jar b/skywalking/agent/optional-reporter-plugins/zstd-jni-1.4.3-1.jar new file mode 100644 index 0000000..9fb89a4 Binary files /dev/null and b/skywalking/agent/optional-reporter-plugins/zstd-jni-1.4.3-1.jar differ diff --git a/skywalking/agent/plugins/apm-activemq-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-activemq-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ca141b7 Binary files /dev/null and b/skywalking/agent/plugins/apm-activemq-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-activemq-artemis-jakarta-client-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-activemq-artemis-jakarta-client-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e01ab2a Binary files /dev/null and b/skywalking/agent/plugins/apm-activemq-artemis-jakarta-client-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-aerospike-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-aerospike-plugin-9.2.0.jar new file mode 100644 index 0000000..96e925a Binary files /dev/null and b/skywalking/agent/plugins/apm-aerospike-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-armeria-0.84.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-armeria-0.84.x-plugin-9.2.0.jar new file mode 100644 index 0000000..9d26976 Binary files /dev/null and b/skywalking/agent/plugins/apm-armeria-0.84.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-armeria-0.85.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-armeria-0.85.x-plugin-9.2.0.jar new file mode 100644 index 0000000..64b96d2 Binary files /dev/null and b/skywalking/agent/plugins/apm-armeria-0.85.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-armeria-1.0.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-armeria-1.0.x-plugin-9.2.0.jar new file mode 100644 index 0000000..a2af0fe Binary files /dev/null and b/skywalking/agent/plugins/apm-armeria-1.0.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-asynchttpclient-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-asynchttpclient-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..34cb78e Binary files /dev/null and b/skywalking/agent/plugins/apm-asynchttpclient-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-avro-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-avro-plugin-9.2.0.jar new file mode 100644 index 0000000..77bbf3a Binary files /dev/null and b/skywalking/agent/plugins/apm-avro-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-canal-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-canal-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..8e2f3b0 Binary files /dev/null and b/skywalking/agent/plugins/apm-canal-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-cassandra-java-driver-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-cassandra-java-driver-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..8c585b9 Binary files /dev/null and b/skywalking/agent/plugins/apm-cassandra-java-driver-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-clickhouse-0.3.1-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-clickhouse-0.3.1-plugin-9.2.0.jar new file mode 100644 index 0000000..b247bf2 Binary files /dev/null and b/skywalking/agent/plugins/apm-clickhouse-0.3.1-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-clickhouse-0.3.2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-clickhouse-0.3.2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..249e3f4 Binary files /dev/null and b/skywalking/agent/plugins/apm-clickhouse-0.3.2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-cxf-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-cxf-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..3491c44 Binary files /dev/null and b/skywalking/agent/plugins/apm-cxf-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-dubbo-2.7.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-dubbo-2.7.x-plugin-9.2.0.jar new file mode 100644 index 0000000..34777af Binary files /dev/null and b/skywalking/agent/plugins/apm-dubbo-2.7.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-dubbo-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-dubbo-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..6decdd1 Binary files /dev/null and b/skywalking/agent/plugins/apm-dubbo-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-dubbo-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-dubbo-plugin-9.2.0.jar new file mode 100644 index 0000000..0690afd Binary files /dev/null and b/skywalking/agent/plugins/apm-dubbo-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-elastic-job-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-elastic-job-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..c50bdc8 Binary files /dev/null and b/skywalking/agent/plugins/apm-elastic-job-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-elasticjob-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-elasticjob-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..3d3d478 Binary files /dev/null and b/skywalking/agent/plugins/apm-elasticjob-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-elasticsearch-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-elasticsearch-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..bad10a5 Binary files /dev/null and b/skywalking/agent/plugins/apm-elasticsearch-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-elasticsearch-6.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-elasticsearch-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..5ad7d91 Binary files /dev/null and b/skywalking/agent/plugins/apm-elasticsearch-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-elasticsearch-7.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-elasticsearch-7.x-plugin-9.2.0.jar new file mode 100644 index 0000000..a6a8472 Binary files /dev/null and b/skywalking/agent/plugins/apm-elasticsearch-7.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-feign-default-http-9.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-feign-default-http-9.x-plugin-9.2.0.jar new file mode 100644 index 0000000..dadf442 Binary files /dev/null and b/skywalking/agent/plugins/apm-feign-default-http-9.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-finagle-6.25.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-finagle-6.25.x-plugin-9.2.0.jar new file mode 100644 index 0000000..94ef51a Binary files /dev/null and b/skywalking/agent/plugins/apm-finagle-6.25.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-grizzly-2.x-4.x-work-threadpool-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-grizzly-2.x-4.x-work-threadpool-plugin-9.2.0.jar new file mode 100644 index 0000000..0be3a0d Binary files /dev/null and b/skywalking/agent/plugins/apm-grizzly-2.x-4.x-work-threadpool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-grizzly-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-grizzly-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..b3a0cce Binary files /dev/null and b/skywalking/agent/plugins/apm-grizzly-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-grpc-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-grpc-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f3ca8e7 Binary files /dev/null and b/skywalking/agent/plugins/apm-grpc-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-guava-eventbus-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-guava-eventbus-plugin-9.2.0.jar new file mode 100644 index 0000000..46f53cb Binary files /dev/null and b/skywalking/agent/plugins/apm-guava-eventbus-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-h2-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-h2-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..5fdcff2 Binary files /dev/null and b/skywalking/agent/plugins/apm-h2-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-hbase-1.x-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-hbase-1.x-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..947e3ec Binary files /dev/null and b/skywalking/agent/plugins/apm-hbase-1.x-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-hikaricp-3.x-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-hikaricp-3.x-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..6ad4a27 Binary files /dev/null and b/skywalking/agent/plugins/apm-hikaricp-3.x-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-httpClient-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-httpClient-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ea77f00 Binary files /dev/null and b/skywalking/agent/plugins/apm-httpClient-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-httpasyncclient-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-httpasyncclient-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..d2e960d Binary files /dev/null and b/skywalking/agent/plugins/apm-httpasyncclient-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-httpclient-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-httpclient-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..77dbdb2 Binary files /dev/null and b/skywalking/agent/plugins/apm-httpclient-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-httpclient-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-httpclient-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..2c89b4a Binary files /dev/null and b/skywalking/agent/plugins/apm-httpclient-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-httpclient-commons-9.2.0.jar b/skywalking/agent/plugins/apm-httpclient-commons-9.2.0.jar new file mode 100644 index 0000000..bd7fc47 Binary files /dev/null and b/skywalking/agent/plugins/apm-httpclient-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-hutool-http-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-hutool-http-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..4e386f9 Binary files /dev/null and b/skywalking/agent/plugins/apm-hutool-http-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-hystrix-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-hystrix-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e4f4f08 Binary files /dev/null and b/skywalking/agent/plugins/apm-hystrix-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-influxdb-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-influxdb-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..edd45c1 Binary files /dev/null and b/skywalking/agent/plugins/apm-influxdb-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jdbc-commons-9.2.0.jar b/skywalking/agent/plugins/apm-jdbc-commons-9.2.0.jar new file mode 100644 index 0000000..f29e69c Binary files /dev/null and b/skywalking/agent/plugins/apm-jdbc-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jersey-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jersey-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1870989 Binary files /dev/null and b/skywalking/agent/plugins/apm-jersey-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jersey-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jersey-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..851510a Binary files /dev/null and b/skywalking/agent/plugins/apm-jersey-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-client-11.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-client-11.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f13b1a6 Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-client-11.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-client-9.0-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-client-9.0-plugin-9.2.0.jar new file mode 100644 index 0000000..629c26a Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-client-9.0-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-client-9.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-client-9.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1ac605c Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-client-9.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-server-11.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-server-11.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7035f31 Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-server-11.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-server-9.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-server-9.x-plugin-9.2.0.jar new file mode 100644 index 0000000..bd98976 Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-server-9.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-jetty-thread-pool-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-jetty-thread-pool-plugin-9.2.0.jar new file mode 100644 index 0000000..2e17d2f Binary files /dev/null and b/skywalking/agent/plugins/apm-jetty-thread-pool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-kafka-commons-9.2.0.jar b/skywalking/agent/plugins/apm-kafka-commons-9.2.0.jar new file mode 100644 index 0000000..f414204 Binary files /dev/null and b/skywalking/agent/plugins/apm-kafka-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-kafka-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-kafka-plugin-9.2.0.jar new file mode 100644 index 0000000..63f5d4b Binary files /dev/null and b/skywalking/agent/plugins/apm-kafka-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-kylin-jdbc-2.6.x-3.x-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-kylin-jdbc-2.6.x-3.x-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..3ee08f6 Binary files /dev/null and b/skywalking/agent/plugins/apm-kylin-jdbc-2.6.x-3.x-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-lettuce-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-lettuce-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..91d662e Binary files /dev/null and b/skywalking/agent/plugins/apm-lettuce-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-light4j-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-light4j-plugin-9.2.0.jar new file mode 100644 index 0000000..2c895c8 Binary files /dev/null and b/skywalking/agent/plugins/apm-light4j-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mariadb-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mariadb-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ca7947c Binary files /dev/null and b/skywalking/agent/plugins/apm-mariadb-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mongodb-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mongodb-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7835b63 Binary files /dev/null and b/skywalking/agent/plugins/apm-mongodb-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mongodb-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mongodb-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1db9b12 Binary files /dev/null and b/skywalking/agent/plugins/apm-mongodb-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mongodb-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mongodb-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..13dfd3f Binary files /dev/null and b/skywalking/agent/plugins/apm-mongodb-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mssql-commons-9.2.0.jar b/skywalking/agent/plugins/apm-mssql-commons-9.2.0.jar new file mode 100644 index 0000000..5704d4d Binary files /dev/null and b/skywalking/agent/plugins/apm-mssql-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mssql-jdbc-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mssql-jdbc-plugin-9.2.0.jar new file mode 100644 index 0000000..1654c5b Binary files /dev/null and b/skywalking/agent/plugins/apm-mssql-jdbc-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mssql-jtds-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mssql-jtds-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..546b6d8 Binary files /dev/null and b/skywalking/agent/plugins/apm-mssql-jtds-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mysql-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mysql-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..3a2d1d8 Binary files /dev/null and b/skywalking/agent/plugins/apm-mysql-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mysql-6.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mysql-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f28c207 Binary files /dev/null and b/skywalking/agent/plugins/apm-mysql-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mysql-8.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-mysql-8.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1ae2b75 Binary files /dev/null and b/skywalking/agent/plugins/apm-mysql-8.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-mysql-commons-9.2.0.jar b/skywalking/agent/plugins/apm-mysql-commons-9.2.0.jar new file mode 100644 index 0000000..ea0168c Binary files /dev/null and b/skywalking/agent/plugins/apm-mysql-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-neo4j-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-neo4j-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..50fde5e Binary files /dev/null and b/skywalking/agent/plugins/apm-neo4j-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-netty-socketio-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-netty-socketio-plugin-9.2.0.jar new file mode 100644 index 0000000..0dd9620 Binary files /dev/null and b/skywalking/agent/plugins/apm-netty-socketio-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-nutz-http-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-nutz-http-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..cb954bf Binary files /dev/null and b/skywalking/agent/plugins/apm-nutz-http-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-nutz-mvc-annotation-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-nutz-mvc-annotation-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1f18114 Binary files /dev/null and b/skywalking/agent/plugins/apm-nutz-mvc-annotation-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-okhttp-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-okhttp-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..65d98e1 Binary files /dev/null and b/skywalking/agent/plugins/apm-okhttp-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-okhttp-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-okhttp-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..4d5bfea Binary files /dev/null and b/skywalking/agent/plugins/apm-okhttp-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-okhttp-common-9.2.0.jar b/skywalking/agent/plugins/apm-okhttp-common-9.2.0.jar new file mode 100644 index 0000000..f349835 Binary files /dev/null and b/skywalking/agent/plugins/apm-okhttp-common-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-play-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-play-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e65ec2d Binary files /dev/null and b/skywalking/agent/plugins/apm-play-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-postgresql-8.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-postgresql-8.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e55ad1a Binary files /dev/null and b/skywalking/agent/plugins/apm-postgresql-8.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-pulsar-2.2-2.7-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-pulsar-2.2-2.7-plugin-9.2.0.jar new file mode 100644 index 0000000..9dfc17e Binary files /dev/null and b/skywalking/agent/plugins/apm-pulsar-2.2-2.7-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-pulsar-2.8.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-pulsar-2.8.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f484045 Binary files /dev/null and b/skywalking/agent/plugins/apm-pulsar-2.8.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-pulsar-common-9.2.0.jar b/skywalking/agent/plugins/apm-pulsar-common-9.2.0.jar new file mode 100644 index 0000000..cf107e9 Binary files /dev/null and b/skywalking/agent/plugins/apm-pulsar-common-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-quasar-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-quasar-plugin-9.2.0.jar new file mode 100644 index 0000000..a73bfd7 Binary files /dev/null and b/skywalking/agent/plugins/apm-quasar-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-rabbitmq-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-rabbitmq-plugin-9.2.0.jar new file mode 100644 index 0000000..e653b25 Binary files /dev/null and b/skywalking/agent/plugins/apm-rabbitmq-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-redisson-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-redisson-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..b83bc04 Binary files /dev/null and b/skywalking/agent/plugins/apm-redisson-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-resttemplate-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-resttemplate-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..82bfd38 Binary files /dev/null and b/skywalking/agent/plugins/apm-resttemplate-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-resttemplate-4.3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-resttemplate-4.3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..4d75f6b Binary files /dev/null and b/skywalking/agent/plugins/apm-resttemplate-4.3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-rocketMQ-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-rocketMQ-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ddcb634 Binary files /dev/null and b/skywalking/agent/plugins/apm-rocketMQ-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-rocketmq-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-rocketmq-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..686eae2 Binary files /dev/null and b/skywalking/agent/plugins/apm-rocketmq-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-rocketmq-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-rocketmq-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..d44c31b Binary files /dev/null and b/skywalking/agent/plugins/apm-rocketmq-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-rocketmq-client-java-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-rocketmq-client-java-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..812b207 Binary files /dev/null and b/skywalking/agent/plugins/apm-rocketmq-client-java-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-servicecomb-java-chassis-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-servicecomb-java-chassis-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..5ed5f87 Binary files /dev/null and b/skywalking/agent/plugins/apm-servicecomb-java-chassis-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-sharding-sphere-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-sharding-sphere-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f4add1c Binary files /dev/null and b/skywalking/agent/plugins/apm-sharding-sphere-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-sharding-sphere-4.1.0-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-sharding-sphere-4.1.0-plugin-9.2.0.jar new file mode 100644 index 0000000..c14c589 Binary files /dev/null and b/skywalking/agent/plugins/apm-sharding-sphere-4.1.0-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-shardingsphere-4.0.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-shardingsphere-4.0.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1c6c474 Binary files /dev/null and b/skywalking/agent/plugins/apm-shardingsphere-4.0.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-shardingsphere-5.0.0-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-shardingsphere-5.0.0-plugin-9.2.0.jar new file mode 100644 index 0000000..2c58d01 Binary files /dev/null and b/skywalking/agent/plugins/apm-shardingsphere-5.0.0-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-solrj-7.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-solrj-7.x-plugin-9.2.0.jar new file mode 100644 index 0000000..a4749ea Binary files /dev/null and b/skywalking/agent/plugins/apm-solrj-7.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-async-annotation-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-async-annotation-plugin-9.2.0.jar new file mode 100644 index 0000000..b355dbb Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-async-annotation-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-cloud-feign-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-cloud-feign-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f451938 Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-cloud-feign-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-cloud-feign-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-cloud-feign-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..5455f74 Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-cloud-feign-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-concurrent-util-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-concurrent-util-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..fb47285 Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-concurrent-util-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-core-patch-9.2.0.jar b/skywalking/agent/plugins/apm-spring-core-patch-9.2.0.jar new file mode 100644 index 0000000..9cf127e Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-core-patch-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-kafka-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-kafka-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..be6b0d4 Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-kafka-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-kafka-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-kafka-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..ad1017a Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-kafka-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spring-scheduled-annotation-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spring-scheduled-annotation-plugin-9.2.0.jar new file mode 100644 index 0000000..dd739c4 Binary files /dev/null and b/skywalking/agent/plugins/apm-spring-scheduled-annotation-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-springmvc-annotation-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-springmvc-annotation-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..179c599 Binary files /dev/null and b/skywalking/agent/plugins/apm-springmvc-annotation-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-springmvc-annotation-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-springmvc-annotation-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7c087bf Binary files /dev/null and b/skywalking/agent/plugins/apm-springmvc-annotation-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-springmvc-annotation-5.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-springmvc-annotation-5.x-plugin-9.2.0.jar new file mode 100644 index 0000000..d51b954 Binary files /dev/null and b/skywalking/agent/plugins/apm-springmvc-annotation-5.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-springmvc-annotation-commons-9.2.0.jar b/skywalking/agent/plugins/apm-springmvc-annotation-commons-9.2.0.jar new file mode 100644 index 0000000..bbe0d93 Binary files /dev/null and b/skywalking/agent/plugins/apm-springmvc-annotation-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-spymemcached-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-spymemcached-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..22cc079 Binary files /dev/null and b/skywalking/agent/plugins/apm-spymemcached-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-struts2-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-struts2-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..db2c735 Binary files /dev/null and b/skywalking/agent/plugins/apm-struts2-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-tomcat-thread-pool-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-tomcat-thread-pool-plugin-9.2.0.jar new file mode 100644 index 0000000..f52135f Binary files /dev/null and b/skywalking/agent/plugins/apm-tomcat-thread-pool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-undertow-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-undertow-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..fea012d Binary files /dev/null and b/skywalking/agent/plugins/apm-undertow-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-undertow-worker-thread-pool-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-undertow-worker-thread-pool-plugin-9.2.0.jar new file mode 100644 index 0000000..a89ab64 Binary files /dev/null and b/skywalking/agent/plugins/apm-undertow-worker-thread-pool-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-vertx-core-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-vertx-core-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..65982bb Binary files /dev/null and b/skywalking/agent/plugins/apm-vertx-core-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-vertx-core-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-vertx-core-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f5dc25e Binary files /dev/null and b/skywalking/agent/plugins/apm-vertx-core-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-xmemcached-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-xmemcached-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..227fdc4 Binary files /dev/null and b/skywalking/agent/plugins/apm-xmemcached-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/apm-xxl-job-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/apm-xxl-job-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7a6ecf6 Binary files /dev/null and b/skywalking/agent/plugins/apm-xxl-job-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/baidu-brpc-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/baidu-brpc-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..6715af2 Binary files /dev/null and b/skywalking/agent/plugins/baidu-brpc-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/baidu-brpc-plugin-9.2.0.jar b/skywalking/agent/plugins/baidu-brpc-plugin-9.2.0.jar new file mode 100644 index 0000000..ab6bed0 Binary files /dev/null and b/skywalking/agent/plugins/baidu-brpc-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/dbcp-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/dbcp-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..8379b18 Binary files /dev/null and b/skywalking/agent/plugins/dbcp-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/druid-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/druid-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..4a103d1 Binary files /dev/null and b/skywalking/agent/plugins/druid-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/dubbo-2.7.x-conflict-patch-9.2.0.jar b/skywalking/agent/plugins/dubbo-2.7.x-conflict-patch-9.2.0.jar new file mode 100644 index 0000000..db309bd Binary files /dev/null and b/skywalking/agent/plugins/dubbo-2.7.x-conflict-patch-9.2.0.jar differ diff --git a/skywalking/agent/plugins/dubbo-3.x-conflict-patch-9.2.0.jar b/skywalking/agent/plugins/dubbo-3.x-conflict-patch-9.2.0.jar new file mode 100644 index 0000000..51b85a9 Binary files /dev/null and b/skywalking/agent/plugins/dubbo-3.x-conflict-patch-9.2.0.jar differ diff --git a/skywalking/agent/plugins/dubbo-conflict-patch-9.2.0.jar b/skywalking/agent/plugins/dubbo-conflict-patch-9.2.0.jar new file mode 100644 index 0000000..22d56bd Binary files /dev/null and b/skywalking/agent/plugins/dubbo-conflict-patch-9.2.0.jar differ diff --git a/skywalking/agent/plugins/elasticsearch-common-9.2.0.jar b/skywalking/agent/plugins/elasticsearch-common-9.2.0.jar new file mode 100644 index 0000000..13abbcb Binary files /dev/null and b/skywalking/agent/plugins/elasticsearch-common-9.2.0.jar differ diff --git a/skywalking/agent/plugins/graphql-12.x-15.x-plugin-9.2.0.jar b/skywalking/agent/plugins/graphql-12.x-15.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e1154a1 Binary files /dev/null and b/skywalking/agent/plugins/graphql-12.x-15.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/graphql-16plus-plugin-9.2.0.jar b/skywalking/agent/plugins/graphql-16plus-plugin-9.2.0.jar new file mode 100644 index 0000000..c064f8b Binary files /dev/null and b/skywalking/agent/plugins/graphql-16plus-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/graphql-8.x-plugin-9.2.0.jar b/skywalking/agent/plugins/graphql-8.x-plugin-9.2.0.jar new file mode 100644 index 0000000..63bc092 Binary files /dev/null and b/skywalking/agent/plugins/graphql-8.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/graphql-9.x-plugin-9.2.0.jar b/skywalking/agent/plugins/graphql-9.x-plugin-9.2.0.jar new file mode 100644 index 0000000..b2f8e54 Binary files /dev/null and b/skywalking/agent/plugins/graphql-9.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/jedis-2.x-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/jedis-2.x-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..98f52f3 Binary files /dev/null and b/skywalking/agent/plugins/jedis-2.x-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/jedis-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/jedis-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e971fd1 Binary files /dev/null and b/skywalking/agent/plugins/jedis-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/jsonrpc4j-1.x-plugin-9.2.0.jar b/skywalking/agent/plugins/jsonrpc4j-1.x-plugin-9.2.0.jar new file mode 100644 index 0000000..f7c8233 Binary files /dev/null and b/skywalking/agent/plugins/jsonrpc4j-1.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/micronaut-http-client-plugin-9.2.0.jar b/skywalking/agent/plugins/micronaut-http-client-plugin-9.2.0.jar new file mode 100644 index 0000000..e370ee3 Binary files /dev/null and b/skywalking/agent/plugins/micronaut-http-client-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/micronaut-http-server-plugin-9.2.0.jar b/skywalking/agent/plugins/micronaut-http-server-plugin-9.2.0.jar new file mode 100644 index 0000000..d6c0a38 Binary files /dev/null and b/skywalking/agent/plugins/micronaut-http-server-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/motan-plugin-9.2.0.jar b/skywalking/agent/plugins/motan-plugin-9.2.0.jar new file mode 100644 index 0000000..42383e2 Binary files /dev/null and b/skywalking/agent/plugins/motan-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/nats-2.14.x-2.15.x-plugin-9.2.0.jar b/skywalking/agent/plugins/nats-2.14.x-2.15.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7b7bbbd Binary files /dev/null and b/skywalking/agent/plugins/nats-2.14.x-2.15.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/okhttp-2.x-plugin-9.2.0.jar b/skywalking/agent/plugins/okhttp-2.x-plugin-9.2.0.jar new file mode 100644 index 0000000..e242970 Binary files /dev/null and b/skywalking/agent/plugins/okhttp-2.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/resteasy-server-3.x-plugin-9.2.0.jar b/skywalking/agent/plugins/resteasy-server-3.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1e05a2c Binary files /dev/null and b/skywalking/agent/plugins/resteasy-server-3.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/resteasy-server-4.x-plugin-9.2.0.jar b/skywalking/agent/plugins/resteasy-server-4.x-plugin-9.2.0.jar new file mode 100644 index 0000000..77605c0 Binary files /dev/null and b/skywalking/agent/plugins/resteasy-server-4.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/resteasy-server-6.x-plugin-9.2.0.jar b/skywalking/agent/plugins/resteasy-server-6.x-plugin-9.2.0.jar new file mode 100644 index 0000000..338f16f Binary files /dev/null and b/skywalking/agent/plugins/resteasy-server-6.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/resttemplate-commons-9.2.0.jar b/skywalking/agent/plugins/resttemplate-commons-9.2.0.jar new file mode 100644 index 0000000..bb45a74 Binary files /dev/null and b/skywalking/agent/plugins/resttemplate-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/sofa-rpc-plugin-9.2.0.jar b/skywalking/agent/plugins/sofa-rpc-plugin-9.2.0.jar new file mode 100644 index 0000000..c2f48cb Binary files /dev/null and b/skywalking/agent/plugins/sofa-rpc-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/spring-commons-9.2.0.jar b/skywalking/agent/plugins/spring-commons-9.2.0.jar new file mode 100644 index 0000000..812eac7 Binary files /dev/null and b/skywalking/agent/plugins/spring-commons-9.2.0.jar differ diff --git a/skywalking/agent/plugins/spring-webflux-5.x-webclient-plugin-9.2.0.jar b/skywalking/agent/plugins/spring-webflux-5.x-webclient-plugin-9.2.0.jar new file mode 100644 index 0000000..d6e578a Binary files /dev/null and b/skywalking/agent/plugins/spring-webflux-5.x-webclient-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/spring-webflux-6.x-webclient-plugin-9.2.0.jar b/skywalking/agent/plugins/spring-webflux-6.x-webclient-plugin-9.2.0.jar new file mode 100644 index 0000000..44a5374 Binary files /dev/null and b/skywalking/agent/plugins/spring-webflux-6.x-webclient-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/thrift-plugin-9.2.0.jar b/skywalking/agent/plugins/thrift-plugin-9.2.0.jar new file mode 100644 index 0000000..e061098 Binary files /dev/null and b/skywalking/agent/plugins/thrift-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/tomcat-10x-plugin-9.2.0.jar b/skywalking/agent/plugins/tomcat-10x-plugin-9.2.0.jar new file mode 100644 index 0000000..f0bf1d9 Binary files /dev/null and b/skywalking/agent/plugins/tomcat-10x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/tomcat-7.x-8.x-plugin-9.2.0.jar b/skywalking/agent/plugins/tomcat-7.x-8.x-plugin-9.2.0.jar new file mode 100644 index 0000000..1b1f480 Binary files /dev/null and b/skywalking/agent/plugins/tomcat-7.x-8.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/plugins/websphere-liberty-23.x-plugin-9.2.0.jar b/skywalking/agent/plugins/websphere-liberty-23.x-plugin-9.2.0.jar new file mode 100644 index 0000000..7970cfc Binary files /dev/null and b/skywalking/agent/plugins/websphere-liberty-23.x-plugin-9.2.0.jar differ diff --git a/skywalking/agent/skywalking-agent.jar b/skywalking/agent/skywalking-agent.jar new file mode 100644 index 0000000..41f5484 Binary files /dev/null and b/skywalking/agent/skywalking-agent.jar differ diff --git a/skywalking/ins b/skywalking/ins new file mode 100644 index 0000000..b255bd9 --- /dev/null +++ b/skywalking/ins @@ -0,0 +1,10 @@ +-Xms256m +-Xmx256m +-XX:MetaspaceSize=128m +-XX:MaxMetaspaceSize=128m +-XX:MaxDirectMemorySize=128m +-javaagent:.\skywalking\agent\skywalking-agent.jar +-Dskywalking.agent.service_name=muyu::cloud-system +-Dskywalking.agent.namespace=DEV +-Dskywalking.logging.file_name=Application_name.log +-Dskywalking.collector.backend_service=ServerHost diff --git a/skywalking/install/elasticsearch/elasticsearch.sh b/skywalking/install/elasticsearch/elasticsearch.sh new file mode 100644 index 0000000..24e8cac --- /dev/null +++ b/skywalking/install/elasticsearch/elasticsearch.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +docker run -d \ + --name elasticsearch \ + -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \ + --network skywalking \ + -e "discovery.type=single-node" \ + -p 9200:9200 \ + -p 9300:9300 \ + -v ./data:/usr/share/elasticsearch/data \ + docker.elastic.co/elasticsearch/elasticsearch:7.17.21 diff --git a/skywalking/install/skywalking-net.sh b/skywalking/install/skywalking-net.sh new file mode 100644 index 0000000..de164e2 --- /dev/null +++ b/skywalking/install/skywalking-net.sh @@ -0,0 +1,2 @@ +docker network create skywalking + diff --git a/skywalking/install/skywalking/skywalking-oap.sh b/skywalking/install/skywalking/skywalking-oap.sh new file mode 100644 index 0000000..767cab9 --- /dev/null +++ b/skywalking/install/skywalking/skywalking-oap.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +docker run -d \ + --name skywalking-oap \ + --restart always \ + --network skywalking \ + -p 12800:12800 \ + -p 11800:11800 \ + -e SW_STORAGE=elasticsearch \ + -e SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200 \ + apache/skywalking-oap-server:9.7.0-java17 + diff --git a/skywalking/install/skywalking/skywalking-ui.sh b/skywalking/install/skywalking/skywalking-ui.sh new file mode 100644 index 0000000..bf37346 --- /dev/null +++ b/skywalking/install/skywalking/skywalking-ui.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +docker run -d \ + --name skywalking-ui \ + --restart always \ + --network skywalking \ + -p 8080:8080 \ + -e SW_OAP_ADDRESS=http://skywalking-oap:12800 \ + apache/skywalking-ui:9.7.0-java17 diff --git a/skywalking/show b/skywalking/show new file mode 100644 index 0000000..c689dbf --- /dev/null +++ b/skywalking/show @@ -0,0 +1,6 @@ +skywalking-oap-server-9.7.0-java17: https://www.kdocs.cn/l/cmEiIEKglupf +skywalking-ui-9.7.0-java17: https://www.kdocs.cn/l/ctyJvGn0z7bZ + +文件下载下来之后使用{docker load < [文件]}这个命令进行加载到docker当中 + +链路追踪-SkyWalking安装参考地址: https://kdocs.cn/l/ceValb05jXOA \ No newline at end of file diff --git a/xxl-job/job-admin/config/application.properties b/xxl-job/job-admin/config/application.properties new file mode 100644 index 0000000..b9e7994 --- /dev/null +++ b/xxl-job/job-admin/config/application.properties @@ -0,0 +1,33 @@ +### web 如果要改变端口和这个访问前缀在此进行更改 +### 原端口是8080 +server.port=80 +### 原请求路径是/xxl-job-admin +server.servlet.context-path=/ +### 调度中心JDBC链接:链接地址请保持和 2.1章节 所创建的调度数据库的地址一致 +spring.datasource.url=jdbc:mysql://xxl-mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai +spring.datasource.username=root +spring.datasource.password=root +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +### 报警邮箱 +spring.mail.host= smtp.163.com +spring.mail.port=465 +spring.mail.username=dongzl828@163.com +spring.mail.password=EGHEDRZUIZWUIGZY +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory + +### 调度中心通讯TOKEN [选填]:非空时启用; +xxl.job.accessToken= + +### 调度中心国际化配置 [必填]: 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文; +xxl.job.i18n=zh_CN + +## 调度线程池最大线程配置【必填】 +xxl.job.triggerpool.fast.max=200 +xxl.job.triggerpool.slow.max=100 + +### 调度中心日志表数据保存天数 [必填]:过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能; +xxl.job.logretentiondays=30 diff --git a/xxl-job/job-admin/depXXLAdmin.sh b/xxl-job/job-admin/depXXLAdmin.sh new file mode 100644 index 0000000..1dc1339 --- /dev/null +++ b/xxl-job/job-admin/depXXLAdmin.sh @@ -0,0 +1,7 @@ +#!/bin/bash +docker run --name xxl-job-admin -d \ + --network xxl-network \ + -v ./config/application.properties:/config/application.properties \ + -e PARAMS="--spring.config.additional-location=/config/application.properties" \ + -v ./log:/data/applogs \ + xuxueli/xxl-job-admin:2.4.1 diff --git a/xxl-job/mysql/config/my.cnf b/xxl-job/mysql/config/my.cnf new file mode 100644 index 0000000..976477b --- /dev/null +++ b/xxl-job/mysql/config/my.cnf @@ -0,0 +1,66 @@ +[client] +# 端口号 +port=3306 +default-character-set=utf8mb4 + +[mysql] +no-beep +default-character-set=utf8mb4 + +[mysqld] +# 端口号 +port=3306 +# 数据目录 +datadir=/var/lib/mysql +# 新模式或表时将使用的默认字符集 +character-set-server=utf8mb4 +# 默认存储引擎 +default-storage-engine=INNODB +# 将 SQL 模式设置为严格 - 暂时关闭 +# sql-mode= +# 最大连接数 +max_connections=1024 +# 表缓存 +table_open_cache=2000 +# 表内存 +tmp_table_size=16M +# 线程缓存 +thread_cache_size=10 +# 设置大小写不敏感 +lower_case_table_names=1 + +# myisam设置 +myisam_max_sort_file_size=100G +myisam_sort_buffer_size=8M +key_buffer_size=8M +read_buffer_size=0 +read_rnd_buffer_size=0 + +# innodb设置 +innodb_flush_log_at_trx_commit=1 +innodb_log_buffer_size=1M +innodb_buffer_pool_size=8M +innodb_log_file_size=48M +innodb_thread_concurrency=33 +innodb_autoextend_increment=64 +innodb_buffer_pool_instances=8 +innodb_concurrency_tickets=5000 +innodb_old_blocks_time=1000 +innodb_open_files=300 +innodb_stats_on_metadata=0 +innodb_file_per_table=1 +innodb_checksum_algorithm=0 +# 其他设置 +back_log=80 +flush_time=0 +join_buffer_size=256K +max_allowed_packet=4M +max_connect_errors=100 +open_files_limit=4161 +sort_buffer_size=256K +table_definition_cache=1400 +binlog_row_event_max_size=8K +sync_master_info=10000 +sync_relay_log=10000 +sync_relay_log_info=10000 + diff --git a/xxl-job/mysql/depXXLMysql.sh b/xxl-job/mysql/depXXLMysql.sh new file mode 100644 index 0000000..68db1c6 --- /dev/null +++ b/xxl-job/mysql/depXXLMysql.sh @@ -0,0 +1,9 @@ +#!/bin/bash +docker run -d --name xxl-mysql \ + --privileged=true \ + --network xxl-network \ + -v ./log:/var/log/mysql \ + -v ./data:/var/lib/mysql \ + -v ./config/my.cnf:/etc/my.cnf \ + -e MYSQL_ROOT_PASSWORD=root \ + mysql:8.4.1 diff --git a/xxl-job/show b/xxl-job/show new file mode 100644 index 0000000..1de3607 --- /dev/null +++ b/xxl-job/show @@ -0,0 +1,13 @@ +1、 下载这两个文件 + mysql-8.4.1: https://kdocs.cn/l/chQxllwDbv1T + xxl-job-admin-2.4.1: https://kdocs.cn/l/cvwthMg4JpPF +2、 加载镜像 + 文件下载下来之后使用{docker load < [文件]}这个命令进行加载到docker当中 +3、执行网络脚本 + xxl-net.sh脚本执行,执行脚本可以添加IP范围 +4、执行镜像脚本 + 如果想要IP固定可以添加`--ip`进行ip的设定 + · 先执行depXXLMysql.sh脚本 + · 再执行depXXLAdmin.sh脚本 +5、hosts配置 + 在host当中配置xxl-job-admin的IP和端口 \ No newline at end of file diff --git a/xxl-job/xxl-net.sh b/xxl-job/xxl-net.sh new file mode 100644 index 0000000..163a4a6 --- /dev/null +++ b/xxl-job/xxl-net.sh @@ -0,0 +1 @@ +docker network create xxl-network