diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..25b312ef --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# http://editorconfig.org +root = true + +# 空格替代Tab缩进在各种编辑工具下效果一致 +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{json,yml,yaml}] +indent_size = 2 + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fa3ee974 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml + +.flattened-pom.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..22593d53 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2023 ruoyi-ai + +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.en.md b/README.en.md new file mode 100644 index 00000000..acf6d4aa --- /dev/null +++ b/README.en.md @@ -0,0 +1,36 @@ +# ruoyi-ai-pro + +#### Description +ruoyi-ai-pro + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 11808056..c64224ba 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,81 @@ -# ruoyi-ai -基于ruoyi-plus实现AI聊天和绘画功能-后端 本项目完全开源免费! 后台管理界面使用elementUI服务端使用Java17+SpringBoot3.X +## 平台简介 +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/ageerle/ruoyi-ai/blob/master/LICENSE) +[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=ruoyi-chatgpt) +
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.0.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.0-blue.svg)]() +[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() + +> 基于ruoyi-plus实现AI聊天和绘画功能-后端 + +> 本项目完全开源免费! +后台管理界面使用elementUI服务端使用Java17+SpringBoot3.X + +实现功能 +1. 接入ChatGPT-4-1106-preview,gpt-4-vision-preview,dall-e-3模型 +2. 支持ChatGPT-4-All alltools版本,集成识图、画图、联网和code interpreter +3. 支持GPTS 可以使用openai的所有的GPTs +4. 接入AzureOpenAI +5. 接入文生图模型Midjourney( 史上最强AI画图) +6. 接入微信支付 +7. 支持微信小程序 + +>测试功能: 私有知识库 + +>项目地址 + + + +## 小程序演示 +
+ drawing + drawing +
+ +## H5演示 +
+ drawing + drawing +
+ +## PC端演示 +
+ drawing + drawing +
+ +## MJ绘图 +
+ drawing + drawing +
+ +## 微信智能助手 +
+ drawing +
+ +## 私有知识库管理(开发中) +
+ drawing + drawing +
+ +## 进群学习 +
+ drawing + drawing +
+ + +## 参考项目 +
    +
  1. https://github.com/Grt1228/chatgpt-java
  2. +
  3. https://gitee.com/dromara/RuoYi-Vue-Plus
  4. +
diff --git a/image/01.png b/image/01.png new file mode 100644 index 00000000..4f623f65 Binary files /dev/null and b/image/01.png differ diff --git a/image/02.png b/image/02.png new file mode 100644 index 00000000..ac033d22 Binary files /dev/null and b/image/02.png differ diff --git a/image/03.png b/image/03.png new file mode 100644 index 00000000..e311e8d0 Binary files /dev/null and b/image/03.png differ diff --git a/image/04.png b/image/04.png new file mode 100644 index 00000000..da499563 Binary files /dev/null and b/image/04.png differ diff --git a/image/05.png b/image/05.png new file mode 100644 index 00000000..a3d8a562 Binary files /dev/null and b/image/05.png differ diff --git a/image/06.png b/image/06.png new file mode 100644 index 00000000..ec7847ed Binary files /dev/null and b/image/06.png differ diff --git a/image/07.png b/image/07.png new file mode 100644 index 00000000..44f5d512 Binary files /dev/null and b/image/07.png differ diff --git a/image/08.png b/image/08.png new file mode 100644 index 00000000..a2479edc Binary files /dev/null and b/image/08.png differ diff --git a/image/09.png b/image/09.png new file mode 100644 index 00000000..eb4aa788 Binary files /dev/null and b/image/09.png differ diff --git a/image/10.png b/image/10.png new file mode 100644 index 00000000..558cf0d7 Binary files /dev/null and b/image/10.png differ diff --git a/image/11.png b/image/11.png new file mode 100644 index 00000000..1ab97ab2 Binary files /dev/null and b/image/11.png differ diff --git a/image/12.png b/image/12.png new file mode 100644 index 00000000..7c4480ae Binary files /dev/null and b/image/12.png differ diff --git a/image/私有知识库业务架构图.drawio.png b/image/私有知识库业务架构图.drawio.png new file mode 100644 index 00000000..ca26dd77 Binary files /dev/null and b/image/私有知识库业务架构图.drawio.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..f85f80fc --- /dev/null +++ b/pom.xml @@ -0,0 +1,471 @@ + + + 4.0.0 + + com.xmzs + ruoyi-ai + ${revision} + + ruoyi-ai + https://gitee.com/ageerle/ruoyi-ai + AI助手后台管理系统 + + + 1.0.0 + 3.0.6 + UTF-8 + UTF-8 + 17 + 3.0.1 + 2.1.0 + 0.15.0 + 5.2.3 + 3.2.1 + 2.3 + 1.34.0 + 3.5.3.1 + 3.9.1 + 5.8.18 + 4.10.0 + 3.0.3 + 3.20.1 + 2.2.4 + 3.6.1 + 2.14.2 + 2.4.0 + 1.2.1 + 0.2.0 + 1.18.26 + 1.72 + + 2.7.0 + + + 1.33 + + + 1.12.400 + + 2.0.23 + 3.1.687 + + + 3.2.2 + 3.2.2 + 3.11.0 + 3.0.0 + 1.3.0 + 4.5.0 + + + + + local + + + local + debug + + + + dev + + + dev + debug + + + + true + + + + prod + + prod + warn + + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + cn.hutool + hutool-bom + ${hutool.version} + pom + import + + + + + com.xmzs + ruoyi-common-bom + ${revision} + pom + import + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + ${springdoc.version} + + + + com.github.therapi + therapi-runtime-javadoc + ${therapi-javadoc.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.apache.poi + poi + ${poi.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + com.alibaba + easyexcel + ${easyexcel.version} + + + org.apache.poi + poi-ooxml-schemas + + + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + cn.dev33 + sa-token-spring-boot3-starter + ${satoken.version} + + + + cn.dev33 + sa-token-jwt + ${satoken.version} + + + cn.hutool + hutool-all + + + + + cn.dev33 + sa-token-core + ${satoken.version} + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${dynamic-ds.version} + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${spring-boot.mybatis} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-annotation + ${mybatis-plus.version} + + + + + p6spy + p6spy + ${p6spy.version} + + + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk-s3.version} + + + + com.aliyun + dysmsapi20170525 + ${aliyun.sms.version} + + + + com.tencentcloudapi + tencentcloud-sdk-java-sms + ${tencent.sms.version} + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + + + + + com.xuxueli + xxl-job-core + ${xxl-job.version} + + + + com.alibaba + transmittable-thread-local + ${alibaba-ttl.version} + + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + + org.bouncycastle + bcprov-jdk15to18 + ${bouncycastle.version} + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + ${mapstruct-plus.version} + + + + + org.lionsoul + ip2region + ${ip2region.version} + + + + com.xmzs + ruoyi-system + ${revision} + + + + com.xmzs + ruoyi-job + ${revision} + + + + com.xmzs + ruoyi-generator + ${revision} + + + + com.xmzs + ruoyi-demo + ${revision} + + + + + + + ruoyi-admin + ruoyi-common + ruoyi-modules + + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.verison} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + com.github.therapi + therapi-runtime-javadoc-scribe + ${therapi-javadoc.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + io.github.linpeilie + mapstruct-plus-processor + ${mapstruct-plus.version} + + + org.projectlombok + lombok-mapstruct-binding + ${mapstruct-plus.lombok.version} + + + + -parameters + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + ${profiles.active} + + exclude + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + + + src/main/resources + + false + + + src/main/resources + + + application* + bootstrap* + banner* + + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + false + + + + + + + diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile new file mode 100644 index 00000000..0996e40c --- /dev/null +++ b/ruoyi-admin/Dockerfile @@ -0,0 +1,23 @@ +FROM findepi/graalvm:java17-native + +MAINTAINER Lion Li + +RUN mkdir -p /ruoyi/server/logs \ + /ruoyi/server/temp \ + /ruoyi/skywalking/agent + +WORKDIR /ruoyi/server + +ENV SERVER_PORT=8080 + +EXPOSE ${SERVER_PORT} + +ADD ./target/ruoyi-admin.jar ./app.jar + +ENTRYPOINT ["java", \ + "-Djava.security.egd=file:/dev/./urandom", \ + "-Dserver.port=${SERVER_PORT}", \ + # 应用名称 如果想区分集群节点监控 改成不同的名称即可 +# "-Dskywalking.agent.service_name=ruoyi-server", \ +# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \ + "-jar", "app.jar"] diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 00000000..477d3f17 --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,138 @@ + + + + ruoyi-ai + com.xmzs + ${revision} + ../pom.xml + + 4.0.0 + jar + ruoyi-admin + + + web服务入口 + + + + + + + com.mysql + mysql-connector-j + + + + com.oracle.database.jdbc + ojdbc8 + + + + org.postgresql + postgresql + + + + com.microsoft.sqlserver + mssql-jdbc + + + + com.xmzs + ruoyi-common-doc + + + + com.xmzs + ruoyi-system + + + + com.xmzs + ruoyi-common-chat + + + + com.xmzs + ruoyi-job + + + + + com.xmzs + ruoyi-generator + + + + + com.xmzs + ruoyi-demo + + + + de.codecentric + spring-boot-admin-starter-client + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + + + + + + + + + net.coobird + thumbnailator + 0.4.11 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + false + ${project.artifactId} + + + + + + diff --git a/ruoyi-admin/src/main/java/com/xmzs/PandaApplication.java b/ruoyi-admin/src/main/java/com/xmzs/PandaApplication.java new file mode 100644 index 00000000..d043c88b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/PandaApplication.java @@ -0,0 +1,23 @@ +package com.xmzs; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +/** + * 启动程序 + * + * @author Lion Li + */ + +@SpringBootApplication +public class PandaApplication { + + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(PandaApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(♥◠‿◠)ノ゙ panda智能助手启动成功 ლ(´ڡ`ლ)゙"); + } +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/PandaServletInitializer.java b/ruoyi-admin/src/main/java/com/xmzs/PandaServletInitializer.java new file mode 100644 index 00000000..fef0656d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/PandaServletInitializer.java @@ -0,0 +1,18 @@ +package com.xmzs; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author Lion Li + */ +public class PandaServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(PandaApplication.class); + } + +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/controller/AuthController.java b/ruoyi-admin/src/main/java/com/xmzs/controller/AuthController.java new file mode 100644 index 00000000..848f7b46 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/controller/AuthController.java @@ -0,0 +1,159 @@ +package com.xmzs.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.collection.CollUtil; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.domain.model.*; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.system.domain.bo.SysTenantBo; +import com.xmzs.system.domain.vo.LoginTenantVo; +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.system.domain.vo.TenantListVo; +import com.xmzs.system.service.ISysTenantService; + + +import com.xmzs.system.service.SysLoginService; +import com.xmzs.system.service.SysRegisterService; +import com.xmzs.web.domain.vo.LoginVo; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.net.URL; +import java.util.List; + +/** + * 认证 + * + * @author Lion Li + */ +@SaIgnore +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final SysLoginService loginService; + private final SysRegisterService registerService; + private final ISysTenantService tenantService; + + + + /** + * 登录方法 + * + * @param body 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public R login(@Validated @RequestBody LoginBody body) { + body.setTenantId(Constants.TENANT_ID); + LoginVo loginVo = new LoginVo(); + // 生成令牌 + String token = loginService.login( + body.getTenantId(), + body.getUsername(), body.getPassword(), + body.getCode(), body.getUuid()); + loginVo.setToken(token); + loginVo.setUserInfo(LoginHelper.getLoginUser()); + return R.ok(loginVo); + } + + /** + * 短信登录 + * + * @param body 登录信息 + * @return 结果 + */ + @PostMapping("/smsLogin") + public R smsLogin(@Validated @RequestBody SmsLoginBody body) { + LoginVo loginVo = new LoginVo(); + // 生成令牌 + String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode()); + loginVo.setToken(token); + return R.ok(loginVo); + } + + /** + * 邮件登录 + * + * @param body 登录信息 + * @return 结果 + */ + @PostMapping("/emailLogin") + public R emailLogin(@Validated @RequestBody EmailLoginBody body) { + LoginVo loginVo = new LoginVo(); + // 生成令牌 + String token = loginService.emailLogin(body.getTenantId(), body.getEmail(), body.getEmailCode()); + loginVo.setToken(token); + return R.ok(loginVo); + } + + /** + * 游客登录 + * + * @param loginBody + * @return 结果 + */ + @PostMapping("/visitorLogin") + public R xcxLogin(@RequestBody VisitorLoginBody loginBody) { + return R.ok(loginService.visitorLogin(loginBody)); + } + + /** + * 退出登录 + */ + @PostMapping("/logout") + public R logout() { + loginService.logout(); + return R.ok("退出成功"); + } + + /** + * 用户注册 + */ + @PostMapping("/register") + public R register(@Validated @RequestBody RegisterBody user) { + registerService.register(user); + return R.ok(); + } + + /** + * 重置密码 + */ + @PostMapping("/reset/password") + @SaIgnore + public R resetPassWord(@Validated @RequestBody RegisterBody user) { + registerService.resetPassWord(user); + return R.ok(); + } + + /** + * 登录页面租户下拉框 + * + * @return 租户列表 + */ + @GetMapping("/tenant/list") + public R tenantList(HttpServletRequest request) throws Exception { + List tenantList = tenantService.queryList(new SysTenantBo()); + List voList = MapstructUtils.convert(tenantList, TenantListVo.class); + // 获取域名 + String host = new URL(request.getRequestURL().toString()).getHost(); + // 根据域名进行筛选 + List list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host)); + // 返回对象 + LoginTenantVo vo = new LoginTenantVo(); + vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList); + vo.setTenantEnabled(TenantHelper.isEnable()); + return R.ok(vo); + } + +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/controller/CaptchaController.java b/ruoyi-admin/src/main/java/com/xmzs/controller/CaptchaController.java new file mode 100644 index 00000000..f33eb0d6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/controller/CaptchaController.java @@ -0,0 +1,139 @@ +package com.xmzs.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import com.xmzs.common.mail.config.properties.MailProperties; +import com.xmzs.common.mail.utils.MailUtils; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.sms.config.properties.SmsProperties; +import com.xmzs.common.sms.core.SmsTemplate; +import com.xmzs.common.sms.entity.SmsResult; +import com.xmzs.common.web.config.properties.CaptchaProperties; +import com.xmzs.common.web.enums.CaptchaType; +import com.xmzs.web.domain.request.EmailRequest; +import com.xmzs.web.domain.vo.CaptchaVo; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +/** + * 验证码操作处理 + * + * @author Lion Li + */ +@SaIgnore +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +public class CaptchaController { + + private final CaptchaProperties captchaProperties; + private final SmsProperties smsProperties; + private final MailProperties mailProperties; + + /** + * 短信验证码 + * + * @param phonenumber 用户手机号 + */ + @GetMapping("/resource/sms/code") + public R smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + // 验证码模板id 自行处理 (查数据库或写死均可) + String templateId = ""; + Map map = new HashMap<>(1); + map.put("code", code); + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + SmsResult result = smsTemplate.send(phonenumber, templateId, map); + if (!result.isSuccess()) { + log.error("验证码短信发送异常 => {}", result); + return R.fail(result.getMessage()); + } + return R.ok(); + } + + /** + * 邮箱验证码 + * + * @param emailRequest 用户邮箱 + */ + @PostMapping("/resource/email/code") + public R emailCode(@RequestBody @Valid EmailRequest emailRequest) { + if (!mailProperties.getEnabled()) { + return R.fail("当前系统没有开启邮箱功能!"); + } + String key = GlobalConstants.CAPTCHA_CODE_KEY + emailRequest.getUsername(); + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + try { + MailUtils.sendText(emailRequest.getUsername(), "【GPT助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); + } catch (Exception e) { + log.error("验证码短信发送异常 => {}", e.getMessage()); + return R.fail(e.getMessage()); + } + return R.ok(); + } + + /** + * 生成验证码 + */ + @GetMapping("/code") + public R getCode() { + CaptchaVo captchaVo = new CaptchaVo(); + boolean captchaEnabled = captchaProperties.getEnable(); + if (!captchaEnabled) { + captchaVo.setCaptchaEnabled(false); + return R.ok(captchaVo); + } + // 保存验证码信息 + String uuid = IdUtil.simpleUUID(); + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; + // 生成验证码 + CaptchaType captchaType = captchaProperties.getType(); + boolean isMath = CaptchaType.MATH == captchaType; + Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); + CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); + AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); + captcha.setGenerator(codeGenerator); + captcha.createCode(); + String code = captcha.getCode(); + if (isMath) { + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); + code = exp.getValue(String.class); + } + RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + captchaVo.setUuid(uuid); + captchaVo.setImg(captcha.getImageBase64()); + return R.ok(captchaVo); + } + +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/controller/ChatController.java b/ruoyi-admin/src/main/java/com/xmzs/controller/ChatController.java new file mode 100644 index 00000000..6f1ebb2e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/controller/ChatController.java @@ -0,0 +1,91 @@ +package com.xmzs.controller; + + +import com.xmzs.common.chat.domain.request.ChatRequest; +import com.xmzs.common.chat.domain.request.Dall3Request; +import com.xmzs.common.chat.entity.images.Item; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.domain.vo.ChatMessageVo; +import com.xmzs.system.service.IChatMessageService; +import com.xmzs.system.service.SseService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @date 2023-03-01 + */ +@Controller +@Slf4j +@RequiredArgsConstructor +public class ChatController { + + private final SseService sseService; + + private final IChatMessageService chatMessageService; + + /** + * 聊天接口 + */ + @PostMapping("/chat") + @ResponseBody + public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest) { + if("gpt-4-all".equals(chatRequest.getModel()) + || chatRequest.getModel().startsWith("gpt-4-gizmo") + || chatRequest.getModel().startsWith("net-") + ){ + return sseService.transitChat(chatRequest); + } + if("azure-gpt-3.5".equals(chatRequest.getModel())){ + return sseService.azureChat(chatRequest); + } + return sseService.sseChat(chatRequest); + } + + @PostMapping("/dall3") + @ResponseBody + public R> dall3(@RequestBody @Valid Dall3Request request) { + return R.ok(sseService.dall3(request)); + } + + @PostMapping("/mjTask") + @ResponseBody + public R mjTask() { + sseService.mjTask(); + return R.ok(); + } + + /** + * 聊天记录 + */ + @PostMapping("/chatList") + @ResponseBody + public R> list(@RequestBody @Valid ChatMessageBo chatRequest,@RequestBody PageQuery pageQuery) { + // 默认查询当前登录用户消息记录 + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new BaseException("用户未登录!"); + } + chatRequest.setUserId(loginUser.getUserId()); + TableDataInfo chatMessageVoTableDataInfo = chatMessageService.queryPageList(chatRequest, pageQuery); + return R.ok(chatMessageVoTableDataInfo); + } + +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/controller/IndexController.java b/ruoyi-admin/src/main/java/com/xmzs/controller/IndexController.java new file mode 100644 index 00000000..5a70eaf5 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/controller/IndexController.java @@ -0,0 +1,26 @@ +package com.xmzs.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * 首页 + * + * @author Lion Li + */ +@SaIgnore +@RequiredArgsConstructor +@Controller +public class IndexController { + + + /** + * 访问首页,提示语 + */ + @GetMapping("/") + public String index() { + return "index.html"; + } +} diff --git a/ruoyi-admin/src/main/java/com/xmzs/controller/PayController.java b/ruoyi-admin/src/main/java/com/xmzs/controller/PayController.java new file mode 100644 index 00000000..a03b8d89 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/xmzs/controller/PayController.java @@ -0,0 +1,151 @@ +package com.xmzs.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.extra.qrcode.QrCodeUtil; +import com.xmzs.common.config.PayConfig; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.oss.core.OssClient; +import com.xmzs.common.oss.entity.UploadResult; +import com.xmzs.common.oss.factory.OssFactory; +import com.xmzs.common.response.PayResponse; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.service.PayService; +import com.xmzs.common.utils.MD5Util; +import com.xmzs.system.domain.bo.PaymentOrdersBo; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.request.OrderRequest; +import com.xmzs.system.domain.vo.PaymentOrdersVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.service.IPaymentOrdersService; +import com.xmzs.system.service.ISysUserService; +import com.xmzs.system.util.OrderNumberGenerator; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/pay") +@Slf4j +public class PayController { + + private final PayService payService; + + private final ISysUserService userService; + + private final IPaymentOrdersService paymentOrdersService; + + /** + * 获取支付二维码 + * + * @Date 2023/7/3 + * @param response + * @return void + **/ + @PostMapping("/payUrl") + public R payUrl(HttpServletResponse response, @RequestBody OrderRequest orderRequest) { + LoginUser loginUser = LoginHelper.getLoginUser(); + // 创建订单 + PaymentOrdersBo paymentOrders = new PaymentOrdersBo(); + paymentOrders.setOrderName(orderRequest.getName()); + paymentOrders.setAmount(new BigDecimal(orderRequest.getMoney())); + String orderNo = OrderNumberGenerator.generate(); + paymentOrders.setOrderNo(orderNo); + paymentOrders.setUserId(loginUser.getUserId()); + // TODO 支付状态默认待支付 - 添加枚举 + paymentOrders.setPaymentStatus("1"); + paymentOrdersService.insertByBo(paymentOrders); + String payUrl = payService.getPayUrl(orderNo, orderRequest.getName(), Double.parseDouble(orderRequest.getMoney()), "192.168.1.6"); + byte[] bytes = QrCodeUtil.generatePng(payUrl, 300, 300); + OssClient storage = OssFactory.instance(); + UploadResult upload=storage.upload(bytes, storage.getPath("qrCode",".png"), "image/png"); + PaymentOrdersVo paymentOrdersVo = new PaymentOrdersVo(); + BeanUtil.copyProperties(paymentOrders,paymentOrdersVo); + paymentOrdersVo.setUrl(upload.getUrl()); + return R.ok(paymentOrdersVo); + } + + + /** + * 跳转通知地址 + * + * @Date 2023/7/3 + * @param + * @return void + **/ + @PostMapping("/notifyUrl") + public void notifyUrl() { + log.info("notifyUrl==========="); + } + + /** + * 获取订单信息 + * + */ + @PostMapping("/orderInfo") + public R orderInfo(@RequestBody OrderRequest orderRequest) { + if(StringUtils.isEmpty(orderRequest.getOrderNo())){ + throw new BaseException("订单号不能为空!"); + } + PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo(); + paymentOrdersBo.setOrderNo(orderRequest.getOrderNo()); + List paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo); + if (CollectionUtil.isEmpty(paymentOrdersList)){ + throw new BaseException("订单不存在!"); + } + PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0); + return R.ok(paymentOrdersVo); + } + + /** + * 跳转通知地址 + * + * @Date 2023/7/3 + * @param payResponse + * @return void + **/ + @GetMapping("/returnUrl") + public String returnUrl(PayResponse payResponse) { + // 校验签名 + String mdString = "money=" + payResponse.getMoney() + "&name=" + payResponse.getName() + + "&out_trade_no=" + payResponse.getOut_trade_no() + "&pid=" + PayConfig.pid + + "&trade_no=" + payResponse.getTrade_no() + "&trade_status=" + payResponse.getTrade_status() + + "&type=" + payResponse.getType() + PayConfig.key; + String sign = MD5Util.GetMD5Code(mdString); + if(!sign.equals(payResponse.getSign())){ + throw new BaseException("校验签名失败!"); + } + double money = Double.parseDouble(payResponse.getMoney()); + log.info("支付订单号{}",payResponse); + PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo(); + paymentOrdersBo.setOrderNo(payResponse.getOut_trade_no()); + List paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo); + if (CollectionUtil.isEmpty(paymentOrdersList)){ + throw new BaseException("订单不存在!"); + } + // 订单状态修改为已支付 + PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0); + paymentOrdersVo.setPaymentStatus("2"); + paymentOrdersVo.setPaymentMethod(payResponse.getType()); + BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo); + paymentOrdersService.updateByBo(paymentOrdersBo); + SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId()); + sysUserVo.setUserBalance(sysUserVo.getUserBalance()+money); + SysUserBo sysUserBo = new SysUserBo(); + BeanUtil.copyProperties(sysUserVo,sysUserBo); + // 设置为付费用户 + sysUserBo.setUserGrade("1"); + userService.updateUser(sysUserBo); + return "success"; + } + +} + diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml new file mode 100644 index 00000000..72324db9 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -0,0 +1,150 @@ +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 + +--- # xxl-job 配置 +xxl.job: + # 执行器开关 + enabled: false + # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。 + admin-addresses: http://localhost:9100/xxl-job-admin + # 执行器通讯TOKEN:非空时启用 + access-token: xxl-job + executor: + # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册 + appname: xxl-job-executor + # 执行器端口号 执行器从9101开始往后写 + port: 9101 + # 执行器注册:默认IP:PORT + address: + # 执行器IP:默认自动获取IP + ip: + # 执行器运行日志文件存储磁盘路径 + logpath: ./logs/xxl-job + # 执行器日志文件保存天数:大于3生效 + logretentiondays: 30 + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: ry-vue + password: ry-vue + + # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# hikari: +# connectionTestQuery: SELECT 1 FROM DUAL +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 连接测试query(配置检测连接是否有效) + connectionTestQuery: SELECT 1 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码(如没有密码请注释掉) + # password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 4 + # Netty线程池数量 + nettyThreads: 8 + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${ruoyi.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 8 + # 连接池大小 + connectionPoolSize: 32 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml new file mode 100644 index 00000000..fdf6c238 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -0,0 +1,174 @@ +--- # 临时文件存储位置 避免临时文件被系统清理报错 +spring.servlet.multipart.location: /ruoyi/server/temp + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 + +--- # xxl-job 配置 +xxl.job: + # 执行器开关 + enabled: false + # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。 + admin-addresses: http://localhost:9100/xxl-job-admin + # 执行器通讯TOKEN:非空时启用 + access-token: xxl-job + executor: + # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册 + appname: xxl-job-executor + # 执行器端口号 执行器从9101开始往后写 + port: 9101 + # 执行器注册:默认IP:PORT + address: + # 执行器IP:默认自动获取IP + ip: + # 执行器运行日志文件存储磁盘路径 + logpath: ./logs/xxl-job + # 执行器日志文件保存天数:大于3生效 + logretentiondays: 30 + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: false + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: root + password: root + # 从库数据源 + slave: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: + password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# hikari: +# connectionTestQuery: SELECT 1 FROM DUAL +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 连接测试query(配置检测连接是否有效) + connectionTestQuery: SELECT 1 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码(如没有密码请注释掉) + # password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${ruoyi.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 00000000..21309dc0 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,345 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: "xmzs" + # 版本 + version: ${revision} + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: false + +captcha: + enable: false + # 页面 <参数设置> 可开启关闭 验证码校验 + # 验证码类型 math 数组计算 char 字符验证 + type: MATH + # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 + category: CIRCLE + # 数字验证码位数 + numberLength: 1 + # 字符验证码长度 + charLength: 4 + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 6039 + servlet: + # 应用的访问路径 + context-path: / + # undertow 配置 + undertow: + # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 + max-http-post-size: -1 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + # 每块buffer的空间大小,越小的空间被利用越充分 + buffer-size: 512 + # 是否分配的直接内存 + direct-buffers: true + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 8 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 256 + +# 日志配置 +logging: + level: + com.xmzs: @logging.level@ + org.springframework: warn + config: classpath:logback-plus.xml + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + application: + name: ${ruoyi.name} + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: @profiles.active@ + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + mvc: + format: + date-time: yyyy-MM-dd HH:mm:ss + jackson: + # 日期格式化 + date-format: yyyy-MM-dd HH:mm:ss + serialization: + # 格式化输出 + indent_output: false + # 忽略无法转换的对象 + fail_on_empty_beans: false + deserialization: + # 允许对象忽略json中不存在的属性 + fail_on_unknown_properties: false + +# Sa-Token配置 +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # token有效期 设为7天 (必定过期) 单位: 秒 + timeout: 604800 + # token临时有效期 (指定时间无操作就过期) 单位: 秒 + activity-timeout: 604800 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: false + # 是否尝试从header里读取token + is-read-header: true + # 是否尝试从cookie里读取token + is-read-cookie: false + # token前缀 + token-prefix: "Bearer" + # jwt秘钥 + jwt-secret-key: abcdefghijklmnopqrstuvwxyz + +# security配置 +security: + # 排除路径 + excludes: + # 修改用户头像 + - /system/user/edit/avatar + - /pay/returnUrl + - /pay/notifyUrl + # 上传文件 + - /resource/oss/upload + # 重置密码 + - /auth/reset/password + # 聊天接口 + - /chat + # 静态资源 + - /*.html + - /**/*.html + - /**/*.css + - /**/*.js + # 公共路径 + - /favicon.ico + - /error + # swagger 文档配置 + - /*/api-docs + - /*/api-docs/** + # actuator 监控配置 + - /actuator + - /actuator/** +# 多租户配置 +tenant: + # 是否开启 + enable: false + # 排除表 + excludes: + - sys_menu + - sys_tenant + - sys_tenant_package + - sys_role_dept + - sys_role_menu + - sys_user_post + - sys_user_role + +# MyBatisPlus配置 +# https://baomidou.com/config/ +mybatis-plus: + # 不支持多包, 如有需要可在注解配置 或 提升扫包等级 + # 例如 com.**.**.mapper + mapperPackage: com.xmzs.**.mapper + # 对应的 XML 文件位置 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.xmzs.**.domain + # 启动时是否检查 MyBatis XML 文件的存在,默认不检查 + checkConfigLocation: false + configuration: + # 自动驼峰命名规则(camel case)映射 + mapUnderscoreToCamelCase: true + # MyBatis 自动映射策略 + # NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射 + autoMappingBehavior: FULL + # MyBatis 自动映射时未知列或未知属性处理策 + # NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息 + autoMappingUnknownColumnBehavior: NONE + # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl + # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl + # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl + logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl + global-config: + # 是否打印 Logo banner + banner: true + dbConfig: + # 主键类型 + # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID + idType: ASSIGN_ID + # 逻辑已删除值 + logicDeleteValue: 2 + # 逻辑未删除值 + logicNotDeleteValue: 0 + # 字段验证策略之 insert,在 insert 的时候的字段验证策略 + # IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL + insertStrategy: NOT_NULL + # 字段验证策略之 update,在 update 的时候的字段验证策略 + updateStrategy: NOT_NULL + # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件 + where-strategy: NOT_NULL + +# 数据加密 +mybatis-encryptor: + # 是否开启加密 + enable: false + # 默认加密算法 + algorithm: BASE64 + # 编码方式 BASE64/HEX。默认BASE64 + encode: BASE64 + # 安全秘钥 对称算法的秘钥 如:AES,SM4 + password: + # 公私钥 非对称算法的公私钥 如:SM2,RSA + publicKey: + privateKey: +--- # mail 邮件发送 +mail: + enabled: true + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(填写授权码) + pass: pass + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +# Swagger配置 +swagger: + info: + # 标题 + title: '标题:${ruoyi.name}多租户管理系统_接口文档' + # 描述 + description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' + # 版本 + version: '版本号: ${ruoyi.version}' + # 作者信息 + contact: + name: ageerle + email: ageerle@163.com + url: https://gitee.com/ageerle/ruoyi-ai + components: + # 鉴权方式配置 + security-schemes: + apiKey: + type: APIKEY + in: HEADER + name: ${sa-token.token-name} + +springdoc: + api-docs: + # 是否开启接口文档 + enabled: true + swagger-ui: + # 持久化认证数据 + persistAuthorization: true + #这里定义了两个分组,可定义多个,也可以不定义 + group-configs: + - group: 1.演示模块 + packages-to-scan: com.xmzs.demo + - group: 2.通用模块 + packages-to-scan: com.xmzs.web + - group: 3.系统模块 + packages-to-scan: com.xmzs.system + - group: 4.代码生成模块 + packages-to-scan: com.xmzs.generator + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 全局线程池相关配置 +thread-pool: + # 是否开启线程池 + enabled: false + # 队列最大长度 + queueCapacity: 128 + # 线程池维护线程所允许的空闲时间 + keepAliveSeconds: 300 + +--- # 分布式锁 lock4j 全局配置 +lock4j: + # 获取分布式锁超时时间,默认为 3000 毫秒 + acquire-timeout: 3000 + # 分布式锁的超时时间,默认为 30 秒 + expire: 30000 + +--- # Actuator 监控端点的配置项 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/sys-console.log + +--- # websocket +websocket: + enabled: true + # 路径 + path: '' + # 设置访问源地址 + allowedOrigins: '*' +# AI助手配置信息 +chat: + apiKey: '' + apiHost: '' +# 中转接口 +transit: + apiKey: '' + apiHost: 'https://api.gptgod.online/' +# 微信小程序配置信息 +wx: + miniapp: + configs: + - appid: # 你的appid + secret: # 你的secret + token: #微信小程序消息服务器配置的token + aesKey: #微信小程序消息服务器配置的EncodingAESKey + msgDataFormat: JSON +baidu: + # 是否开启文本审核 + enabled: false + # 文本审核 + textReview: + apiKey: '' # apiKey + secretKey: '' # secretKey + diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 00000000..4a9538ea --- /dev/null +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -0,0 +1,9 @@ +Application Version: ${revision} +Spring Boot Version: ${spring-boot.version} + ██ ██ ██ ██ + ██████ ░██ ░██ ░██ █████ ██████ ░██ +░██░░░██ ██████ ███████ ░██ ██████ █████ ░██ ██████ ██████ ██░░░██░██░░░██ ██████ +░██ ░██ ░░░░░░██ ░░██░░░██ ██████ ░░░░░░██ █████ ██░░░██░██████ ░░░░░░██ ░░░██░ ░██ ░██░██ ░██░░░██░ +░██████ ███████ ░██ ░██ ██░░░██ ███████ ░░░░░ ░██ ░░ ░██░░░██ ███████ ░██ ░░██████░██████ ░██ +░██░░░ ██░░░░██ ░██ ░██░██ ░██ ██░░░░██ ░██ ██░██ ░██ ██░░░░██ ░██ ░░░░░██░██░░░ ░██ +░██ ░░████████ ███ ░██░░██████░░████████ ░░█████ ░██ ░██░░████████ ░░██ █████ ░██ ░░██ diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 00000000..4f2da28b --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,54 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序code不能为空 +##租户 +tenant.number.not.blank=租户编号不能为空 +tenant.not.exists=对不起, 您的租户不存在,请联系管理员 +tenant.blocked=对不起,您的租户已禁用,请联系管理员 +tenant.expired=对不起,您的租户已过期,请联系管理员 diff --git a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 00000000..0c738a37 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,54 @@ +#错误消息 +not.null=* Required fill in +user.jcaptcha.error=Captcha error +user.jcaptcha.expire=Captcha invalid +user.not.exists=Sorry, your account: {0} does not exist +user.password.not.match=User does not exist/Password error +user.password.retry.limit.count=Password input error {0} times +user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes +user.password.delete=Sorry, your account:{0} has been deleted +user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator +role.blocked=Role disabled,please contact administrators +user.logout.success=Exit successful +length.not.valid=The length must be between {min} and {max} characters +user.username.not.blank=Username cannot be blank +user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number +user.username.length.valid=Account length must be between {min} and {max} characters +user.password.not.blank=Password cannot be empty +user.password.length.valid=Password length must be between {min} and {max} characters +user.password.not.valid=* 5-50 characters +user.email.not.valid=Mailbox format error +user.email.not.blank=Mailbox cannot be blank +user.phonenumber.not.blank=Phone number cannot be blank +user.mobile.phone.number.not.valid=Phone number format error +user.login.success=Login successful +user.register.success=Register successful +user.register.save.error=Failed to save user {0}, The registered account already exists +user.register.error=Register failed, please contact system administrator +user.notfound=Please login again +user.forcelogout=The administrator is forced to exit,please login again +user.unknown.error=Unknown error, please login again +##文件上传消息 +upload.exceed.maxSize=The uploaded file size exceeds the limit file size!
the maximum allowed file size is:{0}MB! +upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters +##权限 +no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}] +no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}] +no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}] +no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}] +no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}] +no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}] +repeat.submit.message=Repeat submit is not allowed, please try again later +rate.limiter.message=Visit too frequently, please try again later +sms.code.not.blank=Sms code cannot be blank +sms.code.retry.limit.count=Sms code input error {0} times +sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes +email.code.not.blank=Email code cannot be blank +email.code.retry.limit.count=Email code input error {0} times +email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes +xcx.code.not.blank=Mini program code cannot be blank +##租户 +tenant.number.not.blank=Tenant number cannot be blank +tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator +tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator +tenant.expired=Sorry, your tenant has expired. Please contact the administrator. diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 00000000..4f2da28b --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,54 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序code不能为空 +##租户 +tenant.number.not.blank=租户编号不能为空 +tenant.not.exists=对不起, 您的租户不存在,请联系管理员 +tenant.blocked=对不起,您的租户已禁用,请联系管理员 +tenant.expired=对不起,您的租户已过期,请联系管理员 diff --git a/ruoyi-admin/src/main/resources/ip2region.xdb b/ruoyi-admin/src/main/resources/ip2region.xdb new file mode 100644 index 00000000..31f96a1f Binary files /dev/null and b/ruoyi-admin/src/main/resources/ip2region.xdb differ diff --git a/ruoyi-admin/src/main/resources/logback-plus.xml b/ruoyi-admin/src/main/resources/logback-plus.xml new file mode 100644 index 00000000..40fa33b7 --- /dev/null +++ b/ruoyi-admin/src/main/resources/logback-plus.xml @@ -0,0 +1,129 @@ + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/sys-console.log + + + ${log.path}/sys-console.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + 0 + + 512 + + + + + + + + 0 + + 512 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/spy.properties b/ruoyi-admin/src/main/resources/spy.properties new file mode 100644 index 00000000..cbce2005 --- /dev/null +++ b/ruoyi-admin/src/main/resources/spy.properties @@ -0,0 +1 @@ +exclude=SELECT 1 diff --git a/ruoyi-admin/src/main/resources/static/1.jpg b/ruoyi-admin/src/main/resources/static/1.jpg new file mode 100644 index 00000000..0a21dea6 Binary files /dev/null and b/ruoyi-admin/src/main/resources/static/1.jpg differ diff --git a/ruoyi-admin/src/main/resources/static/index.html b/ruoyi-admin/src/main/resources/static/index.html new file mode 100644 index 00000000..e2122961 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/index.html @@ -0,0 +1,755 @@ + + + + + 开发教程分享 + + + + +
+
+ +
+ +
+ + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/test/java/com/xmzs/test/AssertUnitTest.java b/ruoyi-admin/src/test/java/com/xmzs/test/AssertUnitTest.java new file mode 100644 index 00000000..6d1f14ab --- /dev/null +++ b/ruoyi-admin/src/test/java/com/xmzs/test/AssertUnitTest.java @@ -0,0 +1,45 @@ +package com.xmzs.test; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * 断言单元测试案例 + * + * @author Lion Li + */ +@DisplayName("断言单元测试案例") +public class AssertUnitTest { + + @DisplayName("测试 assertEquals 方法") + @Test + public void testAssertEquals() { + Assertions.assertEquals("666", new String("666")); + Assertions.assertNotEquals("666", new String("666")); + } + + @DisplayName("测试 assertSame 方法") + @Test + public void testAssertSame() { + Object obj = new Object(); + Object obj1 = obj; + Assertions.assertSame(obj, obj1); + Assertions.assertNotSame(obj, obj1); + } + + @DisplayName("测试 assertTrue 方法") + @Test + public void testAssertTrue() { + Assertions.assertTrue(true); + Assertions.assertFalse(true); + } + + @DisplayName("测试 assertNull 方法") + @Test + public void testAssertNull() { + Assertions.assertNull(null); + Assertions.assertNotNull(null); + } + +} diff --git a/ruoyi-admin/src/test/java/com/xmzs/test/DemoUnitTest.java b/ruoyi-admin/src/test/java/com/xmzs/test/DemoUnitTest.java new file mode 100644 index 00000000..6981eb8a --- /dev/null +++ b/ruoyi-admin/src/test/java/com/xmzs/test/DemoUnitTest.java @@ -0,0 +1,70 @@ +package com.xmzs.test; + +import com.xmzs.common.core.config.RuoYiConfig; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.concurrent.TimeUnit; + +/** + * 单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件 +@DisplayName("单元测试案例") +public class DemoUnitTest { + + @Autowired + private RuoYiConfig ruoYiConfig; + + @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") + @Test + public void testTest() { + System.out.println(ruoYiConfig); + } + + @Disabled + @DisplayName("测试 @Disabled 注解") + @Test + public void testDisabled() { + System.out.println(ruoYiConfig); + } + + @Timeout(value = 2L, unit = TimeUnit.SECONDS) + @DisplayName("测试 @Timeout 注解") + @Test + public void testTimeout() throws InterruptedException { + Thread.sleep(3000); + System.out.println(ruoYiConfig); + } + + + @DisplayName("测试 @RepeatedTest 注解") + @RepeatedTest(3) + public void testRepeatedTest() { + System.out.println(666); + } + + @BeforeAll + public static void testBeforeAll() { + System.out.println("@BeforeAll =================="); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + @AfterAll + public static void testAfterAll() { + System.out.println("@AfterAll =================="); + } + +} diff --git a/ruoyi-admin/src/test/java/com/xmzs/test/ParamUnitTest.java b/ruoyi-admin/src/test/java/com/xmzs/test/ParamUnitTest.java new file mode 100644 index 00000000..82b9f55c --- /dev/null +++ b/ruoyi-admin/src/test/java/com/xmzs/test/ParamUnitTest.java @@ -0,0 +1,72 @@ +package com.xmzs.test; + +import com.xmzs.common.core.enums.UserType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +/** + * 带参数单元测试案例 + * + * @author Lion Li + */ +@DisplayName("带参数单元测试案例") +public class ParamUnitTest { + + @DisplayName("测试 @ValueSource 注解") + @ParameterizedTest + @ValueSource(strings = {"t1", "t2", "t3"}) + public void testValueSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @NullSource 注解") + @ParameterizedTest + @NullSource + public void testNullSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @EnumSource 注解") + @ParameterizedTest + @EnumSource(UserType.class) + public void testEnumSource(UserType type) { + System.out.println(type.getUserType()); + } + + @DisplayName("测试 @MethodSource 注解") + @ParameterizedTest + @MethodSource("getParam") + public void testMethodSource(String str) { + System.out.println(str); + } + + public static Stream getParam() { + List list = new ArrayList<>(); + list.add("t1"); + list.add("t2"); + list.add("t3"); + return list.stream(); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/ruoyi-admin/src/test/java/com/xmzs/test/TagUnitTest.java b/ruoyi-admin/src/test/java/com/xmzs/test/TagUnitTest.java new file mode 100644 index 00000000..e4513873 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/xmzs/test/TagUnitTest.java @@ -0,0 +1,54 @@ +package com.xmzs.test; + +import org.junit.jupiter.api.*; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * 标签单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest +@DisplayName("标签单元测试案例") +public class TagUnitTest { + + @Tag("dev") + @DisplayName("测试 @Tag dev") + @Test + public void testTagDev() { + System.out.println("dev"); + } + + @Tag("prod") + @DisplayName("测试 @Tag prod") + @Test + public void testTagProd() { + System.out.println("prod"); + } + + @Tag("local") + @DisplayName("测试 @Tag local") + @Test + public void testTagLocal() { + System.out.println("local"); + } + + @Tag("exclude") + @DisplayName("测试 @Tag exclude") + @Test + public void testTagExclude() { + System.out.println("exclude"); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml new file mode 100644 index 00000000..c155b140 --- /dev/null +++ b/ruoyi-common/pom.xml @@ -0,0 +1,46 @@ + + + + ruoyi-ai + com.xmzs + ${revision} + ../pom.xml + + 4.0.0 + + + ruoyi-common-bom + ruoyi-common-core + ruoyi-common-doc + ruoyi-common-excel + ruoyi-common-idempotent + ruoyi-common-job + ruoyi-common-log + ruoyi-common-mail + ruoyi-common-mybatis + ruoyi-common-oss + ruoyi-common-ratelimiter + ruoyi-common-redis + ruoyi-common-satoken + ruoyi-common-security + ruoyi-common-sms + ruoyi-common-web + ruoyi-common-translation + ruoyi-common-sensitive + ruoyi-common-json + ruoyi-common-encrypt + ruoyi-common-tenant + ruoyi-common-chat + ruoyi-common-pay + + + ruoyi-common + pom + + + common 通用模块 + + + diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml new file mode 100644 index 00000000..3f1db623 --- /dev/null +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -0,0 +1,178 @@ + + + 4.0.0 + + com.xmzs + ruoyi-common-bom + ${revision} + pom + + + ruoyi-common-bom common依赖项 + + + + 1.0.0 + + + + + + + com.xmzs + ruoyi-common-core + ${revision} + + + + + com.xmzs + ruoyi-common-doc + ${revision} + + + + + com.xmzs + ruoyi-common-excel + ${revision} + + + + + com.xmzs + ruoyi-common-idempotent + ${revision} + + + + + com.xmzs + ruoyi-common-job + ${revision} + + + + + com.xmzs + ruoyi-common-log + ${revision} + + + + + com.xmzs + ruoyi-common-mail + ${revision} + + + + + com.xmzs + ruoyi-common-mybatis + ${revision} + + + + + com.xmzs + ruoyi-common-oss + ${revision} + + + + + com.xmzs + ruoyi-common-ratelimiter + ${revision} + + + + + com.xmzs + ruoyi-common-redis + ${revision} + + + + + com.xmzs + ruoyi-common-satoken + ${revision} + + + + + com.xmzs + ruoyi-common-security + ${revision} + + + + + com.xmzs + ruoyi-common-sms + ${revision} + + + + + com.xmzs + ruoyi-common-web + ${revision} + + + + + com.xmzs + ruoyi-common-translation + ${revision} + + + + + com.xmzs + ruoyi-common-sensitive + ${revision} + + + + + com.xmzs + ruoyi-common-json + ${revision} + + + + + com.xmzs + ruoyi-common-encrypt + ${revision} + + + + + com.xmzs + ruoyi-common-tenant + ${revision} + + + + + com.xmzs + ruoyi-common-chat + ${revision} + + + + + com.xmzs + ruoyi-common-pay + ${revision} + + + + + diff --git a/ruoyi-common/ruoyi-common-chat/pom.xml b/ruoyi-common/ruoyi-common-chat/pom.xml new file mode 100644 index 00000000..92b65ade --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/pom.xml @@ -0,0 +1,86 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-chat + + + ruoyi-common-chat 模块 + + + + 2.9.0 + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-json + + + + com.xmzs + ruoyi-common-redis + + + com.xmzs + ruoyi-common-satoken + + + com.xmzs + ruoyi-common-json + + + org.springframework.boot + spring-boot-starter-websocket + + + + com.squareup.retrofit2 + retrofit + ${retrofit2.version} + + + com.squareup.retrofit2 + converter-jackson + ${retrofit2.version} + + + com.squareup.retrofit2 + adapter-rxjava2 + ${retrofit2.version} + + + + com.knuddels + jtokkit + 0.5.0 + + + + + com.azure + azure-ai-openai + 1.0.0-beta.6 + + + + cn.hutool + hutool-all + 5.8.12 + + + diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/ChatConfig.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/ChatConfig.java new file mode 100644 index 00000000..e10dd535 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/ChatConfig.java @@ -0,0 +1,49 @@ +package com.xmzs.common.chat.config; + + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import com.xmzs.common.chat.openai.OpenAiStreamClient; +import com.xmzs.common.chat.openai.function.KeyRandomStrategy; +import com.xmzs.common.chat.openai.interceptor.OpenAILogger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * chat配置类 + * + * @author: wangle + * @date: 2023/5/16 + */ +@Configuration +public class ChatConfig { + @Value("${chat.apiKey}") + private List apiKey; + @Value("${chat.apiHost}") + private String apiHost; + + @Bean(name = "openAiStreamClient") + public OpenAiStreamClient openAiStreamClient() { + HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger()); + httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); + OkHttpClient okHttpClient = new OkHttpClient + .Builder() + .addInterceptor(httpLoggingInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .writeTimeout(600, TimeUnit.SECONDS) + .readTimeout(600, TimeUnit.SECONDS) + .build(); + return OpenAiStreamClient + .builder() + .apiHost(apiHost) + .apiKey(apiKey) + //自定义key使用策略 默认随机策略 + .keyStrategy(new KeyRandomStrategy()) + .okHttpClient(okHttpClient) + .build(); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/LocalCache.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/LocalCache.java new file mode 100644 index 00000000..e3a6b955 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/LocalCache.java @@ -0,0 +1,36 @@ +package com.xmzs.common.chat.config; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; +import lombok.extern.slf4j.Slf4j; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @date 2023-03-10 + */ +@Slf4j +public class LocalCache { + /** + * 缓存时长 + */ + public static final long TIMEOUT = 30 * DateUnit.MINUTE.getMillis(); + /** + * 清理间隔 + */ + private static final long CLEAN_TIMEOUT = 30 * DateUnit.MINUTE.getMillis(); + + /** + * 缓存对象 + */ + public static final TimedCache CACHE = CacheUtil.newTimedCache(TIMEOUT); + + + static { + //启动定时任务 + CACHE.schedulePrune(CLEAN_TIMEOUT); + } + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/WebSocketConfig.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/WebSocketConfig.java new file mode 100644 index 00000000..c617bfd9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/WebSocketConfig.java @@ -0,0 +1,61 @@ +package com.xmzs.common.chat.config; + +import cn.hutool.core.util.StrUtil; +import com.xmzs.common.chat.config.properties.WebSocketProperties; +import com.xmzs.common.chat.handler.PlusWebSocketHandler; +import com.xmzs.common.chat.interceptor.PlusWebSocketInterceptor; +import com.xmzs.common.chat.listener.WebSocketTopicListener; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.server.HandshakeInterceptor; + +/** + * WebSocket 配置 + * + * @author zendwang + */ +@AutoConfiguration +@ConditionalOnProperty(value = "websocket.enabled", havingValue = "true") +@EnableConfigurationProperties(WebSocketProperties.class) +@EnableWebSocket +public class WebSocketConfig { + + @Bean + public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor, + WebSocketHandler webSocketHandler, + WebSocketProperties webSocketProperties) { + if (StrUtil.isBlank(webSocketProperties.getPath())) { + webSocketProperties.setPath("/websocket"); + } + + if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) { + webSocketProperties.setAllowedOrigins("*"); + } + + return registry -> registry + .addHandler(webSocketHandler, webSocketProperties.getPath()) + .addInterceptors(handshakeInterceptor) + .setAllowedOrigins(webSocketProperties.getAllowedOrigins()); + } + + @Bean + public HandshakeInterceptor handshakeInterceptor() { + return new PlusWebSocketInterceptor(); + } + + @Bean + public WebSocketHandler webSocketHandler() { + return new PlusWebSocketHandler(); + } + + @Bean + public WebSocketTopicListener topicListener() { + return new WebSocketTopicListener(); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/properties/WebSocketProperties.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/properties/WebSocketProperties.java new file mode 100644 index 00000000..6fef98f8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/properties/WebSocketProperties.java @@ -0,0 +1,26 @@ +package com.xmzs.common.chat.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * WebSocket 配置项 + * + * @author zendwang + */ +@ConfigurationProperties("websocket") +@Data +public class WebSocketProperties { + + private Boolean enabled; + + /** + * 路径 + */ + private String path; + + /** + * 设置访问源地址 + */ + private String allowedOrigins; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/OpenAIConst.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/OpenAIConst.java new file mode 100644 index 00000000..317db614 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/OpenAIConst.java @@ -0,0 +1,31 @@ +package com.xmzs.common.chat.constant; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-06 + */ +public class OpenAIConst { + + public final static String OPENAI_HOST = "https://api.openai.com/"; + + public final static int SUCCEED_CODE = 200; + + public final static double GPT3_COST = 0.03; + + public final static double GPT4_COST = 0.3; + + /** 绘图费用 */ + public final static double DALL3_COST = 0.3; + + /** 绘图费用-高清 */ + public final static double DALL3_HD_COST = 0.6; + + /** mdjourney绘图费用 */ + public final static double MJ_COST = 0.3; + + /** 默认账户余额 */ + public final static double USER_BALANCE = 5; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/WebSocketConstants.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/WebSocketConstants.java new file mode 100644 index 00000000..e0e67e09 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/WebSocketConstants.java @@ -0,0 +1,28 @@ +package com.xmzs.common.chat.constant; + +/** + * websocket的常量配置 + * + * @author zendwang + */ +public interface WebSocketConstants { + /** + * websocketSession中的参数的key + */ + String LOGIN_USER_KEY = "loginUser"; + + /** + * 订阅的频道 + */ + String WEB_SOCKET_TOPIC = "global:websocket"; + + /** + * 前端心跳检查的命令 + */ + String PING = "ping"; + + /** + * 服务端心跳恢复的字符串 + */ + String PONG = "pong"; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatProcessRequest.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatProcessRequest.java new file mode 100644 index 00000000..7a88cc38 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatProcessRequest.java @@ -0,0 +1,31 @@ +package com.xmzs.common.chat.domain.request; + + +import lombok.Data; + +/** + * @author hncboy + * @date 2023/3/23 13:17 + * 消息处理请求 + */ +@Data +public class ChatProcessRequest { + + private String prompt; + + private Options options; + + private String systemMessage; + + @Data + public static class Options { + + private String conversationId; + + /** + * 这里的父级消息指的是回答的父级消息 id + * 前端发送问题,需要上下文的话传回答的父级消息 id + */ + private String parentMessageId; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatRequest.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatRequest.java new file mode 100644 index 00000000..802118c2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatRequest.java @@ -0,0 +1,55 @@ +package com.xmzs.common.chat.domain.request; + +import com.xmzs.common.chat.entity.chat.Content; +import com.xmzs.common.chat.entity.chat.Message; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @sine 2023-04-08 + */ +@Data +public class ChatRequest { + + @NotEmpty(message = "传入的模型不能为空") + private String model; + + @NotEmpty(message = "对话消息不能为空") + List messages; + + List content; + + private String prompt; + + private String userId; + + /** + * 需要识别的图片地址 + */ + private String imgurl; + + /** + * gpt的默认设置 + */ + private String systemMessage = ""; + + private double top_p = 1; + + private double temperature = 0.2; + + /** + * 上下文的条数 + */ + private Integer contentNumber = 10; + + /** + * 是否携带上下文 + */ + private Boolean usingContext = Boolean.TRUE; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/Dall3Request.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/Dall3Request.java new file mode 100644 index 00000000..11a7ae52 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/Dall3Request.java @@ -0,0 +1,33 @@ +package com.xmzs.common.chat.domain.request; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @sine 2023-04-08 + */ +@Data +public class Dall3Request { + + @NotEmpty(message = "传入的模型不能为空") + private String model; + + @NotEmpty(message = "提示词不能为空") + private String prompt; + + /** 图片大小 */ + @NotEmpty(message = "图片大小不能为空") + private String size ; + + /** 图片质量 */ + @NotEmpty(message = "图片质量不能为空") + private String quality; + + /** 图片风格 */ + @NotEmpty(message = "图片风格不能为空") + private String style; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/SaveMsgRequest.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/SaveMsgRequest.java new file mode 100644 index 00000000..cccd824a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/SaveMsgRequest.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.domain.request; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @sine 2023-04-08 + */ +@Data +public class SaveMsgRequest { + + @NotEmpty(message = "传入的模型不能为空") + private String model; + + @NotEmpty(message = "对话消息不能为空") + private String msg; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/response/ChatResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/response/ChatResponse.java new file mode 100644 index 00000000..14104542 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/response/ChatResponse.java @@ -0,0 +1,19 @@ +package com.xmzs.common.chat.domain.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @sine 2023-04-08 + */ +@Data +public class ChatResponse { + /** + * 问题消耗tokens + */ + @JsonProperty("question_tokens") + private long questionTokens = 0; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/BillingUsage.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/BillingUsage.java new file mode 100644 index 00000000..b7c402a9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/BillingUsage.java @@ -0,0 +1,33 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 描述:金额消耗信息 + * + * @author https:www.unfbx.com + * @since 2023-04-08 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class BillingUsage { + + @JsonProperty("object") + private String object; + /** + * 账号金额消耗明细 + */ + @JsonProperty("daily_costs") + private List dailyCosts; + /** + * 总使用金额:美分 + */ + @JsonProperty("total_usage") + private BigDecimal totalUsage; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/CreditGrantsResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/CreditGrantsResponse.java new file mode 100644 index 00000000..9937e639 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/CreditGrantsResponse.java @@ -0,0 +1,39 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 描述:余额查询接口返回值 + * + * @author https:www.unfbx.com + * @since 2023-03-18 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreditGrantsResponse implements Serializable { + private String object; + /** + * 总金额:美元 + */ + @JsonProperty("total_granted") + private BigDecimal totalGranted; + /** + * 总使用金额:美元 + */ + @JsonProperty("total_used") + private BigDecimal totalUsed; + /** + * 总剩余金额:美元 + */ + @JsonProperty("total_available") + private BigDecimal totalAvailable; + /** + * 余额明细 + */ + private Grants grants; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/DailyCost.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/DailyCost.java new file mode 100644 index 00000000..4af4bc29 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/DailyCost.java @@ -0,0 +1,28 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * 描述:金额消耗列表 + * + * @author https:www.unfbx.com + * @since 2023-04-08 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DailyCost { + /** + * 时间戳 + */ + @JsonProperty("timestamp") + private long timestamp; + /** + * 模型消耗金额详情 + */ + @JsonProperty("line_items") + private List lineItems; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Datum.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Datum.java new file mode 100644 index 00000000..a9b8e39d --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Datum.java @@ -0,0 +1,40 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-18 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Datum { + private String object; + private String id; + /** + * 赠送金额:美元 + */ + @JsonProperty("grant_amount") + private BigDecimal grantAmount; + /** + * 使用金额:美元 + */ + @JsonProperty("used_amount") + private BigDecimal usedAmount; + /** + * 生效时间戳 + */ + @JsonProperty("effective_at") + private Long effectiveAt; + /** + * 过期时间戳 + */ + @JsonProperty("expires_at") + private Long expiresAt; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Grants.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Grants.java new file mode 100644 index 00000000..41a4fd07 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Grants.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-18 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Grants { + private String object; + @JsonProperty("data") + private List data; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/KeyInfo.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/KeyInfo.java new file mode 100644 index 00000000..7933fa09 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/KeyInfo.java @@ -0,0 +1,56 @@ +package com.xmzs.common.chat.entity.billing; + +import lombok.*; + +import java.time.LocalDate; + +/** + * openKey信息 + * + * @author admin + * @date 2023/6/15 + */ +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class KeyInfo { + /** + * 订阅类型 + */ + private String planTitle; + /** + * key值 + */ + private String keyValue; + /** + * 剩余额度 + */ + private Double remaining; + + /** + * 账户总余额 + */ + private Double totalAmount; + + /** + * 已使用的额度 + */ + private Double totalUsage; + + /** + * 截至日期 + */ + private LocalDate limitDate; + + /** + * 是否绑卡 + */ + private Boolean isHasPaymentMethod; + + /** + * 最高可用模型 + */ + private String model; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/LineItem.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/LineItem.java new file mode 100644 index 00000000..b09c510a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/LineItem.java @@ -0,0 +1,25 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 描述:金额消耗列表 + * + * @author https:www.unfbx.com + * @since 2023-04-08 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class LineItem { + /** + * 模型名称 + */ + private String name; + /** + * 消耗金额 + */ + private BigDecimal cost; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Plan.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Plan.java new file mode 100644 index 00000000..c0af8388 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Plan.java @@ -0,0 +1,17 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-04-08 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Plan { + private String title; + private String id; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Subscription.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Subscription.java new file mode 100644 index 00000000..bbdad7f9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Subscription.java @@ -0,0 +1,73 @@ +package com.xmzs.common.chat.entity.billing; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 描述:账户信息 + * + * @author https:www.unfbx.com + * @since 2023-04-08 + */ +@Data +public class Subscription { + + @JsonProperty("object") + private String object; + + /** + * 付款方式 + */ + @JsonProperty("has_payment_method") + private boolean hasPaymentMethod; + + @JsonProperty("canceled") + private boolean canceled; + @JsonProperty("canceled_at") + private Object canceledAt; + + @JsonProperty("delinquent") + private Object delinquent; + @JsonProperty("access_until") + private long accessUntil; + @JsonProperty("soft_limit") + private long softLimit; + @JsonProperty("hard_limit") + private long hardLimit; + @JsonProperty("system_hard_limit") + private long systemHardLimit; + @JsonProperty("soft_limit_usd") + private double softLimitUsd; + @JsonProperty("hard_limit_usd") + private double hardLimitUsd; + @JsonProperty("system_hard_limit_usd") + private double systemHardLimitUsd; + /** + * 计划 + */ + @JsonProperty("plan") + private Plan plan; + + /** + * 账户名称 + */ + @JsonProperty("account_name") + private String accountName; + + @JsonProperty("po_number") + private Object poNumber; + + /** + * 账单邮箱 + */ + @JsonProperty("billing_email") + private Object billingEmail; + @JsonProperty("tax_ids") + private Object taxIds; + @JsonProperty("billing_address") + private Object billingAddress; + @JsonProperty("business_address") + private Object businessAddress; + @JsonProperty("primary") + private Boolean primary; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseChatCompletion.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseChatCompletion.java new file mode 100644 index 00000000..6bbede9e --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseChatCompletion.java @@ -0,0 +1,238 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import com.xmzs.common.chat.constant.OpenAIConst; +import com.xmzs.common.chat.entity.chat.tool.Tools; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import static com.xmzs.common.chat.entity.chat.BaseChatCompletion.Model.GPT_3_5_TURBO; + +/** + * 描述: chat模型基础类 + * + * @author https:www.unfbx.com + * @since 1.1.2 + * 2023-11-10 + */ +@Data +@SuperBuilder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class BaseChatCompletion implements Serializable { + + @NonNull + @Builder.Default + private String model = GPT_3_5_TURBO.getName(); + + /** + * 指定模型必须输出的格式的对象。 + * + * @since 1.1.2 + */ + @JsonProperty("response_format") + private ResponseFormat responseFormat; + + /** + * 已过时 + * + * @see #tools + */ + @Deprecated + private List functions; + + /** + * 取值:null,auto或者自定义 + * functions没有值的时候默认为:null + * functions存在值得时候默认为:auto + * 也可以自定义 + *

已过时

+ * + * @see #toolChoice + */ + @Deprecated + @JsonProperty("function_call") + private Object functionCall; + + /** + * 模型可能调用的工具列表。 + * 当前版本仅支持:functions + * + * @since 1.1.2 + */ + private List tools; + + /** + * 取值:String或者ToolChoiceObj + * + * @since 1.1.2 + */ + @JsonProperty("tool_choice") + private Object toolChoice; + + /** + * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。 + *

+ * We generally recommend altering this or but not both.top_p + */ + @Builder.Default + private double temperature = 0.2; + + /** + * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。 + *

+ * 我们通常建议更改此设置,但不要同时更改两者。temperature + */ + @JsonProperty("top_p") + @Builder.Default + private Double topP = 1d; + + + /** + * 为每个提示生成的完成次数。 + */ + @Builder.Default + private Integer n = 1; + + + /** + * 是否流式输出. + * default:false + */ + @Builder.Default + private boolean stream = false; + /** + * 停止输出标识 + */ + private List stop; + /** + * 最大支持4096 + */ + @JsonProperty("max_tokens") + @Builder.Default + private Integer maxTokens = 2048; + + + @JsonProperty("presence_penalty") + @Builder.Default + private double presencePenalty = 0; + + /** + * -2.0 ~~ 2.0 + */ + @JsonProperty("frequency_penalty") + @Builder.Default + private double frequencyPenalty = 0; + + @JsonProperty("logit_bias") + private Map logitBias; + /** + * 用户唯一值,确保接口不被重复调用 + */ + private String user; + + /** + * @since 1.1.2 + */ + private Integer seed; + + + /** + * 最新模型参考官方文档: + * 官方稳定模型列表 + */ + @Getter + @AllArgsConstructor + public enum Model { + /** + * gpt-3.5-turbo + */ + GPT_3_5_TURBO("gpt-3.5-turbo"), + /** + * 临时模型,不建议使用,2023年9 月 13 日将被弃用 + */ + @Deprecated + GPT_3_5_TURBO_0301("gpt-3.5-turbo-0301"), + /** + * gpt-3.5-turbo-0613 支持函数 + */ + GPT_3_5_TURBO_1106("gpt-3.5-turbo-1106"), + + GPT_3_5_TURBO_0613("gpt-3.5-turbo-0613"), + /** + * gpt-3.5-turbo-16k 超长上下文 + */ + GPT_3_5_TURBO_16K("gpt-3.5-turbo-16k"), + /** + * gpt-3.5-turbo-16k-0613 超长上下文 支持函数 + */ + GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"), + /** + * GPT4.0 + */ + GPT_4("gpt-4"), + /** + * 临时模型,不建议使用,2023年9 月 13 日将被弃用 + */ + @Deprecated + GPT_4_0314("gpt-4-0314"), + /** + * GPT4.0 超长上下文 + */ + GPT_4_32K("gpt-4-32k"), + /** + * 临时模型,不建议使用,2023年9 月 13 日将被弃用 + */ + @Deprecated + GPT_4_32K_0314("gpt-4-32k-0314"), + + /** + * gpt-4-0613,支持函数 + */ + GPT_4_0613("gpt-4-0613"), + /** + * gpt-4-0613,支持函数 + */ + GPT_4_32K_0613("gpt-4-32k-0613"), + /** + * 支持数组模式,支持function call,支持可重复输出 + */ + GPT_4_1106_PREVIEW("gpt-4-1106-preview"), + /** + * 支持图片 + */ + GPT_4_VISION_PREVIEW("gpt-4-vision-preview"), + ; + private final String name; + } + + @Getter + @AllArgsConstructor + public enum ChatType { + /** + * 对话类型 - 输入 + */ + CHAT_IN("in"), + /** + * 对话类型 - 输出 + */ + CHAT_OUT("out"), + + ; + private final String name; + } + + public static double getModelCost(String modelName) { + return switch (modelName) { + case "gpt-3.5-turbo-0613" -> OpenAIConst.GPT3_COST; + default -> OpenAIConst.GPT4_COST; + }; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseMessage.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseMessage.java new file mode 100644 index 00000000..391b4c6a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseMessage.java @@ -0,0 +1,84 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.unfbx.chatgpt.entity.chat.tool.ToolCalls; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 1.1.2 + * 2023-03-02 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +public class BaseMessage implements Serializable { + + /** + * 目前支持四个中角色参考官网,进行情景输入: + * https://platform.openai.com/docs/guides/chat/introduction + */ + private String role; + + + private String name; + + /** + * The tool calls generated by the model, such as function calls. + * @since 1.1.2 + */ + @JsonProperty("tool_calls") + private List toolCalls; + + /** + * @since 1.1.2 + */ + @JsonProperty("tool_call_id") + private String toolCallId; + + @Deprecated + @JsonProperty("function_call") + private FunctionCall functionCall; + + + /** + * 构造函数 + * + * @param role 角色 + * @param name name + * @param functionCall functionCall + */ + public BaseMessage(String role, String name, FunctionCall functionCall) { + this.role = role; + this.name = name; + this.functionCall = functionCall; + } + + public BaseMessage() { + } + + + @Getter + @AllArgsConstructor + public enum Role { + + SYSTEM("system"), + USER("user"), + ASSISTANT("assistant"), + FUNCTION("function"), + TOOL("tool"), + ; + private final String name; + } + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatChoice.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatChoice.java new file mode 100644 index 00000000..b3a1644a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatChoice.java @@ -0,0 +1,31 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-02 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChatChoice implements Serializable { + private long index; + /** + * 请求参数stream为true返回是delta + */ + @JsonProperty("delta") + private Message delta; + /** + * 请求参数stream为false返回是message + */ + @JsonProperty("message") + private Message message; + @JsonProperty("finish_reason") + private String finishReason; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletion.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletion.java new file mode 100644 index 00000000..0e626ebe --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletion.java @@ -0,0 +1,34 @@ +package com.xmzs.common.chat.entity.chat; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import lombok.experimental.SuperBuilder; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: chat模型参数 + * + * @author https:www.unfbx.com + * 2023-03-02 + */ +@Data +@SuperBuilder +@Slf4j +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class ChatCompletion extends BaseChatCompletion implements Serializable { + + /** + * 问题描述 + */ + @NonNull + private List messages; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionResponse.java new file mode 100644 index 00000000..170e74dd --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionResponse.java @@ -0,0 +1,26 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.xmzs.common.chat.entity.common.Usage; +import lombok.Data; + + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: chat答案类 + * + * @author https:www.unfbx.com + * 2023-03-02 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChatCompletionResponse implements Serializable { + private String id; + private String object; + private long created; + private String model; + private List choices; + private Usage usage; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionWithPicture.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionWithPicture.java new file mode 100644 index 00000000..a5c68018 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionWithPicture.java @@ -0,0 +1,30 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import lombok.experimental.SuperBuilder; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: chat模型附带图片的参数 + * + * @author https:www.unfbx.com + * @since 1.1.2 + * 2023-11-10 + */ +@Data +@SuperBuilder +@Slf4j +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class ChatCompletionWithPicture extends BaseChatCompletion implements Serializable { + /** + * 问题描述 + */ + private List messages; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Content.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Content.java new file mode 100644 index 00000000..f26fcb9a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Content.java @@ -0,0 +1,43 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +/** + * 描述: + * + * @author https://www.unfbx.com + * @since 1.1.2 + * 2023-11-10 + */ +@Data +@Builder +@Slf4j +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class Content { + /** + * 输入类型:text、image_url + * + * @see Type + */ + private String type; + private String text; + @JsonProperty("image_url") + private ImageUrl imageUrl; + + /** + * 生成图片风格 + */ + @Getter + @AllArgsConstructor + public enum Type { + TEXT("text"), + IMAGE_URL("image_url"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/FunctionCall.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/FunctionCall.java new file mode 100644 index 00000000..cc19b3a5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/FunctionCall.java @@ -0,0 +1,27 @@ +package com.xmzs.common.chat.entity.chat; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 描述:函数调用返回值 + * + * @author https://www.unfbx.com + * @since 2023-06-14 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FunctionCall { + /** + * 方法名 + */ + private String name; + /** + * 方法参数 + */ + private String arguments; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Functions.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Functions.java new file mode 100644 index 00000000..95c1b3c7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Functions.java @@ -0,0 +1,46 @@ +package com.xmzs.common.chat.entity.chat; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述:方法参数实体类,实例数据如下 + *

+ *     {
+ *          "name": "get_current_weather",
+ *          "description": "Get the current weather in a given location",
+ *          "parameters": {
+ *              "type": "object",
+ *              "properties": {
+ *                  "location": {
+ *                      "type": "string",
+ *                      "description": "The city and state, e.g. San Francisco, CA"
+ *                  },
+ *                  "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ *              },
+ *              "required": ["location"]
+ *          },
+ *     }
+ * 
+ * @author https:www.unfbx.com + * @since 2023-06-14 + */ +@Data +@Builder +public class Functions implements Serializable { + /** + * 方法名称 + */ + private String name; + /** + * 方法描述 + */ + private String description; + /** + * 方法参数 + * 扩展参数可以继承Parameters自己实现,json格式的数据 + */ + private Parameters parameters; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ImageUrl.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ImageUrl.java new file mode 100644 index 00000000..c2fa6b37 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ImageUrl.java @@ -0,0 +1,28 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 描述: + * + * @author https://www.unfbx.com + * 2023-11-10 + */ +@Data +@Builder +@Slf4j +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class ImageUrl { + /** + * 图片地址,支持base64. eg: data:image/jpeg;base64,{base64_image} + * https://platform.openai.com/docs/guides/vision + */ + private String url; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Message.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Message.java new file mode 100644 index 00000000..3738ebba --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Message.java @@ -0,0 +1,117 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-02 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Message implements Serializable { + + /** + * 目前支持四个中角色参考官网,进行情景输入: + * https://platform.openai.com/docs/guides/chat/introduction + */ + private String role; + + private String content; + + private String name; + + @JsonProperty("function_call") + private FunctionCall functionCall; + + public static Builder builder() { + return new Builder(); + } + + /** + * 构造函数 + * + * @param role 角色 + * @param content 描述主题信息 + * @param name name + * @param functionCall functionCall + */ + public Message(String role, String content, String name, FunctionCall functionCall) { + this.role = role; + this.content = content; + this.name = name; + this.functionCall = functionCall; + } + + public Message() { + } + + private Message(Builder builder) { + setRole(builder.role); + setContent(builder.content); + setName(builder.name); + setFunctionCall(builder.functionCall); + } + + + @Getter + @AllArgsConstructor + public enum Role { + + SYSTEM("system"), + USER("user"), + ASSISTANT("assistant"), + FUNCTION("function"), + ; + private String name; + } + + public static final class Builder { + private String role; + private String content; + private String name; + private FunctionCall functionCall; + + public Builder() { + } + + public Builder role(Role role) { + this.role = role.getName(); + return this; + } + + public Builder role(String role) { + this.role = role; + return this; + } + + public Builder content(String content) { + this.content = content; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder functionCall(FunctionCall functionCall) { + this.functionCall = functionCall; + return this; + } + + public Message build() { + return new Message(this); + } + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/MessagePicture.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/MessagePicture.java new file mode 100644 index 00000000..9ad7d632 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/MessagePicture.java @@ -0,0 +1,114 @@ +package com.xmzs.common.chat.entity.chat; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.unfbx.chatgpt.entity.chat.tool.ToolCalls; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-02 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +public class MessagePicture extends BaseMessage implements Serializable { + /** + * Content数组支持多图片输入 + * https://platform.openai.com/docs/guides/vision + */ + private List content; + + + public static Builder builder() { + return new Builder(); + } + + /** + * 构造函数 + * + * @param role 角色 + * @param name name + * @param content content + * @param functionCall functionCall + */ + public MessagePicture(String role, String name, List content, List toolCalls, String toolCallId, FunctionCall functionCall) { + this.content = content; + super.setRole(role); + super.setName(name); + super.setToolCalls(toolCalls); + super.setToolCallId(toolCallId); + super.setFunctionCall(functionCall); + } + + public MessagePicture() { + } + + private MessagePicture(Builder builder) { + setContent(builder.content); + super.setRole(builder.role); + super.setName(builder.name); + super.setFunctionCall(builder.functionCall); + super.setToolCalls(builder.toolCalls); + super.setToolCallId(builder.toolCallId); + } + + public static final class Builder { + private String role; + private List content; + private String name; + private String toolCallId; + private List toolCalls; + private FunctionCall functionCall; + + public Builder() { + } + + public Builder role(Role role) { + this.role = role.getName(); + return this; + } + + public Builder role(String role) { + this.role = role; + return this; + } + + public Builder content(List content) { + this.content = content; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder functionCall(FunctionCall functionCall) { + this.functionCall = functionCall; + return this; + } + + public Builder toolCalls(List toolCalls) { + this.toolCalls = toolCalls; + return this; + } + + public Builder toolCallId(String toolCallId) { + this.toolCallId = toolCallId; + return this; + } + + public MessagePicture build() { + return new MessagePicture(this); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Parameters.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Parameters.java new file mode 100644 index 00000000..b4d5ad8a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Parameters.java @@ -0,0 +1,42 @@ +package com.xmzs.common.chat.entity.chat; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +/** + * 描述:方法参数类,扩展参数可以继承Parameters自己实现 + * 参考: + *
+ * {
+ *     "type": "object",
+ *     "properties": {
+ *         "location": {
+ *             "type": "string",
+ *             "description": "The city and state, e.g. San Francisco, CA"
+ *         },
+ *         "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ *     },
+ *     "required": ["location"]
+ * }
+ * 
+ * @author https:www.unfbx.com + * @since 2023-06-14 + */ +@Data +@Builder +public class Parameters implements Serializable { + /** + * 参数类型 + */ + private String type; + /** + * 参数属性、描述 + */ + private Object properties; + /** + * 方法必输字段 + */ + private List required; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ResponseFormat.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ResponseFormat.java new file mode 100644 index 00000000..06da355c --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ResponseFormat.java @@ -0,0 +1,28 @@ +package com.xmzs.common.chat.entity.chat; + +import lombok.*; + +/** + * 指定模型必须输出的格式的对象。 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ResponseFormat { + /** + * 默认:text + * + * @see Type + */ + private String type; + + @Getter + @AllArgsConstructor + public enum Type { + JSON_OBJECT("json_object"), + TEXT("text"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCallFunction.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCallFunction.java new file mode 100644 index 00000000..293ac314 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCallFunction.java @@ -0,0 +1,31 @@ +package com.xmzs.common.chat.entity.chat.tool; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * ToolCall 的 Function参数 + * The function that the model called. + * + * @author https:www.unfbx.com + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolCallFunction implements Serializable { + /** + * 方法名 + */ + private String name; + /** + * 方法参数 + */ + private String arguments; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCalls.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCalls.java new file mode 100644 index 00000000..9b304b57 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCalls.java @@ -0,0 +1,38 @@ +package com.unfbx.chatgpt.entity.chat.tool; + +import com.xmzs.common.chat.entity.chat.tool.ToolCallFunction; +import lombok.*; + +import java.io.Serializable; + +/** + * The tool calls generated by the model, such as function calls. + * + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolCalls implements Serializable { + /** + * The ID of the tool call. + */ + private String id; + /** + * The type of the tool. Currently, only function is supported. + */ + private String type; + + private ToolCallFunction function; + + @Getter + @AllArgsConstructor + public enum Type { + FUNCTION("function"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoice.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoice.java new file mode 100644 index 00000000..d99b6577 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoice.java @@ -0,0 +1,25 @@ +package com.xmzs.common.chat.entity.chat.tool; + +import lombok.*; + +import java.io.Serializable; + +/** + * choice和object同时存在是以object为准 + * + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +public class ToolChoice implements Serializable { + + @Getter + @AllArgsConstructor + public enum Choice { + NONE("none"), + AUTO("auto"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObj.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObj.java new file mode 100644 index 00000000..04dbfd55 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObj.java @@ -0,0 +1,33 @@ +package com.xmzs.common.chat.entity.chat.tool; + +import lombok.*; + +/** + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolChoiceObj { + /** + * 需要调用的方法名称 + */ + private ToolChoiceObjFunction function; + /** + * 工具的类型。目前仅支持函数。 + * + * @see Type + */ + private String type; + + @Getter + @AllArgsConstructor + public enum Type { + FUNCTION("function"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObjFunction.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObjFunction.java new file mode 100644 index 00000000..b45ab9ab --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObjFunction.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.entity.chat.tool; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolChoiceObjFunction { + + private String name; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/Tools.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/Tools.java new file mode 100644 index 00000000..43f5be92 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/Tools.java @@ -0,0 +1,35 @@ +package com.xmzs.common.chat.entity.chat.tool; + + +import lombok.*; + +import java.io.Serializable; + +/** + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Tools implements Serializable { + + /** + * 目前只支持:function + * + * @see Type + */ + private String type; + + private ToolsFunction function; + + @Getter + @AllArgsConstructor + public enum Type { + FUNCTION("function"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolsFunction.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolsFunction.java new file mode 100644 index 00000000..7465eb5e --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolsFunction.java @@ -0,0 +1,36 @@ +package com.xmzs.common.chat.entity.chat.tool; + + +import com.xmzs.common.chat.entity.chat.Parameters; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author unfbx + * @since 1.1.2 + * 2023-11-09 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolsFunction implements Serializable { + + /** + * 要调用的函数的名称。必须是 a-z、A-Z、0-9,或包含下划线和破折号,最大长度为 64 + */ + private String name; + /** + * 对函数功能的描述,模型使用它来选择何时以及如何调用该函数。 + */ + private String description; + /** + * 函数接受的参数,描述为 JSON Schema 对象 + * 扩展参数可以继承Parameters自己实现,json格式的数据 + */ + private Parameters parameters; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Choice.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Choice.java new file mode 100644 index 00000000..63360b87 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Choice.java @@ -0,0 +1,23 @@ +package com.xmzs.common.chat.entity.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Choice implements Serializable { + private String text; + private long index; + private Object logprobs; + @JsonProperty("finish_reason") + private String finishReason; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/DeleteResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/DeleteResponse.java new file mode 100644 index 00000000..4d35e0d2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/DeleteResponse.java @@ -0,0 +1,20 @@ +package com.xmzs.common.chat.entity.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DeleteResponse implements Serializable { + private String id; + private String object; + private boolean deleted; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/OpenAiResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/OpenAiResponse.java new file mode 100644 index 00000000..36b8b6e2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/OpenAiResponse.java @@ -0,0 +1,30 @@ +package com.xmzs.common.chat.entity.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpenAiResponse implements Serializable { + private String object; + private List data; + private Error error; + + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public class Error { + private String message; + private String type; + private String param; + private String code; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Usage.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Usage.java new file mode 100644 index 00000000..9802aa76 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Usage.java @@ -0,0 +1,24 @@ +package com.xmzs.common.chat.entity.common; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Usage implements Serializable { + @JsonProperty("prompt_tokens") + private long promptTokens; + @JsonProperty("completion_tokens") + private long completionTokens; + @JsonProperty("total_tokens") + private long totalTokens; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/Completion.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/Completion.java new file mode 100644 index 00000000..f3ecc121 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/Completion.java @@ -0,0 +1,126 @@ +package com.xmzs.common.chat.entity.completions; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 描述: 问题类 + * + * @author https:www.unfbx.com + * 2023-02-11 + */ +@Data +@Builder +@Slf4j +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class Completion implements Serializable { + + @NonNull + @Builder.Default + private String model = Model.DAVINCI_003.getName(); + /** + * 问题描述 + */ + @NonNull + private String prompt; + /** + * 完成输出后的后缀,用于格式化输出结果 + */ + private String suffix; + + /** + * 最大支持4096 + */ + @JsonProperty("max_tokens") + @Builder.Default + private Integer maxTokens = 2048; + /** + * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。 + *

+ * We generally recommend altering this or but not both.top_p + */ + @Builder.Default + private double temperature = 0; + + /** + * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。 + *

+ * 我们通常建议更改此设置,但不要同时更改两者。temperature + */ + @JsonProperty("top_p") + @Builder.Default + private Double topP = 1d; + + /** + * 为每个提示生成的完成次数。 + */ + @Builder.Default + private Integer n = 1; + + @Builder.Default + private boolean stream = false; + /** + * 最大值:5 + */ + private Integer logprobs; + + @Builder.Default + private boolean echo = false; + + private List stop; + + @JsonProperty("presence_penalty") + @Builder.Default + private double presencePenalty = 0; + + /** + * -2.0 ~~ 2.0 + */ + @JsonProperty("frequency_penalty") + @Builder.Default + private double frequencyPenalty = 0; + + @JsonProperty("best_of") + @Builder.Default + private Integer bestOf = 1; + + @JsonProperty("logit_bias") + private Map logitBias; + /** + * 用户唯一值,确保接口不被重复调用 + */ + private String user; + + /** + * 获取当前参数的tokens数 + * @return token数量 + */ +// public long tokens() { +// if (StrUtil.isBlank(this.prompt) || StrUtil.isBlank(this.model)) { +// log.warn("参数异常model:{},prompt:{}", this.model, this.prompt); +// return 0; +// } +// return TikTokensUtil.tokens(this.model, this.prompt); +// } + + @Getter + @AllArgsConstructor + public enum Model { + DAVINCI_003("text-davinci-003"), + DAVINCI_002("text-davinci-002"), + DAVINCI("davinci"), + ; + private String name; + } +} + + diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/CompletionResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/CompletionResponse.java new file mode 100644 index 00000000..891aa91d --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/CompletionResponse.java @@ -0,0 +1,27 @@ +package com.xmzs.common.chat.entity.completions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.xmzs.common.chat.entity.common.Choice; +import com.xmzs.common.chat.entity.common.OpenAiResponse; +import com.xmzs.common.chat.entity.common.Usage; +import lombok.Data; + + +import java.io.Serializable; + +/** + * 描述: 答案类 + * + * @author https:www.unfbx.com + * 2023-02-11 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CompletionResponse extends OpenAiResponse implements Serializable { + private String id; + private String object; + private long created; + private String model; + private Choice[] choices; + private Usage usage; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/dto/WebSocketMessageDto.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/dto/WebSocketMessageDto.java new file mode 100644 index 00000000..7a73aaef --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/dto/WebSocketMessageDto.java @@ -0,0 +1,29 @@ +package com.xmzs.common.chat.entity.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 消息的dto + * + * @author zendwang + */ +@Data +public class WebSocketMessageDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 需要推送到的session key 列表 + */ + private List sessionKeys; + + /** + * 需要发送的消息 + */ + private String message; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/Edit.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/Edit.java new file mode 100644 index 00000000..7e803362 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/Edit.java @@ -0,0 +1,104 @@ +package com.xmzs.common.chat.entity.edits; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Builder +@Slf4j +@NoArgsConstructor +@AllArgsConstructor +public class Edit implements Serializable { + /** + * 编辑模型,目前支持两种 + */ + @NonNull + private String model; + + @NonNull + private String input; + /** + * 提示说明。告知模型如何修改。 + */ + @NonNull + private String instruction; + + + /** + * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。 + * + * We generally recommend altering this or but not both.top_p + */ + @Builder.Default + private double temperature = 0; + + /** + * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。 + * + * 我们通常建议更改此设置,但不要同时更改两者。temperature + */ + @JsonProperty("top_p") + @Builder.Default + private Double topP = 1d; + + /** + * 为每个提示生成的完成次数。 + */ + @Builder.Default + private Integer n = 1; + + public void setModel(Model model) { + this.model = model.getName(); + } + + public void setTemperature(double temperature) { + if (temperature > 2 || temperature < 0) { + log.error("temperature参数异常,temperature属于[0,2]"); + this.temperature = 2; + return; + } + if (temperature < 0) { + log.error("temperature参数异常,temperature属于[0,2]"); + this.temperature = 0; + return; + } + this.temperature = temperature; + } + + + public void setTopP(Double topP) { + this.topP = topP; + } + + public void setN(Integer n) { + this.n = n; + } + + public void setInput(String input) { + this.input = input; + } + + public void setInstruction(String instruction) { + this.instruction = instruction; + } + @Getter + @AllArgsConstructor + public enum Model { + TEXT_DAVINCI_EDIT_001("text-davinci-edit-001"), + CODE_DAVINCI_EDIT_001("code-davinci-edit-001"), + ; + private String name; + } +} + + + diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/EditResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/EditResponse.java new file mode 100644 index 00000000..8f5c60b8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/EditResponse.java @@ -0,0 +1,27 @@ +package com.xmzs.common.chat.entity.edits; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.xmzs.common.chat.entity.common.Choice; +import com.xmzs.common.chat.entity.common.Usage; +import lombok.Data; + + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class EditResponse implements Serializable { + private String id; + private String object; + private long created; + private String model; + private Choice[] choices; + private Usage usage; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Embedding.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Embedding.java new file mode 100644 index 00000000..79965902 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Embedding.java @@ -0,0 +1,54 @@ +package com.xmzs.common.chat.entity.embeddings; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Slf4j +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class Embedding implements Serializable { + @NonNull + @Builder.Default + private String model = Model.TEXT_EMBEDDING_ADA_002.getName(); + /** + * 必选项:长度不能超过:8192 + */ + @NonNull + private List input; + + private String user; + + public void setModel(Model model) { + if (Objects.isNull(model)) { + model = Model.TEXT_EMBEDDING_ADA_002; + } + this.model = model.getName(); + } + + + public void setUser(String user) { + this.user = user; + } + + @Getter + @AllArgsConstructor + public enum Model { + TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"), + ; + private String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/EmbeddingResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/EmbeddingResponse.java new file mode 100644 index 00000000..57bb1140 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/EmbeddingResponse.java @@ -0,0 +1,25 @@ +package com.xmzs.common.chat.entity.embeddings; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.xmzs.common.chat.entity.common.Usage; +import lombok.Data; + + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class EmbeddingResponse implements Serializable { + + private String object; + private List data; + private String model; + private Usage usage; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Item.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Item.java new file mode 100644 index 00000000..2ceb1c89 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Item.java @@ -0,0 +1,16 @@ +package com.xmzs.common.chat.entity.embeddings; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Item implements Serializable { + private String object; + private List embedding; + private Integer index; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/engines/Engine.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/engines/Engine.java new file mode 100644 index 00000000..1f8e552d --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/engines/Engine.java @@ -0,0 +1,25 @@ +package com.xmzs.common.chat.entity.engines; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Engine implements Serializable { + + private String id; + private String object; + private String owner; + private boolean ready; + private Object permissions; + private long created; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/File.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/File.java new file mode 100644 index 00000000..581cd297 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/File.java @@ -0,0 +1,28 @@ +package com.xmzs.common.chat.entity.files; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class File implements Serializable { + + private String id; + private String object; + private long bytes; + private long created_at; + private String filename; + private String purpose; + private String status; + @JsonProperty("status_details") + private String statusDetails; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/UploadFileResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/UploadFileResponse.java new file mode 100644 index 00000000..00276ade --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/files/UploadFileResponse.java @@ -0,0 +1,17 @@ +package com.xmzs.common.chat.entity.files; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class UploadFileResponse extends File implements Serializable { +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/Event.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/Event.java new file mode 100644 index 00000000..af4a0d50 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/Event.java @@ -0,0 +1,17 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Event implements Serializable { + private String object; + @JsonProperty("created_at") + private long createdAt; + private String level; + private String message; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTune.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTune.java new file mode 100644 index 00000000..29d4e5a5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTune.java @@ -0,0 +1,123 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.xmzs.common.chat.openai.exception.CommonError; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.exception.base.BaseException; + + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +@Getter +@Slf4j +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class FineTune implements Serializable { + + /** + * 上传的文件ID + */ + @NonNull + @JsonProperty("training_file") + private String trainingFile; + + @JsonProperty("validation_file") + private String validationFile; + /** + * 参考 + * @see Model + */ + private String model; + + @JsonProperty("n_epochs") + @Builder.Default + private Integer n_epochs = 4; + + @JsonProperty("batch_size") + private Integer batchSize; + + @JsonProperty("learning_rate_multiplier") + private Double learningRateMultiplier; + + @JsonProperty("prompt_loss_weight") + @Builder.Default + private Double promptLossWeight = 0.01; + + @JsonProperty("compute_classification_metrics") + @Builder.Default + private boolean computeClassificationMetrics = false; + + @JsonProperty("classification_n_classes") + private Integer classificationNClasses; + + @JsonProperty("classification_betas") + private List classificationBetas; + + private String suffix; + + public void setTrainingFile(String trainingFile) { + this.trainingFile = trainingFile; + } + + public void setValidationFile(String validationFile) { + this.validationFile = validationFile; + } + + public void setModel(String model) { + this.model = model; + } + + public void setN_epochs(Integer n_epochs) { + this.n_epochs = n_epochs; + } + + public void setBatchSize(Integer batchSize) { + this.batchSize = batchSize; + } + + public void setLearningRateMultiplier(Double learningRateMultiplier) { + this.learningRateMultiplier = learningRateMultiplier; + } + + public void setPromptLossWeight(Double promptLossWeight) { + this.promptLossWeight = promptLossWeight; + } + + public void setComputeClassificationMetrics(boolean computeClassificationMetrics) { + this.computeClassificationMetrics = computeClassificationMetrics; + } + + public void setClassificationNClasses(Integer classificationNClasses) { + this.classificationNClasses = classificationNClasses; + } + + public void setClassificationBetas(List classificationBetas) { + this.classificationBetas = classificationBetas; + } + + public void setSuffix(String suffix) { + if(Objects.nonNull(suffix) && !"".equals(suffix) && suffix.length() > 40){ + log.error("后缀长度不能大于40"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + this.suffix = suffix; + } + + @Getter + @AllArgsConstructor + public enum Model { + // or a fine-tuned model created after 2022-04-21. + ADA("ada"), + BABBAGE("babbage"), + CURIE("curie"), + DAVINCI("davinci"), + ; + private String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneDeleteResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneDeleteResponse.java new file mode 100644 index 00000000..ddb787ac --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneDeleteResponse.java @@ -0,0 +1,18 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class FineTuneDeleteResponse implements Serializable { + + private String id; + + private String object; + + private boolean deleted; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneResponse.java new file mode 100644 index 00000000..2753fcaa --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/FineTuneResponse.java @@ -0,0 +1,49 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class FineTuneResponse implements Serializable { + + private String id; + + private String object; + + private String model; + + @JsonProperty("created_at") + private long createdAt; + + private List events; + + @JsonProperty("fine_tuned_model") + private String fineTunedModel; + + @JsonProperty("hyperparams") + private HyperParam hyperParams; + + @JsonProperty("organization_id") + private String organizationId; + + @JsonProperty("result_files") + private List resultFiles; + + private String status; + + @JsonProperty("validation_files") + private List validationFiles; + + @JsonProperty("training_files") + private List trainingFiles; + + @JsonProperty("updated_at") + private long updatedAt; + + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/HyperParam.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/HyperParam.java new file mode 100644 index 00000000..1aca22d8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/HyperParam.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class HyperParam implements Serializable { + + @JsonProperty("batch_size") + private Integer batchSize; + @JsonProperty("learning_rate_multiplier") + private Double learningRateMultiplier; + @JsonProperty("n_epochs") + private Integer nEpochs; + @JsonProperty("prompt_loss_weight") + private Double promptLossWeight; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/TrainingFile.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/TrainingFile.java new file mode 100644 index 00000000..e0736b80 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/fineTune/TrainingFile.java @@ -0,0 +1,23 @@ +package com.xmzs.common.chat.entity.fineTune; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class TrainingFile implements Serializable { + + private String id; + private String object; + private long bytes; + @JsonProperty("created_at") + private long createdAt; + private String filename; + private String purpose; + private String status; + @JsonProperty("status_details") + private String statusDetails; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Image.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Image.java new file mode 100644 index 00000000..76b75d56 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Image.java @@ -0,0 +1,108 @@ +package com.xmzs.common.chat.entity.images; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Slf4j +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class Image implements Serializable { + + /** + * 提示词:dall-e-2支持1000字符、dall-e-3支持4000字符 + */ + private String prompt; + /** + * 支持dall-e-2、dall-e-3 + * + * @see Model + */ + @Builder.Default + private String model = Model.DALL_E_3.getName(); + + /** + * 此参数仅仅dall-e-3,默认值:standard + * + * @see Quality + */ + private String quality; + + /** + * 为每个提示生成的个数,dall-e-3只能为1。 + */ + private Integer n; + /** + * 图片尺寸,默认值:1024x1024 + * dall-e-2支持:256x256, 512x512, or 1024x1024 + * dall-e-3支持:1024x1024, 1792x1024, or 1024x1792 + * + * @see SizeEnum + */ + private String size; + /** + * 此参数仅仅dall-e-3,取值范围:vivid、natural + * 默认值:vivid + * + * @see Style + */ + private String style; + + /** + * 生成图片格式:url、b64_json + * + * @see ResponseFormat + */ + @JsonProperty("response_format") + private String responseFormat; + + private String user; + + /** + * 图片生成模型 + */ + @Getter + @AllArgsConstructor + public enum Model { + DALL_E_2("dall-e-2"), + DALL_E_3("dall-e-3"), + ; + private final String name; + } + + /** + * 生成图片质量 + */ + @Getter + @AllArgsConstructor + public enum Quality { + STANDARD("standard"), + HD("hd"), + ; + private final String name; + } + + /** + * 生成图片风格 + */ + @Getter + @AllArgsConstructor + public enum Style { + VIVID("vivid"), + NATURAL("natural"), + ; + private final String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageEdit.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageEdit.java new file mode 100644 index 00000000..d1e87681 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageEdit.java @@ -0,0 +1,99 @@ +package com.xmzs.common.chat.entity.images; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.xmzs.common.chat.openai.exception.CommonError; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.exception.base.BaseException; + + +import java.io.Serializable; +import java.util.Objects; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Slf4j +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class ImageEdit implements Serializable { + /** + * 必选项:描述文字,最多1000字符 + */ + @NonNull + private String prompt; + /** + * 为每个提示生成的完成次数。 + */ + @Builder.Default + private Integer n = 1; + /** + * 256x256 + * 512x512 + * 1024x1024 + */ + @Builder.Default + private String size = SizeEnum.size_512.getName(); + + @JsonProperty("response_format") + @Builder.Default + private String responseFormat = ResponseFormat.URL.getName(); + + private String user; + + public ImageEdit setN(Integer n) { + if(n < 1){ + log.warn("n最小值1"); + n = 1; + } + if(n > 10){ + log.warn("n最大值10"); + n = 10; + } + this.n = n; + return this; + } + + public ImageEdit setPrompt(String prompt) { + if(Objects.isNull(prompt) || "".equals(prompt)){ + log.error("参数异常"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + if(prompt.length() > 1000){ + log.error("长度超过1000"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + this.prompt = prompt; + return this; + } + + public ImageEdit setSize(SizeEnum size) { + if(Objects.isNull(size)){ + size = SizeEnum.size_512; + } + this.size = size.getName(); + return this; + } + + public ImageEdit setResponseFormat(ResponseFormat responseFormat) { + if(Objects.isNull(responseFormat)){ + responseFormat = ResponseFormat.URL; + } + this.responseFormat = responseFormat.getName(); + return this; + } + + public ImageEdit setUser(String user) { + this.user = user; + return this; + } + + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageResponse.java new file mode 100644 index 00000000..1d2b5a08 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageResponse.java @@ -0,0 +1,20 @@ +package com.xmzs.common.chat.entity.images; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ImageResponse implements Serializable { + private long created; + private List data; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageVariations.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageVariations.java new file mode 100644 index 00000000..58018db7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ImageVariations.java @@ -0,0 +1,81 @@ +package com.xmzs.common.chat.entity.images; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Slf4j +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +public class ImageVariations implements Serializable { + /** + * 为每个提示生成的完成次数。 + */ + @Builder.Default + private Integer n = 1; + /** + * 256x256 + * 512x512 + * 1024x1024 + */ + @Builder.Default + private String size = SizeEnum.size_512.getName(); + + @JsonProperty("response_format") + @Builder.Default + private String responseFormat = ResponseFormat.URL.getName(); + + private String user; + + + public void setN(Integer n) { + if (n < 1) { + log.warn("n最小值1"); + this.n = 1; + return; + } + if (n > 10) { + log.warn("n最大值10"); + this.n = 10; + return; + } + this.n = n; + } + + + public void setSize(SizeEnum size) { + if (Objects.isNull(size)) { + size = SizeEnum.size_512; + } + this.size = size.getName(); + } + + public void setResponseFormat(ResponseFormat responseFormat) { + if (Objects.isNull(responseFormat)) { + responseFormat = ResponseFormat.URL; + } + this.responseFormat = responseFormat.getName(); + } + + public void setUser(String user) { + this.user = user; + } + + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Item.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Item.java new file mode 100644 index 00000000..c22afa44 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/Item.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.entity.images; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Item implements Serializable { + private String url; + @JsonProperty("b64_json") + private String b64Json; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ResponseFormat.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ResponseFormat.java new file mode 100644 index 00000000..b75c6912 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/ResponseFormat.java @@ -0,0 +1,22 @@ +package com.xmzs.common.chat.entity.images; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@AllArgsConstructor +@Getter +public enum ResponseFormat implements Serializable { + URL("url"), + B64_JSON("b64_json"), + ; + + private String name; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/SizeEnum.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/SizeEnum.java new file mode 100644 index 00000000..a72ef384 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/images/SizeEnum.java @@ -0,0 +1,26 @@ +package com.xmzs.common.chat.entity.images; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@AllArgsConstructor +public enum SizeEnum implements Serializable { + size_1024_1792("1024x1792"), + size_1792_1024("1792x1024"), + size_1024("1024x1024"), + size_512("512x512"), + size_256("256x256"), + + ; + private String name; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Model.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Model.java new file mode 100644 index 00000000..0b45b6fc --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Model.java @@ -0,0 +1,29 @@ +package com.xmzs.common.chat.entity.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Model implements Serializable { + + private String id; + private String object; + private long created; + @JsonProperty("owned_by") + private String ownedBy; + @JsonProperty("permission") + private List permission; + private String root; + private Object parent; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/ModelResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/ModelResponse.java new file mode 100644 index 00000000..33aee8d8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/ModelResponse.java @@ -0,0 +1,20 @@ +package com.xmzs.common.chat.entity.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ModelResponse implements Serializable { + private String object; + private List data; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Permission.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Permission.java new file mode 100644 index 00000000..ffdbd526 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/models/Permission.java @@ -0,0 +1,45 @@ +package com.xmzs.common.chat.entity.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Permission implements Serializable { + + private String id; + @JsonProperty("object") + private String object; + @JsonProperty("created") + private long created; + @JsonProperty("allow_create_engine") + private boolean allowCreateEngine; + @JsonProperty("allow_sampling") + private boolean allowSampling; + @JsonProperty("allow_logprobs") + private boolean allowLogprobs; + @JsonProperty("allow_search_indices") + private boolean allowSearchIndices; + @JsonProperty("allow_view") + private boolean allowView; + @JsonProperty("allow_fine_tuning") + private boolean allowFineTuning; + @JsonProperty("organization") + private String organization; + /** + * 不知道是什么类型的数据 + */ + @JsonProperty("group") + private Object group; + @JsonProperty("is_blocking") + private boolean isBlocking; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Categories.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Categories.java new file mode 100644 index 00000000..140195d9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Categories.java @@ -0,0 +1,50 @@ +package com.xmzs.common.chat.entity.moderations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Categories implements Serializable { + /** + * 表达、煽动或宣扬基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓的仇恨的内容。 + */ + private boolean hate; + /** + * 仇恨内容,还包括对目标群体的暴力或严重伤害。 + */ + @JsonProperty("hate/threatening") + private boolean hateThreatening; + /** + * 宣扬、鼓励或描绘自残行为(例如自杀、割伤和饮食失调)的内容。 + */ + @JsonProperty("self-harm") + private boolean selfHarm; + /** + * 旨在引起性兴奋的内容,例如对性活动的描述,或宣传性服务(不包括性教育和健康)的内容。 + */ + private boolean sexual; + /** + * 包含未满 18 周岁的个人的色情内容。 + */ + @JsonProperty("sexual/minors") + private boolean sexualMinors; + /** + * 宣扬或美化暴力或歌颂他人遭受苦难或羞辱的内容。 + */ + private boolean violence; + /** + * 以极端血腥细节描绘死亡、暴力或严重身体伤害的暴力内容。 + */ + @JsonProperty("violence/graphic") + private boolean violenceGraphic; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/CategoryScores.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/CategoryScores.java new file mode 100644 index 00000000..9efcdd2d --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/CategoryScores.java @@ -0,0 +1,31 @@ +package com.xmzs.common.chat.entity.moderations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CategoryScores implements Serializable { + private BigDecimal hate; + @JsonProperty("hate/threatening") + private BigDecimal hateThreatening; + @JsonProperty("self-harm") + private BigDecimal selfHarm; + private BigDecimal sexual; + @JsonProperty("sexual/minors") + private BigDecimal sexualMinors; + private BigDecimal violence; + @JsonProperty("violence/graphic") + private BigDecimal violenceGraphic; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Moderation.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Moderation.java new file mode 100644 index 00000000..193659a8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Moderation.java @@ -0,0 +1,55 @@ +package com.xmzs.common.chat.entity.moderations; + +import com.xmzs.common.chat.openai.exception.CommonError; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.exception.base.BaseException; + + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * 描述:文本审核,敏感词鉴别 + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Getter +@Builder +@Slf4j +@NoArgsConstructor +@AllArgsConstructor +public class Moderation implements Serializable { + + @NonNull + private List input; + @Builder.Default + private String model = Model.TEXT_MODERATION_LATEST.getName(); + + public void setInput(List input) { + if (Objects.isNull(input) || input.size() == 0) { + log.error("input不能为空"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + this.input = input; + } + + public void setModel(Model model) { + if (Objects.isNull(model)) { + model = Model.TEXT_MODERATION_LATEST; + } + this.model = model.getName(); + } + + @Getter + @AllArgsConstructor + public enum Model { + TEXT_MODERATION_STABLE("text-moderation-stable"), + TEXT_MODERATION_LATEST("text-moderation-latest"), + ; + + private String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/ModerationResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/ModerationResponse.java new file mode 100644 index 00000000..91e00a94 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/ModerationResponse.java @@ -0,0 +1,21 @@ +package com.xmzs.common.chat.entity.moderations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ModerationResponse implements Serializable { + private String id; + private String model; + private List results; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Result.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Result.java new file mode 100644 index 00000000..12ab9ac7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/moderations/Result.java @@ -0,0 +1,22 @@ +package com.xmzs.common.chat.entity.moderations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Result implements Serializable { + private Categories categories; + @JsonProperty("category_scores") + private CategoryScores categoryScores; + private boolean flagged; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Transcriptions.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Transcriptions.java new file mode 100644 index 00000000..58032619 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Transcriptions.java @@ -0,0 +1,49 @@ +package com.xmzs.common.chat.entity.whisper; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldNameConstants; + +/** + * @author Admin + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldNameConstants +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Transcriptions extends Whisper { + /** + * 模型目前只支持这一种:WHISPER_1 + */ + @Builder.Default + private String model = Whisper.Model.WHISPER_1.getName(); + /** + * 提示语,需要与语音语言匹配 + */ + private String prompt; + /** + * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。 + * 默认值:json + */ + @JsonProperty("response_format") + @Builder.Default + private String responseFormat = ResponseFormat.JSON.getName(); + /** + * 温度控制随机效果:0-1,值越大输出更加随机 + * 默认值:0 + */ + @Builder.Default + private Double temperature = 0d; + /** + * 输入音频的语言,以 ISO-639-1 格式提供输入语言将提高准确性和延迟。 + * 参考:ISO-639-1 + */ + private String language; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Translations.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Translations.java new file mode 100644 index 00000000..fe68f9b6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Translations.java @@ -0,0 +1,41 @@ +package com.xmzs.common.chat.entity.whisper; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldNameConstants; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldNameConstants +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Translations { + /** + * 模型目前只支持这一种:WHISPER_1 + */ + @Builder.Default + private String model = Whisper.Model.WHISPER_1.getName(); + /** + * 提示语,需要与语音语言匹配 + */ + private String prompt; + /** + * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。 + * 默认值:json + */ + @JsonProperty("response_format") + @Builder.Default + private String responseFormat = Whisper.ResponseFormat.JSON.getName(); + /** + * 温度控制随机效果:0-1,值越大输出更加随机 + * 默认值:0 + */ + @Builder.Default + private double temperature = 0; + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Whisper.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Whisper.java new file mode 100644 index 00000000..4e4f681b --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/Whisper.java @@ -0,0 +1,38 @@ +package com.xmzs.common.chat.entity.whisper; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + +import java.io.Serializable; + +/** + * 描述:语音转文字 + * + * @author https:www.unfbx.com + * @since 2023-03-02 + */ +@Data +public class Whisper implements Serializable { + + + @Getter + @AllArgsConstructor + public enum Model { + WHISPER_1("whisper-1"), + ; + private String name; + } + + @Getter + @AllArgsConstructor + public enum ResponseFormat { + JSON("json"), + TEXT("text"), + SRT("srt"), + VERBOSE_JSON("verbose_json"), + VTT("vtt"), + ; + private String name; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/WhisperResponse.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/WhisperResponse.java new file mode 100644 index 00000000..183207f1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/whisper/WhisperResponse.java @@ -0,0 +1,19 @@ +package com.xmzs.common.chat.entity.whisper; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.io.Serializable; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @since 2023-03-02 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class WhisperResponse implements Serializable { + + private String text; +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/handler/PlusWebSocketHandler.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/handler/PlusWebSocketHandler.java new file mode 100644 index 00000000..96c17de0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/handler/PlusWebSocketHandler.java @@ -0,0 +1,162 @@ +package com.xmzs.common.chat.handler; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSONObject; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.chat.config.LocalCache; +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import com.xmzs.common.chat.holder.WebSocketSessionHolder; +import com.xmzs.common.chat.listener.WebSocketEventListener; +import com.xmzs.common.chat.openai.OpenAiStreamClient; +import com.xmzs.common.chat.entity.chat.Message; +import com.xmzs.common.chat.utils.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.socket.*; +import org.springframework.web.socket.handler.AbstractWebSocketHandler; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * WebSocketHandler 实现类 + * + * @author zendwang + */ +@Slf4j +public class PlusWebSocketHandler extends AbstractWebSocketHandler { + + /** + * 是否开启文本审核 + */ + @Value("${baidu.enabled}") + private Boolean enabled; + + /** + * 连接成功后 + */ + @Override + public void afterConnectionEstablished(WebSocketSession session) { + WebSocketSessionHolder.addSession(session.getId(), session); + } + + /** + * 处理发送来的文本消息 + * + * @param session + * @param message + */ + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) { +// if(enabled){ +// // 判断文本是否合规 +// TextReviewService textReviewService=(TextReviewService) SpringUtils.context().getBean("textReviewService"); +// String type = textReviewService.textReview(message.getPayload()); +// // 审核状态 1 代表合法 +// String conclusionType = "1"; +// if (!conclusionType.equals(type) && StringUtils.isNotEmpty(type)) { +// HashMap msgMap = new HashMap<>(10); +// msgMap.put("content", "文本不合规,请修改!"); +// String jsonStr = JSONUtil.toJsonStr(msgMap); +// WebSocketUtils.sendMessage(session, jsonStr); +// WebSocketUtils.sendMessage(session, "[DONE]"); +// return; +// } +// } + WebSocketEventListener eventSourceListener = new WebSocketEventListener(session); + String messageContext = (String) LocalCache.CACHE.get(session.getId()); + List messages = new ArrayList<>(); + if (StrUtil.isNotBlank(messageContext)) { + messages = JSONUtil.toList(messageContext, Message.class); + // 上下文长度 + int contextSize=10; + if (messages.size() >= contextSize) { + messages = messages.subList(1, contextSize); + } + Message currentMessage = Message.builder().content(message.getPayload()).role(Message.Role.USER).build(); + messages.add(currentMessage); + } else { + Message currentMessage = Message.builder().content(message.getPayload()).role(Message.Role.USER).build(); + messages.add(currentMessage); + } + ChatCompletion chatCompletion = ChatCompletion + .builder() + .model(ChatCompletion.Model.GPT_3_5_TURBO.getName()) + .messages(messages) + .temperature(0.2) + .stream(true) + .build(); + OpenAiStreamClient openAiStreamClient=(OpenAiStreamClient) SpringUtils.context().getBean("openAiStreamClient"); + openAiStreamClient.streamChatCompletion(chatCompletion, eventSourceListener); + LocalCache.CACHE.put(session.getId(), JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT); + } + + /** + * 根据key获取Value值 + * + * @Date 2023/7/27 + * @param jsonObject + * @param key + * @param defaultValue + * @return String + **/ + public String getValue(JSONObject jsonObject,String key,String defaultValue){ + String value = (String)jsonObject.get(key); + if(StrUtil.isEmpty(value)){ + return defaultValue; + } + return value; + } + + @Override + protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { + super.handleBinaryMessage(session, message); + } + + /** + * 心跳监测的回复 + * + * @param session + * @param message + * @throws Exception + */ + @Override + protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { + WebSocketUtils.sendPongMessage(session); + } + + /** + * 连接出错时 + * + * @param session + * @param exception + * @throws Exception + */ + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { + log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage()); + } + + /** + * 连接关闭后 + * + * @param session + * @param status + */ + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + WebSocketSessionHolder.removeSession(session.getId()); + } + + /** + * 是否支持分片消息 + * + * @return + */ + @Override + public boolean supportsPartialMessages() { + return false; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/holder/WebSocketSessionHolder.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/holder/WebSocketSessionHolder.java new file mode 100644 index 00000000..d04a0bb7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/holder/WebSocketSessionHolder.java @@ -0,0 +1,37 @@ +package com.xmzs.common.chat.holder; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.web.socket.WebSocketSession; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * WebSocketSession 用于保存当前所有在线的会话信息 + * + * @author zendwang + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class WebSocketSessionHolder { + + private static final Map USER_SESSION_MAP = new ConcurrentHashMap<>(); + + public static void addSession(String sessionKey, WebSocketSession session) { + USER_SESSION_MAP.put(sessionKey, session); + } + + public static void removeSession(String sessionKey) { + if (USER_SESSION_MAP.containsKey(sessionKey)) { + USER_SESSION_MAP.remove(sessionKey); + } + } + + public static WebSocketSession getSessions(Long sessionKey) { + return USER_SESSION_MAP.get(sessionKey); + } + + public static Boolean existSession(Long sessionKey) { + return USER_SESSION_MAP.containsKey(sessionKey); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/interceptor/PlusWebSocketInterceptor.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/interceptor/PlusWebSocketInterceptor.java new file mode 100644 index 00000000..49f24672 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/interceptor/PlusWebSocketInterceptor.java @@ -0,0 +1,46 @@ +package com.xmzs.common.chat.interceptor; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; + +import java.util.Map; + +/** + * WebSocket握手请求的拦截器 + * + * @author zendwang + */ +@Slf4j +public class PlusWebSocketInterceptor implements HandshakeInterceptor { + + /** + * 握手前 + * + * @param request request + * @param response response + * @param wsHandler wsHandler + * @param attributes attributes + * @return 是否握手成功 + */ + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) { + return true; + } + + + /** + * 握手后 + * + * @param request request + * @param response response + * @param wsHandler wsHandler + * @param exception 异常 + */ + @Override + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { + + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/SSEEventSourceListener.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/SSEEventSourceListener.java new file mode 100644 index 00000000..2ec6b966 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/SSEEventSourceListener.java @@ -0,0 +1,81 @@ +package com.xmzs.common.chat.listener; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.chat.config.LocalCache; +import com.xmzs.common.chat.entity.chat.ChatChoice; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import com.xmzs.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; + +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; + +import java.util.Objects; + +/** + * 描述:OpenAIEventSourceListener + * + * @author https:www.unfbx.com + * @date 2023-02-22 + */ +@Slf4j +@AllArgsConstructor +public class SSEEventSourceListener extends EventSourceListener { + + private static final String DONE_SIGNAL = "[DONE]"; + + private final ResponseBodyEmitter emitter; + + /** + * {@inheritDoc} + */ + @Override + public void onOpen(EventSource eventSource, Response response) { + log.info("OpenAI建立sse连接..."); + } + + /** + * {@inheritDoc} + */ + @SneakyThrows + @Override + public void onEvent(EventSource eventSource, String id, String type, String data) { + try { + log.info("响应数据{}=========",data); + emitter.send(data); + if (data.equals(DONE_SIGNAL)) { + //成功响应 + emitter.complete(); + } + } catch (Exception e) { + log.error("sse信息推送失败!"); + eventSource.cancel(); + } + } + + @Override + public void onClosed(EventSource eventSource) { + log.info("OpenAI关闭sse连接..."); + } + + @SneakyThrows + @Override + public void onFailure(EventSource eventSource, Throwable t, Response response) { + if (Objects.isNull(response)) { + return; + } + ResponseBody body = response.body(); + if (Objects.nonNull(body)) { + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); + } else { + log.error("OpenAI sse连接异常data:{},异常:{}", response, t); + } + eventSource.cancel(); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketEventListener.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketEventListener.java new file mode 100644 index 00000000..027cd0df --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketEventListener.java @@ -0,0 +1,94 @@ +package com.xmzs.common.chat.listener; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import com.xmzs.common.chat.constant.OpenAIConst; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + +import java.util.Objects; + +/** + * 描述:OpenAI流式输出Socket接收 + * + * @author https:www.unfbx.com + * @date 2023-03-23 + */ +@Slf4j +public class WebSocketEventListener extends EventSourceListener { + + private WebSocketSession session; + + /** + * 消息结束标识 + */ + private final String msgEnd = "[DONE]"; + + public WebSocketEventListener(WebSocketSession session) { + this.session = session; + } + + /** + * {@inheritDoc} + */ + @Override + public void onOpen(EventSource eventSource, Response response) { + log.info("OpenAI建立Socket连接..."); + } + + /** + * {@inheritDoc} + */ + @SneakyThrows + @Override + public void onEvent(EventSource eventSource, String id, String type, String data) { + log.info("OpenAI返回数据:{}", data); + if (data.equals(msgEnd)) { + log.info("OpenAI返回数据结束了"); + session.sendMessage(new TextMessage(msgEnd)); + return; + } + ObjectMapper mapper = new ObjectMapper(); + // 读取Json + ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); + String delta = mapper.writeValueAsString(completionResponse.getChoices().get(0).getDelta()); + session.sendMessage(new TextMessage(delta)); + } + + + @Override + public void onClosed(EventSource eventSource) { + log.info("OpenAI关闭Socket连接..."); + } + + + @SneakyThrows + @Override + public void onFailure(EventSource eventSource, Throwable t, Response response) { + if (Objects.isNull(response)) { + return; + } + ResponseBody body = response.body(); + if (Objects.nonNull(body)) { + // 返回非流式回复内容 + if(response.code() == OpenAIConst.SUCCEED_CODE){ + ObjectMapper mapper = new ObjectMapper(); + ChatCompletionResponse completionResponse = mapper.readValue(body.string(), ChatCompletionResponse.class); + String delta = mapper.writeValueAsString(completionResponse.getChoices().get(0).getMessage().getContent()); + session.sendMessage(new TextMessage(delta)); + }else { + log.error("Socket连接异常data:{},异常:{}", body.string(), t); + } + } else { + log.error("Socket连接异常data:{},异常:{}", response, t); + } + eventSource.cancel(); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketTopicListener.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketTopicListener.java new file mode 100644 index 00000000..632dda03 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/listener/WebSocketTopicListener.java @@ -0,0 +1,38 @@ +package com.xmzs.common.chat.listener; + +import cn.hutool.core.collection.CollUtil; +import com.xmzs.common.chat.holder.WebSocketSessionHolder; +import com.xmzs.common.chat.utils.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.Ordered; + +/** + * WebSocket 主题订阅监听器 + * + * @author zendwang + */ +@Slf4j +public class WebSocketTopicListener implements ApplicationRunner, Ordered { + + @Override + public void run(ApplicationArguments args) throws Exception { + WebSocketUtils.subscribeMessage((message) -> { + log.info("WebSocket主题订阅收到消息session keys={} message={}!", message.getSessionKeys(), message.getMessage()); + if (CollUtil.isNotEmpty(message.getSessionKeys())) { + message.getSessionKeys().forEach(key -> { + if (WebSocketSessionHolder.existSession(key)) { + WebSocketUtils.sendMessage(key, message.getMessage()); + } + }); + } + }); + log.info("初始化WebSocket主题订阅监听器成功"); + } + + @Override + public int getOrder() { + return -1; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiApi.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiApi.java new file mode 100644 index 00000000..d3e77191 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiApi.java @@ -0,0 +1,343 @@ +package com.xmzs.common.chat.openai; + +import com.xmzs.common.chat.entity.chat.ChatCompletionWithPicture; +import io.reactivex.Single; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; + +import com.xmzs.common.chat.entity.billing.BillingUsage; +import com.xmzs.common.chat.entity.billing.CreditGrantsResponse; +import com.xmzs.common.chat.entity.billing.Subscription; +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import com.xmzs.common.chat.entity.common.DeleteResponse; +import com.xmzs.common.chat.entity.common.OpenAiResponse; +import com.xmzs.common.chat.entity.completions.Completion; +import com.xmzs.common.chat.entity.completions.CompletionResponse; +import com.xmzs.common.chat.entity.edits.Edit; +import com.xmzs.common.chat.entity.edits.EditResponse; +import com.xmzs.common.chat.entity.embeddings.Embedding; +import com.xmzs.common.chat.entity.embeddings.EmbeddingResponse; +import com.xmzs.common.chat.entity.engines.Engine; +import com.xmzs.common.chat.entity.files.File; +import com.xmzs.common.chat.entity.files.UploadFileResponse; +import com.xmzs.common.chat.entity.fineTune.Event; +import com.xmzs.common.chat.entity.fineTune.FineTune; +import com.xmzs.common.chat.entity.fineTune.FineTuneDeleteResponse; +import com.xmzs.common.chat.entity.fineTune.FineTuneResponse; +import com.xmzs.common.chat.entity.images.Image; +import com.xmzs.common.chat.entity.images.ImageResponse; +import com.xmzs.common.chat.entity.models.Model; +import com.xmzs.common.chat.entity.models.ModelResponse; +import com.xmzs.common.chat.entity.moderations.Moderation; +import com.xmzs.common.chat.entity.moderations.ModerationResponse; +import com.xmzs.common.chat.entity.whisper.WhisperResponse; +import retrofit2.http.*; + +import java.time.LocalDate; +import java.util.Map; + +/** + * 描述: open ai官方api接口 + * + * @author https:www.unfbx.com + * 2023-02-15 + */ +public interface OpenAiApi { + + /** + * 模型列表 + * + * @return Single ModelResponse + */ + @GET("v1/models") + Single models(); + + /** + * models 返回的数据id + * + * @param id 模型主键 + * @return Single Model + */ + @GET("v1/models/{id}") + Single model(@Path("id") String id); + + /** + * 文本问答 + * Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + * + * @param completion 问答参数 + * @return Single CompletionResponse + */ + @POST("v1/completions") + Single completions(@Body Completion completion); + + /** + * Creates a new edit for the provided input, instruction, and parameters. + * 文本修复 + * + * @param edit 编辑参数 + * @return Single EditResponse + */ + @POST("v1/edits") + Single edits(@Body Edit edit); + + /** + * Creates an image given a prompt. + * 根据描述生成图片 + * + * @param image 图片对象 + * @return Single ImageResponse + */ + @POST("v1/images/generations") + Single genImages(@Body Image image); + + /** + * Creates an edited or extended image given an original image and a prompt. + * 根据描述修改图片 + * + * @param image 图片对象 + * @param mask 图片对象 + * @param requestBodyMap 请求参数 + * @return Single ImageResponse + */ + @Multipart + @POST("v1/images/edits") + Single editImages(@Part() MultipartBody.Part image, + @Part() MultipartBody.Part mask, + @PartMap() Map requestBodyMap + ); + + /** + * Creates a variation of a given image. + * + * @param image 图片对象 + * @param requestBodyMap 请求参数 + * @return Single ImageResponse + */ + @Multipart + @POST("v1/images/variations") + Single variationsImages(@Part() MultipartBody.Part image, + @PartMap() Map requestBodyMap + ); + + /** + * 文本向量计算 + * + * @param embedding 向量参数 + * @return Single EmbeddingResponse + */ + @POST("v1/embeddings") + Single embeddings(@Body Embedding embedding); + + + /** + * Returns a list of files that belong to the user's organization. + * + * @return Single OpenAiResponse File + */ + @GET("/v1/files") + Single> files(); + + /** + * 删除文件 + * + * @param fileId 文件id + * @return Single DeleteResponse + */ + @DELETE("v1/files/{file_id}") + Single deleteFile(@Path("file_id") String fileId); + + /** + * 上传文件 + * + * @param purpose purpose + * @param file 文件对象 + * @return Single UploadFileResponse + */ + @Multipart + @POST("v1/files") + Single uploadFile(@Part MultipartBody.Part file, + @Part("purpose") RequestBody purpose); + + + /** + * 检索文件 + * + * @param fileId 文件id + * @return Single File + */ + @GET("v1/files/{file_id}") + Single retrieveFile(@Path("file_id") String fileId); + + /** + * 检索文件内容 + * ###不对免费用户开放### + * ###不对免费用户开放### + * ###不对免费用户开放### + * + * @param fileId 文件id + * @return Single ResponseBody + */ + @Streaming + @GET("v1/files/{file_id}/content") + Single retrieveFileContent(@Path("file_id") String fileId); + + + /** + * 文本审核 + * + * @param moderation 文本审核参数 + * @return Single ModerationResponse + */ + @POST("v1/moderations") + Single moderations(@Body Moderation moderation); + + + /** + * 创建微调作业 + * + * @param fineTune 微调 + * @return Single FineTuneResponse + */ + @POST("v1/fine-tunes") + Single fineTune(@Body FineTune fineTune); + + /** + * 微调作业集合 + * + * @return Single OpenAiResponse FineTuneResponse + */ + @GET("v1/fine-tunes") + Single> fineTunes(); + + + /** + * 检索微调作业 + * + * @return Single FineTuneResponse + */ + @GET("v1/fine-tunes/{fine_tune_id}") + Single retrieveFineTune(@Path("fine_tune_id") String fineTuneId); + + /** + * 取消微调作业 + * + * @return Single FineTuneResponse + */ + @POST("v1/fine-tunes/{fine_tune_id}/cancel") + Single cancelFineTune(@Path("fine_tune_id") String fineTuneId); + + /** + * 微调作业事件列表 + * + * @return Single OpenAiResponse Event + */ + @GET("v1/fine-tunes/{fine_tune_id}/events") + Single> fineTuneEvents(@Path("fine_tune_id") String fineTuneId); + + /** + * 删除微调作业模型 + * Delete a fine-tuned model. You must have the Owner role in your organization. + * + * @return Single DeleteResponse + */ + @DELETE("v1/models/{model}") + Single deleteFineTuneModel(@Path("model") String model); + + + /** + * 引擎列表 + * 官方已废弃此接口 + * + * @return Single OpenAiResponse Engine + */ + @Deprecated + @GET("v1/engines") + Single> engines(); + + /** + * 检索引擎 + * 官方已废弃此接口 + * + * @param engineId 引擎id + * @return Engine + */ + @Deprecated + @GET("v1/engines/{engine_id}") + Single engine(@Path("engine_id") String engineId); + + + /** + * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型 + * + * @param chatCompletion chat completion + * @return 返回答案 + */ + @POST("v1/chat/completions") + Single chatCompletion(@Body ChatCompletion chatCompletion); + + + /** + * 语音转文字 + * + * @param file 语音文件 + * @param requestBodyMap 参数 + * @return 文本 + */ + @Multipart + @POST("v1/audio/transcriptions") + Single speechToTextTranscriptions(@Part MultipartBody.Part file, + @PartMap() Map requestBodyMap); + + /** + * 语音翻译:目前仅支持翻译为英文 + * + * @param file 语音文件 + * @param requestBodyMap 参数 + * @return 文本 + */ + @Multipart + @POST("v1/audio/translations") + Single speechToTextTranslations(@Part MultipartBody.Part file, + @PartMap() Map requestBodyMap); + + /** + * 余额查询 + * 官方禁止访问此接口 + * + * @return 余额结果 + */ + @GET("dashboard/billing/credit_grants") + @Deprecated + Single creditGrants(); + + /** + * 账户信息查询:里面包含总金额(美元)等信息 + * + * @return 账户信息 + */ + @GET("v1/dashboard/billing/subscription") + Single subscription(); + + /** + * 账户调用接口消耗金额信息查询 + * totalUsage = 账户总使用金额(美分) + * + * @param starDate 开始时间 + * @param endDate 结束时间 + * @return 消耗金额信息 + */ + @GET("v1/dashboard/billing/usage") + Single billingUsage(@Query("start_date") LocalDate starDate, @Query("end_date") LocalDate endDate); + + /** + * 最新版的GPT-4 chat completion 支持图片输入 + * + * @param chatCompletion chat completion + * @return 返回答案 + */ + @POST("v1/chat/completions") + Single chatCompletionWithPicture(@Body ChatCompletionWithPicture chatCompletion); +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiClient.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiClient.java new file mode 100644 index 00000000..81572e97 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiClient.java @@ -0,0 +1,806 @@ +package com.xmzs.common.chat.openai; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; + + +import com.xmzs.common.chat.entity.images.*; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.chat.constant.OpenAIConst; +import com.xmzs.common.chat.entity.billing.BillingUsage; +import com.xmzs.common.chat.entity.billing.Subscription; +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import com.xmzs.common.chat.entity.chat.Message; +import com.xmzs.common.chat.entity.common.DeleteResponse; +import com.xmzs.common.chat.entity.common.OpenAiResponse; +import com.xmzs.common.chat.entity.completions.Completion; +import com.xmzs.common.chat.entity.completions.CompletionResponse; +import com.xmzs.common.chat.entity.edits.Edit; +import com.xmzs.common.chat.entity.edits.EditResponse; +import com.xmzs.common.chat.entity.embeddings.Embedding; +import com.xmzs.common.chat.entity.embeddings.EmbeddingResponse; +import com.xmzs.common.chat.entity.engines.Engine; +import com.xmzs.common.chat.entity.files.File; +import com.xmzs.common.chat.entity.files.UploadFileResponse; +import com.xmzs.common.chat.entity.fineTune.Event; +import com.xmzs.common.chat.entity.fineTune.FineTune; +import com.xmzs.common.chat.entity.fineTune.FineTuneDeleteResponse; +import com.xmzs.common.chat.entity.fineTune.FineTuneResponse; +import com.xmzs.common.chat.entity.models.Model; +import com.xmzs.common.chat.entity.models.ModelResponse; +import com.xmzs.common.chat.entity.moderations.Moderation; +import com.xmzs.common.chat.entity.moderations.ModerationResponse; +import com.xmzs.common.chat.entity.whisper.Translations; +import com.xmzs.common.chat.entity.whisper.WhisperResponse; +import com.xmzs.common.chat.openai.exception.CommonError; +import com.xmzs.common.chat.openai.function.KeyRandomStrategy; +import com.xmzs.common.chat.openai.function.KeyStrategyFunction; +import com.xmzs.common.chat.openai.interceptor.DefaultOpenAiAuthInterceptor; +import com.xmzs.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor; +import com.xmzs.common.chat.openai.interceptor.OpenAiAuthInterceptor; +import io.reactivex.Single; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.jackson.JacksonConverterFactory; + + +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.TimeUnit; + + +/** + * 描述: open ai 客户端 + * + * @author https:www.unfbx.com + * @since 2023-02-11 + */ + +@Slf4j +public class OpenAiClient { + /** + * keys + */ + @Getter + @NotNull + private List apiKey; + /** + * 自定义api host使用builder的方式构造client + */ + @Getter + private String apiHost; + @Getter + private OpenAiApi openAiApi; + /** + * 自定义的okHttpClient + * 如果不自定义 ,就是用sdk默认的OkHttpClient实例 + */ + @Getter + private OkHttpClient okHttpClient; + /** + * api key的获取策略 + */ + @Getter + private KeyStrategyFunction, String> keyStrategy; + + /** + * 自定义鉴权处理拦截器
+ * 可以不设置,默认实现:DefaultOpenAiAuthInterceptor
+ * 如需自定义实现参考:DealKeyWithOpenAiAuthInterceptor + * + * @see DynamicKeyOpenAiAuthInterceptor + * @see DefaultOpenAiAuthInterceptor + */ + @Getter + private OpenAiAuthInterceptor authInterceptor; + + /** + * 构造器 + * + * @return OpenAiClient.Builder + */ + public static OpenAiClient.Builder builder() { + return new OpenAiClient.Builder(); + } + + /** + * 构造 + * + * @param builder + */ + private OpenAiClient(Builder builder) { + if (CollectionUtil.isEmpty(builder.apiKey)) { + throw new BaseException(CommonError.API_KEYS_NOT_NUL.msg() + ); + } + apiKey = builder.apiKey; + + if (StrUtil.isBlank(builder.apiHost)) { + builder.apiHost = OpenAIConst.OPENAI_HOST; + } + apiHost = builder.apiHost; + + if (Objects.isNull(builder.keyStrategy)) { + builder.keyStrategy = new KeyRandomStrategy(); + } + keyStrategy = builder.keyStrategy; + + if (Objects.isNull(builder.authInterceptor)) { + builder.authInterceptor = new DefaultOpenAiAuthInterceptor(); + } + authInterceptor = builder.authInterceptor; + authInterceptor.setApiKey(this.apiKey); + authInterceptor.setKeyStrategy(this.keyStrategy); + + if (Objects.isNull(builder.okHttpClient)) { + builder.okHttpClient = this.okHttpClient(); + } else { + //自定义的okhttpClient 需要增加api keys + builder.okHttpClient = builder.okHttpClient + .newBuilder() + .addInterceptor(authInterceptor) + .build(); + } + okHttpClient = builder.okHttpClient; + this.openAiApi = new Retrofit.Builder() + .baseUrl(apiHost) + .client(okHttpClient) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create()) + .build().create(OpenAiApi.class); + } + + + /** + * 创建默认OkHttpClient + * + * @return + */ + private OkHttpClient okHttpClient() { + if (Objects.isNull(this.authInterceptor)) { + this.authInterceptor = new DefaultOpenAiAuthInterceptor(); + } + this.authInterceptor.setApiKey(this.apiKey); + this.authInterceptor.setKeyStrategy(this.keyStrategy); + return new OkHttpClient + .Builder() + .addInterceptor(this.authInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS).build(); + } + + /** + * openAi模型列表 + * + * @return Model list + */ + public List models() { + Single models = this.openAiApi.models(); + return models.blockingGet().getData(); + } + + /** + * openAi模型详细信息 + * + * @param id 模型主键 + * @return Model 模型类 + */ + public Model model(String id) { + if (Objects.isNull(id) || "".equals(id)) { + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + Single model = this.openAiApi.model(id); + return model.blockingGet(); + } + + + /** + * 问答接口 + * + * @param completion 问答参数 + * @return CompletionResponse + */ + public CompletionResponse completions(Completion completion) { + Single completions = this.openAiApi.completions(completion); + return completions.blockingGet(); + } + + /** + * 问答接口-简易版 + * + * @param question 问题描述 + * @return CompletionResponse + */ + public CompletionResponse completions(String question) { + Completion q = Completion.builder() + .prompt(question) + .build(); + Single completions = this.openAiApi.completions(q); + return completions.blockingGet(); + } + + /** + * 文本修改 + * + * @param edit 图片对象 + * @return EditResponse + */ + public EditResponse edit(Edit edit) { + Single edits = this.openAiApi.edits(edit); + return edits.blockingGet(); + } + + /** + * 根据描述生成图片 + * + * @param prompt 描述信息 + * @return ImageResponse + */ + public ImageResponse genImages(String prompt) { + Image image = Image.builder().prompt(prompt).build(); + return this.genImages(image); + } + + /** + * 根据描述生成图片 + * + * @param image 图片参数 + * @return ImageResponse + */ + public ImageResponse genImages(Image image) { + Single edits = this.openAiApi.genImages(image); + return edits.blockingGet(); + } + + /** + * Creates an edited or extended image given an original image and a prompt. + * 根据描述修改图片 + * + * @param image 图片对象 + * @param prompt 描述信息 + * @return Item list + */ + public List editImages(java.io.File image, String prompt) { + ImageEdit imageEdit = ImageEdit.builder().prompt(prompt).build(); + return this.editImages(image, null, imageEdit); + } + + /** + * Creates an edited or extended image given an original image and a prompt. + * 根据描述修改图片 + * + * @param image 图片对象 + * @param imageEdit 图片参数 + * @return Item list + */ + public List editImages(java.io.File image, ImageEdit imageEdit) { + return this.editImages(image, null, imageEdit); + } + + /** + * Creates an edited or extended image given an original image and a prompt. + * 根据描述修改图片 + * + * @param image png格式的图片,最大4MB + * @param mask png格式的图片,最大4MB + * @param imageEdit 图片参数 + * @return Item list + */ + public List editImages(java.io.File image, java.io.File mask, ImageEdit imageEdit) { + checkImage(image); + checkImageFormat(image); + checkImageSize(image); + if (Objects.nonNull(mask)) { + checkImageFormat(image); + checkImageSize(image); + } + // 创建 RequestBody,用于封装构建RequestBody + RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), image); + MultipartBody.Part imageMultipartBody = MultipartBody.Part.createFormData("image", image.getName(), imageBody); + MultipartBody.Part maskMultipartBody = null; + if (Objects.nonNull(mask)) { + RequestBody maskBody = RequestBody.create(MediaType.parse("multipart/form-data"), mask); + maskMultipartBody = MultipartBody.Part.createFormData("mask", image.getName(), maskBody); + } + Map requestBodyMap = new HashMap<>(); + requestBodyMap.put("prompt", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getPrompt())); + requestBodyMap.put("n", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getN().toString())); + requestBodyMap.put("size", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getSize())); + requestBodyMap.put("response_format", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getResponseFormat())); + if (!(Objects.isNull(imageEdit.getUser()) || "".equals(imageEdit.getUser()))) { + requestBodyMap.put("user", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getUser())); + } + Single imageResponse = this.openAiApi.editImages( + imageMultipartBody, + maskMultipartBody, + requestBodyMap + ); + return imageResponse.blockingGet().getData(); + } + + /** + * Creates a variation of a given image. + *

+ * 变化图片,类似ai重做图片 + * + * @param image 图片对象 + * @param imageVariations 图片参数 + * @return ImageResponse + */ + public ImageResponse variationsImages(java.io.File image, ImageVariations imageVariations) { + checkImage(image); + checkImageFormat(image); + checkImageSize(image); + RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), image); + MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("image", image.getName(), imageBody); + Map requestBodyMap = new HashMap<>(); + requestBodyMap.put("n", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getN().toString())); + requestBodyMap.put("size", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getSize())); + requestBodyMap.put("response_format", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getResponseFormat())); + if (!(Objects.isNull(imageVariations.getUser()) || "".equals(imageVariations.getUser()))) { + requestBodyMap.put("user", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getUser())); + } + Single variationsImages = this.openAiApi.variationsImages( + multipartBody, + requestBodyMap + ); + return variationsImages.blockingGet(); + } + + /** + * Creates a variation of a given image. + * + * @param image 图片对象 + * @return ImageResponse + */ + public ImageResponse variationsImages(java.io.File image) { + checkImage(image); + checkImageFormat(image); + checkImageSize(image); + ImageVariations imageVariations = ImageVariations.builder().build(); + return this.variationsImages(image, imageVariations); + } + + /** + * 校验图片不能为空 + * + * @param image + */ + private void checkImage(java.io.File image) { + if (Objects.isNull(image)) { + log.error("image不能为空"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + } + + /** + * 校验图片格式 + * + * @param image + */ + private void checkImageFormat(java.io.File image) { + if (!(image.getName().endsWith("png") || image.getName().endsWith("PNG"))) { + log.error("image格式错误"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + } + + /** + * 校验图片大小 + * + * @param image + */ + private void checkImageSize(java.io.File image) { + if (image.length() > 4 * 1024 * 1024) { + log.error("image最大支持4MB"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + } + + /** + * 向量计算:单文本 + * + * @param input 单文本 + * @return EmbeddingResponse + */ + public EmbeddingResponse embeddings(String input) { + List inputs = new ArrayList<>(1); + inputs.add(input); + Embedding embedding = Embedding.builder().input(inputs).build(); + return this.embeddings(embedding); + } + + /** + * 向量计算:集合文本 + * + * @param input 文本集合 + * @return EmbeddingResponse + */ + public EmbeddingResponse embeddings(List input) { + Embedding embedding = Embedding.builder().input(input).build(); + return this.embeddings(embedding); + } + + /** + * 文本转换向量 + * + * @param embedding 入参 + * @return EmbeddingResponse + */ + public EmbeddingResponse embeddings(Embedding embedding) { + Single embeddings = this.openAiApi.embeddings(embedding); + return embeddings.blockingGet(); + } + + /** + * 获取文件列表 + * + * @return File list + */ + public List files() { + Single> files = this.openAiApi.files(); + return files.blockingGet().getData(); + } + + /** + * 删除文件 + * + * @param fileId 文件id + * @return DeleteResponse + */ + public DeleteResponse deleteFile(String fileId) { + Single deleteFile = this.openAiApi.deleteFile(fileId); + return deleteFile.blockingGet(); + } + + /** + * 上传文件 + * + * @param purpose purpose + * @param file 文件对象 + * @return UploadFileResponse + */ + public UploadFileResponse uploadFile(String purpose, java.io.File file) { + // 创建 RequestBody,用于封装构建RequestBody + RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); + MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody); + + RequestBody purposeBody = RequestBody.create(MediaType.parse("multipart/form-data"), purpose); + Single uploadFileResponse = this.openAiApi.uploadFile(multipartBody, purposeBody); + return uploadFileResponse.blockingGet(); + } + + /** + * 上传文件 + * + * @param file 文件 + * @return UploadFileResponse + */ + public UploadFileResponse uploadFile(java.io.File file) { + //purpose 官网示例默认是:fine-tune + return this.uploadFile("fine-tune", file); + } + + /** + * 检索文件 + * + * @param fileId 文件id + * @return File + */ + public File retrieveFile(String fileId) { + Single fileContent = this.openAiApi.retrieveFile(fileId); + return fileContent.blockingGet(); + } + + /** + * 检索文件内容 + * 免费用户无法使用此接口 #未经过测试 + * + * @param fileId + * @return ResponseBody + */ +// public ResponseBody retrieveFileContent(String fileId) { +// Single fileContent = this.openAiApi.retrieveFileContent(fileId); +// return fileContent.blockingGet(); +// } + + /** + * 文本审核 + * + * @param input 待检测数据 + * @return ModerationResponse + */ + public ModerationResponse moderations(String input) { + List content = new ArrayList<>(1); + content.add(input); + Moderation moderation = Moderation.builder().input(content).build(); + return this.moderations(moderation); + } + + /** + * 文本审核 + * + * @param input 待检测数据集合 + * @return ModerationResponse + */ + public ModerationResponse moderations(List input) { + Moderation moderation = Moderation.builder().input(input).build(); + return this.moderations(moderation); + } + + /** + * 文本审核 + * + * @param moderation 审核参数 + * @return ModerationResponse + */ + public ModerationResponse moderations(Moderation moderation) { + Single moderations = this.openAiApi.moderations(moderation); + return moderations.blockingGet(); + } + + /** + * 创建微调模型 + * + * @param fineTune 微调作业id + * @return FineTuneResponse + */ + public FineTuneResponse fineTune(FineTune fineTune) { + Single fineTuneResponse = this.openAiApi.fineTune(fineTune); + return fineTuneResponse.blockingGet(); + } + + /** + * 创建微调模型 + * + * @param trainingFileId 文件id,文件上传返回的id + * @return FineTuneResponse + */ + public FineTuneResponse fineTune(String trainingFileId) { + FineTune fineTune = FineTune.builder().trainingFile(trainingFileId).build(); + return this.fineTune(fineTune); + } + + /** + * 微调模型列表 + * + * @return FineTuneResponse list + */ + public List fineTunes() { + Single> fineTunes = this.openAiApi.fineTunes(); + return fineTunes.blockingGet().getData(); + } + + /** + * 检索微调作业 + * + * @param fineTuneId 微调作业id + * @return FineTuneResponse + */ + public FineTuneResponse retrieveFineTune(String fineTuneId) { + Single fineTune = this.openAiApi.retrieveFineTune(fineTuneId); + return fineTune.blockingGet(); + } + + /** + * 取消微调作业 + * + * @param fineTuneId 主键 + * @return FineTuneResponse + */ + public FineTuneResponse cancelFineTune(String fineTuneId) { + Single fineTune = this.openAiApi.cancelFineTune(fineTuneId); + return fineTune.blockingGet(); + } + + /** + * 微调作业事件列表 + * + * @param fineTuneId 微调作业id + * @return Event List + */ + public List fineTuneEvents(String fineTuneId) { + Single> events = this.openAiApi.fineTuneEvents(fineTuneId); + return events.blockingGet().getData(); + } + + /** + * 删除微调作业模型 + * Delete a fine-tuned model. You must have the Owner role in your organization. + * + * @param model 模型名称 + * @return FineTuneDeleteResponse + */ + public FineTuneDeleteResponse deleteFineTuneModel(String model) { + Single delete = this.openAiApi.deleteFineTuneModel(model); + return delete.blockingGet(); + } + + + /** + * 引擎列表 + * + * @return Engine List + */ + @Deprecated + public List engines() { + Single> engines = this.openAiApi.engines(); + return engines.blockingGet().getData(); + } + + /** + * 引擎详细信息 + * + * @param engineId 引擎id + * @return Engine + */ + @Deprecated + public Engine engine(String engineId) { + Single engine = this.openAiApi.engine(engineId); + return engine.blockingGet(); + } + + /** + * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型 + * + * @param chatCompletion 问答参数 + * @return 答案 + */ + public ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) { + Single chatCompletionResponse = this.openAiApi.chatCompletion(chatCompletion); + return chatCompletionResponse.blockingGet(); + } + + /** + * 简易版 + * + * @param messages 问答参数 + * @return 答案 + */ + public ChatCompletionResponse chatCompletion(List messages) { + ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).build(); + return this.chatCompletion(chatCompletion); + } + + + /** + * 语音翻译:目前仅支持翻译为英文 + * + * @param translations 参数 + * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm + * @return 翻译后文本 + */ + public WhisperResponse speechToTextTranslations(java.io.File file, Translations translations) { + //文件 + RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); + MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody); + //自定义参数 + Map requestBodyMap = new HashMap<>(5,1L); + + if (StrUtil.isNotBlank(translations.getModel())) { + requestBodyMap.put(Translations.Fields.model, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getModel())); + } + if (StrUtil.isNotBlank(translations.getPrompt())) { + requestBodyMap.put(Translations.Fields.prompt, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getPrompt())); + } + if (StrUtil.isNotBlank(translations.getResponseFormat())) { + requestBodyMap.put(Translations.Fields.responseFormat, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getResponseFormat())); + } + requestBodyMap.put(Translations.Fields.temperature, RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(translations.getTemperature()))); + Single whisperResponse = this.openAiApi.speechToTextTranslations(multipartBody, requestBodyMap); + return whisperResponse.blockingGet(); + } + + /** + * 简易版 语音翻译:目前仅支持翻译为英文 + * + * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm + * @return 翻译后文本 + */ + public WhisperResponse speechToTextTranslations(java.io.File file) { + Translations translations = Translations.builder().build(); + return this.speechToTextTranslations(file, translations); + } + + /** + * 校验语音文件大小给出提示,目前官方限制25MB,后续可能会改动所以不报错只做提示 + * + * @param file + */ + private void checkSpeechFileSize(java.io.File file) { + if (file.length() > 25 * 1204 * 1024) { + log.warn("2023-03-02官方文档提示:文件不能超出25MB"); + } + } + + /** + * 账户信息查询:里面包含总金额等信息 + * + * @return 账户信息 + */ + public Subscription subscription() { + Single subscription = this.openAiApi.subscription(); + return subscription.blockingGet(); + } + /** + * 账户调用接口消耗金额信息查询 + * 最多查询100天 + * + * @param starDate 开始时间 + * @param endDate 结束时间 + * @return 消耗金额信息 + */ + public BillingUsage billingUsage(@NotNull LocalDate starDate, @NotNull LocalDate endDate) { + Single billingUsage = this.openAiApi.billingUsage(starDate, endDate); + return billingUsage.blockingGet(); + } + + + public static final class Builder { + /** + * api keys + */ + private @NotNull List apiKey; + /** + * api请求地址,结尾处有斜杠 + * + */ + private String apiHost; + /** + * 自定义OkhttpClient + */ + private OkHttpClient okHttpClient; + + /** + * api key的获取策略 + */ + private KeyStrategyFunction keyStrategy; + + /** + * 自定义鉴权拦截器 + */ + private OpenAiAuthInterceptor authInterceptor; + + public Builder() { + } + + /** + * @param val api请求地址,结尾处有斜杠 + * @return Builder对象 + */ + public Builder apiHost(String val) { + apiHost = val; + return this; + } + + public Builder apiKey(@NotNull List val) { + apiKey = val; + return this; + } + + public Builder keyStrategy(KeyStrategyFunction val) { + keyStrategy = val; + return this; + } + + public Builder okHttpClient(OkHttpClient val) { + okHttpClient = val; + return this; + } + + public Builder authInterceptor(OpenAiAuthInterceptor val) { + authInterceptor = val; + return this; + } + + public OpenAiClient build() { + return new OpenAiClient(this); + } + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiStreamClient.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiStreamClient.java new file mode 100644 index 00000000..3fc25f19 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/OpenAiStreamClient.java @@ -0,0 +1,419 @@ +package com.xmzs.common.chat.openai; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.ContentType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.chat.config.LocalCache; +import com.xmzs.common.chat.entity.billing.BillingUsage; +import com.xmzs.common.chat.entity.billing.KeyInfo; +import com.xmzs.common.chat.entity.billing.Subscription; +import com.xmzs.common.chat.entity.chat.BaseChatCompletion; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import com.xmzs.common.chat.entity.chat.ChatCompletionWithPicture; +import com.xmzs.common.chat.entity.images.Image; +import com.xmzs.common.chat.entity.images.ImageResponse; +import com.xmzs.common.chat.entity.models.Model; +import com.xmzs.common.chat.entity.models.ModelResponse; +import com.xmzs.common.chat.entity.whisper.Transcriptions; +import com.xmzs.common.chat.entity.whisper.WhisperResponse; +import com.xmzs.common.chat.openai.exception.CommonError; +import com.xmzs.common.chat.openai.function.KeyRandomStrategy; +import com.xmzs.common.chat.openai.function.KeyStrategyFunction; +import com.xmzs.common.chat.openai.interceptor.DefaultOpenAiAuthInterceptor; +import com.xmzs.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor; +import com.xmzs.common.chat.openai.interceptor.OpenAiAuthInterceptor; +import com.xmzs.common.core.exception.ServiceException; +import io.reactivex.Single; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import okhttp3.sse.EventSources; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.chat.constant.OpenAIConst; +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import java.io.InputStream; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.nio.charset.StandardCharsets; +import java.rmi.ServerException; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.net.URI; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.http.HttpResponse; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +/** + * 描述: open ai 客户端 + * + * @author https:www.unfbx.com + * 2023-02-28 + */ + +@Slf4j +public class OpenAiStreamClient { + @Getter + @NotNull + private List apiKey; + /** + * 自定义api host使用builder的方式构造client + */ + @Getter + private String apiHost; + /** + * 自定义的okHttpClient + * 如果不自定义 ,就是用sdk默认的OkHttpClient实例 + */ + @Getter + private OkHttpClient okHttpClient; + + /** + * api key的获取策略 + */ + @Getter + private KeyStrategyFunction, String> keyStrategy; + + @Getter + private OpenAiApi openAiApi; + + /** + * 自定义鉴权处理拦截器
+ * 可以不设置,默认实现:DefaultOpenAiAuthInterceptor
+ * 如需自定义实现参考:DealKeyWithOpenAiAuthInterceptor + * + * @see DynamicKeyOpenAiAuthInterceptor + * @see DefaultOpenAiAuthInterceptor + */ + @Getter + private OpenAiAuthInterceptor authInterceptor; + + private static final String API_KEY = "sk-Waea254YSRYVg4FZVCz2CDz73B22xRpmKpJ41kbczVgpPxvg"; + + HttpClient client = HttpClient.newHttpClient(); + + private static final String DONE_SIGNAL = "[DONE]"; + + /** + * 构造实例对象 + * + * @param builder + */ + private OpenAiStreamClient(Builder builder) { + if (CollectionUtil.isEmpty(builder.apiKey)) { + throw new BaseException(CommonError.API_KEYS_NOT_NUL.msg()); + } + apiKey = builder.apiKey; + + if (StrUtil.isBlank(builder.apiHost)) { + builder.apiHost = OpenAIConst.OPENAI_HOST; + } + apiHost = builder.apiHost; + + if (Objects.isNull(builder.keyStrategy)) { + builder.keyStrategy = new KeyRandomStrategy(); + } + keyStrategy = builder.keyStrategy; + + if (Objects.isNull(builder.authInterceptor)) { + builder.authInterceptor = new DefaultOpenAiAuthInterceptor(); + } + authInterceptor = builder.authInterceptor; + //设置apiKeys和key的获取策略 + authInterceptor.setApiKey(this.apiKey); + authInterceptor.setKeyStrategy(this.keyStrategy); + + if (Objects.isNull(builder.okHttpClient)) { + builder.okHttpClient = this.okHttpClient(); + } else { + //自定义的okhttpClient 需要增加api keys + builder.okHttpClient = builder.okHttpClient + .newBuilder() + .addInterceptor(authInterceptor) + .build(); + } + okHttpClient = builder.okHttpClient; + + this.openAiApi = new Retrofit.Builder() + .baseUrl(apiHost) + .client(okHttpClient) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create()) + .build().create(OpenAiApi.class); + } + + /** + * 创建默认的OkHttpClient + */ + private OkHttpClient okHttpClient() { + if (Objects.isNull(this.authInterceptor)) { + this.authInterceptor = new DefaultOpenAiAuthInterceptor(); + } + this.authInterceptor.setApiKey(this.apiKey); + this.authInterceptor.setKeyStrategy(this.keyStrategy); + OkHttpClient okHttpClient = new OkHttpClient + .Builder() + .addInterceptor(this.authInterceptor) + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(50, TimeUnit.SECONDS) + .readTimeout(50, TimeUnit.SECONDS) + .build(); + return okHttpClient; + } + + + /** + * 流式输出,最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型 + * + * @param chatCompletion 问答参数 + * @param eventSourceListener 监听器 + */ + public void streamChatCompletion(T chatCompletion, EventSourceListener eventSourceListener) { + if (Objects.isNull(eventSourceListener)) { + log.error("参数异常:EventSourceListener不能为空!"); + throw new BaseException(CommonError.PARAM_ERROR.msg()); + } + try { + EventSource.Factory factory = EventSources.createFactory(this.okHttpClient); + ObjectMapper mapper = new ObjectMapper(); + String requestBody = mapper.writeValueAsString(chatCompletion); + Request request = new Request.Builder() + .url(this.apiHost + "v1/chat/completions") + .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody)) + .build(); + factory.newEventSource(request, eventSourceListener); + } catch (Exception e) { + log.error("请求参数解析异常:{}", e.getMessage()); + } + } + + + /** + * 根据描述生成图片 + * + * @param image 图片参数 + * @return ImageResponse + */ + public ImageResponse genImages(Image image) { + Single edits = this.openAiApi.genImages(image); + return edits.blockingGet(); + } + + /** + * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型 + * + * @param chatCompletion 问答参数 + * @return 答案 + */ + public ChatCompletionResponse chatCompletion(T chatCompletion) { + if (chatCompletion instanceof ChatCompletion) { + Single chatCompletionResponse = this.openAiApi.chatCompletion((ChatCompletion) chatCompletion); + return chatCompletionResponse.blockingGet(); + } + Single chatCompletionResponse = this.openAiApi.chatCompletionWithPicture((ChatCompletionWithPicture) chatCompletion); + return chatCompletionResponse.blockingGet(); + } + + /** + * 获取openKey账户信息(近90天) + * + * @param key + * @return KeyInfo + * @Date 2023/7/6 + **/ + public KeyInfo getKeyInfo(String key) { + Date now = new Date(); + Date start = new Date(now.getTime() - (long) 90 * 24 * 60 * 60 * 1000); + Date end = new Date(now.getTime() + (long) 24 * 60 * 60 * 1000); + + BillingUsage billingUsage = billingUsage(start.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), end.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); + double totalUsage = billingUsage.getTotalUsage().doubleValue() / 100; + System.out.println(totalUsage); + Subscription subscription = subscription(); + KeyInfo keyInfo = new KeyInfo(); + String start_key = key.substring(0, 6); + String end_key = key.substring(key.length() - 6); + String mid_key = key.substring(6, key.length() - 6); + mid_key = mid_key.replaceAll(".", "*"); + + keyInfo.setKeyValue(start_key + mid_key + end_key); + keyInfo.setTotalAmount(subscription.getHardLimitUsd()); + keyInfo.setRemaining(subscription.getHardLimitUsd() - totalUsage); + keyInfo.setTotalUsage(totalUsage); + keyInfo.setLimitDate(new Date(subscription.getAccessUntil() * 1000).toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); + keyInfo.setPlanTitle(subscription.getPlan() != null ? subscription.getPlan().getTitle() : "null"); + keyInfo.setIsHasPaymentMethod(subscription.isHasPaymentMethod()); + keyInfo.setModel(getModelName()); + return keyInfo; + } + + /** + * 获取可用模型 + * + * @param + * @return String + * @Date 2023/7/6 + **/ + public String getModelName() { + Single models = this.openAiApi.models(); + List modelList = models.blockingGet().getData(); + for (Model model : modelList) { + if (Objects.equals(model.getId(), "gpt-4")) { + return "GPT-4.0"; + } + } + return "GPT-3.5"; + } + + /** + * 账户调用接口消耗金额信息查询 + * 最多查询100天 + * + * @param starDate 开始时间 + * @param endDate 结束时间 + * @return 消耗金额信息 + */ + public BillingUsage billingUsage(@NotNull LocalDate starDate, @NotNull LocalDate endDate) { + Single billingUsage = this.openAiApi.billingUsage(starDate, endDate); + return billingUsage.blockingGet(); + } + + /** + * 账户信息查询:里面包含总金额等信息 + * + * @return 账户信息 + */ + public Subscription subscription() { + Single subscription = this.openAiApi.subscription(); + return subscription.blockingGet(); + } + + /** + * 语音转文字 + * + * @param transcriptions 参数 + * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm + * @return 语音文本 + */ + public WhisperResponse speechToTextTranscriptions(java.io.File file, Transcriptions transcriptions) { + //文件 + RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); + MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody); + //自定义参数 + Map requestBodyMap = new HashMap<>(10); + if (StrUtil.isNotBlank(transcriptions.getLanguage())) { + requestBodyMap.put(Transcriptions.Fields.language, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getLanguage())); + } + if (StrUtil.isNotBlank(transcriptions.getModel())) { + requestBodyMap.put(Transcriptions.Fields.model, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getModel())); + } + if (StrUtil.isNotBlank(transcriptions.getPrompt())) { + requestBodyMap.put(Transcriptions.Fields.prompt, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getPrompt())); + } + if (StrUtil.isNotBlank(transcriptions.getResponseFormat())) { + requestBodyMap.put(Transcriptions.Fields.responseFormat, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getResponseFormat())); + } + if (Objects.nonNull(transcriptions.getTemperature())) { + requestBodyMap.put(Transcriptions.Fields.temperature, RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(transcriptions.getTemperature()))); + } + Single whisperResponse = this.openAiApi.speechToTextTranscriptions(multipartBody, requestBodyMap); + return whisperResponse.blockingGet(); + } + + /** + * 简易版 语音转文字 + * + * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm + * @return 语音文本 + */ + public WhisperResponse speechToTextTranscriptions(java.io.File file) { + Transcriptions transcriptions = Transcriptions.builder().build(); + return this.speechToTextTranscriptions(file, transcriptions); + } + + /** + * 构造 + * + * @return Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private @NotNull List apiKey; + /** + * api请求地址,结尾处有斜杠 + * + * @see OpenAIConst + */ + private String apiHost; + + /** + * 自定义OkhttpClient + */ + private OkHttpClient okHttpClient; + + + /** + * api key的获取策略 + */ + private KeyStrategyFunction keyStrategy; + + /** + * 自定义鉴权拦截器 + */ + private OpenAiAuthInterceptor authInterceptor; + + public Builder() { + } + + public Builder apiKey(@NotNull List val) { + apiKey = val; + return this; + } + + /** + * @param val api请求地址,结尾处有斜杠 + * @return Builder + * @see OpenAIConst + */ + public Builder apiHost(String val) { + apiHost = val; + return this; + } + + public Builder keyStrategy(KeyStrategyFunction val) { + keyStrategy = val; + return this; + } + + public Builder okHttpClient(OkHttpClient val) { + okHttpClient = val; + return this; + } + + public Builder authInterceptor(OpenAiAuthInterceptor val) { + authInterceptor = val; + return this; + } + + public OpenAiStreamClient build() { + return new OpenAiStreamClient(this); + } + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/TestOpenAIAPI.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/TestOpenAIAPI.java new file mode 100644 index 00000000..f4e8bd51 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/TestOpenAIAPI.java @@ -0,0 +1,32 @@ +package com.xmzs.common.chat.openai; + +import okhttp3.*; + +import java.io.IOException; + +public class TestOpenAIAPI { + + private final OkHttpClient client = new OkHttpClient(); + private static final String API_KEY = "sk-Waea254YSRYVg4FZVCz2CDz73B22xRpmKpJ41kbczVgpPxvg"; + private static final String URL = "https://api.gptgod.online/v1/chat/completions"; + + public void getChatGptResponse(String prompt) throws IOException { + RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), + "{\"model\": \"gpt-3.5-turbo\", \"messages\": [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, {\"role\": \"user\", \"content\": \"" + prompt + "\"}]}"); + + Request request = new Request.Builder() + .url(URL) + .post(body) + .addHeader("Authorization", "Bearer " + API_KEY) + .build(); + + try (Response response = client.newCall(request).execute()) { + System.out.println(response.body().string()); + } + } + + public static void main(String[] args) throws IOException { + TestOpenAIAPI api = new TestOpenAIAPI(); + api.getChatGptResponse("Hello, how are you?"); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/CommonError.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/CommonError.java new file mode 100644 index 00000000..a02877d3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/CommonError.java @@ -0,0 +1,39 @@ +package com.xmzs.common.chat.openai.exception; + +/** + * 描述: 错误 + * + * @author https:www.unfbx.com + * 2023-02-11 + */ +public enum CommonError implements IError { + API_KEYS_NOT_NUL(500, "API KEYS 不能为空"), + NO_ACTIVE_API_KEYS(500, "没有可用的API KEYS"), + SYS_ERROR(500, "系统繁忙"), + PARAM_ERROR(501, "参数异常"), + RETRY_ERROR(502, "请求异常,请重试~"), + //官方的错误码列表:https://platform.openai.com/docs/guides/error-codes/api-errors + OPENAI_AUTHENTICATION_ERROR(401, "身份验证无效/提供的 API 密钥不正确/您必须是组织的成员才能使用 API"), + OPENAI_LIMIT_ERROR(429 , "达到请求的速率限制/您超出了当前配额,请检查您的计划和帐单详细信息/发动机当前过载,请稍后重试"), + OPENAI_SERVER_ERROR(500, "服务器在处理您的请求时出错"), + ; + + + private int code; + private String msg; + + CommonError(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String msg() { + return this.msg; + } + + @Override + public int code() { + return this.code; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/IError.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/IError.java new file mode 100644 index 00000000..98f73b8e --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/exception/IError.java @@ -0,0 +1,12 @@ +package com.xmzs.common.chat.openai.exception; +/** + * 描述: + * + * @author https:www.unfbx.com + * 2023-02-11 + */ +public interface IError { + String msg(); + + int code(); +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyRandomStrategy.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyRandomStrategy.java new file mode 100644 index 00000000..8251db3c --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyRandomStrategy.java @@ -0,0 +1,19 @@ +package com.xmzs.common.chat.openai.function; + +import cn.hutool.core.util.RandomUtil; + +import java.util.List; + +/** + * 描述:随机策略 + * + * @author https:www.unfbx.com + * @since 2023-04-03 + */ +public class KeyRandomStrategy implements KeyStrategyFunction, String> { + + @Override + public String apply(List apiKeys) { + return RandomUtil.randomEle(apiKeys); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyStrategyFunction.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyStrategyFunction.java new file mode 100644 index 00000000..97d90a89 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/function/KeyStrategyFunction.java @@ -0,0 +1,24 @@ +package com.xmzs.common.chat.openai.function; + +import java.util.function.Function; + +/** + * 描述:key 的获取策略 + * jdk默认实现 + * @see Function + * + * @author https:www.unfbx.com + * @since 2023-04-03 + */ +@FunctionalInterface +public interface KeyStrategyFunction { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + */ + R apply(T t); + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DefaultOpenAiAuthInterceptor.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DefaultOpenAiAuthInterceptor.java new file mode 100644 index 00000000..fb17ccf6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DefaultOpenAiAuthInterceptor.java @@ -0,0 +1,65 @@ +package com.xmzs.common.chat.openai.interceptor; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * 描述:请求增加header apikey + * + * @author https:www.unfbx.com + * @since 2023-03-23 + */ +@Slf4j +public class DefaultOpenAiAuthInterceptor extends OpenAiAuthInterceptor { + /** + * 请求头处理 + */ + public DefaultOpenAiAuthInterceptor() { + super.setWarringConfig(null); + } + + /** + * 构造方法 + * + * @param warringConfig 所有的key都失效后的告警参数配置 + */ + public DefaultOpenAiAuthInterceptor(Map warringConfig) { + super.setWarringConfig(warringConfig); + } + + /** + * 拦截器鉴权 + * + * @param chain Chain + * @return Response对象 + * @throws IOException io异常 + */ + @Override + public Response intercept(Chain chain) throws IOException { + Request original = chain.request(); + return chain.proceed(auth(super.getKey(), original)); + } + + /** + * key失效或者禁用后的处理逻辑 + * 默认不处理 + * + * @param apiKey 返回新的api keys集合 + * @return 新的apiKey集合 + */ + @Override + protected List onErrorDealApiKeys(String apiKey) { + return super.getApiKey(); + } + + @Override + protected void noHaveActiveKeyWarring() { + log.error("--------> [告警] 没有可用的key!!!"); + return; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DynamicKeyOpenAiAuthInterceptor.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DynamicKeyOpenAiAuthInterceptor.java new file mode 100644 index 00000000..7deba375 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/DynamicKeyOpenAiAuthInterceptor.java @@ -0,0 +1,109 @@ +package com.xmzs.common.chat.openai.interceptor; + +import cn.hutool.json.JSONUtil; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Request; +import okhttp3.Response; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.chat.entity.common.OpenAiResponse; +import com.xmzs.common.chat.openai.exception.CommonError; + + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 描述:动态处理key的鉴权拦截器 + * + * @author https:www.unfbx.com + * @since 2023-04-25 + */ +@Getter +@Slf4j +public class DynamicKeyOpenAiAuthInterceptor extends OpenAiAuthInterceptor { + /** + * 账号被封了 + */ + private static final String ACCOUNT_DEACTIVATED = "account_deactivated"; + /** + * key不正确 + */ + private static final String INVALID_API_KEY = "invalid_api_key"; + + /** + * 请求头处理 + * + */ + public DynamicKeyOpenAiAuthInterceptor() { + this.setWarringConfig(null); + } + + /** + * 构造方法 + * + * @param warringConfig 所有的key都失效后的告警参数配置 + */ + public DynamicKeyOpenAiAuthInterceptor(Map warringConfig) { + this.setWarringConfig(warringConfig); + } + + @Override + public Response intercept(Chain chain) throws IOException { + String key = getKey(); + Request original = chain.request(); + Request request = this.auth(key, original); + Response response = chain.proceed(request); + if (!response.isSuccessful()) { + String errorMsg = response.body().string(); + if (response.code() == CommonError.OPENAI_AUTHENTICATION_ERROR.code() + || response.code() == CommonError.OPENAI_LIMIT_ERROR.code() + || response.code() == CommonError.OPENAI_SERVER_ERROR.code()) { + OpenAiResponse openAiResponse = JSONUtil.toBean(errorMsg, OpenAiResponse.class); + String errorCode = openAiResponse.getError().getCode(); + log.error("--------> 请求openai异常,错误code:{}", errorCode); + log.error("--------> 请求异常:{}", errorMsg); + //账号被封或者key不正确就移除掉 + if (ACCOUNT_DEACTIVATED.equals(errorCode) || INVALID_API_KEY.equals(errorCode)) { + super.setApiKey(this.onErrorDealApiKeys(key)); + } + throw new BaseException(openAiResponse.getError().getMessage()); + } + //非官方定义的错误code + log.error("--------> 请求异常:{}", errorMsg); + OpenAiResponse openAiResponse = JSONUtil.toBean(errorMsg, OpenAiResponse.class); + if (Objects.nonNull(openAiResponse.getError())) { + log.error(openAiResponse.getError().getMessage()); + throw new BaseException(openAiResponse.getError().getMessage()); + } + throw new BaseException(CommonError.RETRY_ERROR.msg()); + } + return response; + } + + + @Override + protected List onErrorDealApiKeys(String errorKey) { + List apiKey = super.getApiKey().stream().filter(e -> !errorKey.equals(e)).collect(Collectors.toList()); + log.error("--------> 当前ApiKey:[{}] 失效了,移除!", errorKey); + return apiKey; + } + + /** + * 所有的key都失效后,自定义预警配置 + * 不配置直接return + */ + @Override + protected void noHaveActiveKeyWarring() { + log.error("--------> [告警] 没有可用的key!!!"); + return; + } + + @Override + public Request auth(String key, Request original) { + return super.auth(key, original); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAILogger.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAILogger.java new file mode 100644 index 00000000..5c24efb2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAILogger.java @@ -0,0 +1,18 @@ +package com.xmzs.common.chat.openai.interceptor; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.logging.HttpLoggingInterceptor; + +/** + * 描述: 日志 + * + * @author https:www.unfbx.com + * 2023-02-28 + */ +@Slf4j +public class OpenAILogger implements HttpLoggingInterceptor.Logger { + @Override + public void log(String message) { + log.info("OkHttp-------->:{}", message); + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiAuthInterceptor.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiAuthInterceptor.java new file mode 100644 index 00000000..f40d782a --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiAuthInterceptor.java @@ -0,0 +1,85 @@ +package com.xmzs.common.chat.openai.interceptor; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import lombok.Getter; +import lombok.Setter; +import okhttp3.Interceptor; +import okhttp3.Request; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.chat.openai.exception.CommonError; +import com.xmzs.common.chat.openai.function.KeyStrategyFunction; + + +import java.util.List; +import java.util.Map; + +public abstract class OpenAiAuthInterceptor implements Interceptor { + + + /** + * key 集合 + */ + @Getter + @Setter + private List apiKey; + /** + * 自定义的key的使用策略 + */ + @Getter + @Setter + private KeyStrategyFunction, String> keyStrategy; + + /** + * 预警触发参数配置,配置参数实现飞书、钉钉、企业微信、邮箱预警等功能 + */ + @Getter + @Setter + private Map warringConfig; + + /** + * 自定义apiKeys的处理逻辑 + * + * @param errorKey 错误的key + * @return 返回值是新的apiKeys + */ + protected abstract List onErrorDealApiKeys(String errorKey); + + /** + * 所有的key都失效后,自定义预警配置 + * 可以通过warringConfig配置参数实现飞书、钉钉、企业微信、邮箱预警等 + */ + protected abstract void noHaveActiveKeyWarring(); + + + /** + * 获取请求key + * + * @return key + */ + public final String getKey() { + if (CollectionUtil.isEmpty(apiKey)) { + this.noHaveActiveKeyWarring(); + throw new BaseException(CommonError.NO_ACTIVE_API_KEYS.msg()); + } + return keyStrategy.apply(apiKey); + } + + /** + * 默认的鉴权处理方法 + * + * @param key api key + * @param original 源请求体 + * @return 请求体 + */ + public Request auth(String key, Request original) { + Request request = original.newBuilder() + .header(Header.AUTHORIZATION.getValue(), "Bearer " + key) + .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .method(original.method(), original.body()) + .build(); + return request; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiResponseInterceptor.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiResponseInterceptor.java new file mode 100644 index 00000000..4d3ade56 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/openai/interceptor/OpenAiResponseInterceptor.java @@ -0,0 +1,48 @@ +package com.xmzs.common.chat.openai.interceptor; + +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.chat.entity.common.OpenAiResponse; +import com.xmzs.common.chat.openai.exception.CommonError; + + +import java.io.IOException; +import java.util.Objects; + +/** + * 描述:openai 返回值处理Interceptor + * + * @author https:www.unfbx.com + * @since 2023-03-23 + */ +@Slf4j +public class OpenAiResponseInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + + Request original = chain.request(); + Response response = chain.proceed(original); + if (!response.isSuccessful()) { + if (response.code() == CommonError.OPENAI_AUTHENTICATION_ERROR.code() + || response.code() == CommonError.OPENAI_LIMIT_ERROR.code() + || response.code() == CommonError.OPENAI_SERVER_ERROR.code()) { + OpenAiResponse openAiResponse = JSONUtil.toBean(response.body().string(), OpenAiResponse.class); + log.error(openAiResponse.getError().getMessage()); + throw new BaseException(openAiResponse.getError().getMessage()); + } + String errorMsg = response.body().string(); + log.error("--------> 请求异常:{}", errorMsg); + OpenAiResponse openAiResponse = JSONUtil.toBean(errorMsg, OpenAiResponse.class); + if (Objects.nonNull(openAiResponse.getError())) { + log.error(openAiResponse.getError().getMessage()); + throw new BaseException(openAiResponse.getError().getMessage()); + } + throw new BaseException(CommonError.RETRY_ERROR.msg()); + } + return response; + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/TikTokensUtil.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/TikTokensUtil.java new file mode 100644 index 00000000..34fd90e9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/TikTokensUtil.java @@ -0,0 +1,234 @@ +package com.xmzs.common.chat.utils; + +import cn.hutool.core.util.StrUtil; +import com.knuddels.jtokkit.Encodings; +import com.knuddels.jtokkit.api.Encoding; +import com.knuddels.jtokkit.api.EncodingRegistry; +import com.knuddels.jtokkit.api.EncodingType; +import com.knuddels.jtokkit.api.ModelType; +import lombok.extern.slf4j.Slf4j; + +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import com.xmzs.common.chat.entity.chat.FunctionCall; +import com.xmzs.common.chat.entity.chat.Message; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * 描述:token计算工具类 + * + * @author https:www.unfbx.com + * @since 2023-04-04 + */ +@Slf4j +public class TikTokensUtil { + /** + * 模型名称对应Encoding + */ + private static final Map modelMap = new HashMap<>(); + /** + * registry实例 + */ + private static final EncodingRegistry registry = Encodings.newDefaultEncodingRegistry(); + + static { + for (ModelType modelType : ModelType.values()) { + modelMap.put(modelType.getName(), registry.getEncodingForModel(modelType)); + } + modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO)); + modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO)); + modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO)); + modelMap.put(ChatCompletion.Model.GPT_4_32K.getName(), registry.getEncodingForModel(ModelType.GPT_4)); + modelMap.put(ChatCompletion.Model.GPT_4_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4)); + modelMap.put(ChatCompletion.Model.GPT_4_32K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4)); + modelMap.put(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4)); + modelMap.put(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4)); + } + + /** + * 通过Encoding和text获取编码数组 + * + * @param enc Encoding类型 + * @param text 文本信息 + * @return 编码数组 + */ + public static List encode(@NotNull Encoding enc, String text) { + return StrUtil.isBlank(text) ? new ArrayList<>() : enc.encode(text); + } + + /** + * 通过Encoding计算text信息的tokens + * + * @param enc Encoding类型 + * @param text 文本信息 + * @return tokens数量 + */ + public static int tokens(@NotNull Encoding enc, String text) { + return encode(enc, text).size(); + } + + + /** + * 通过Encoding和encoded数组反推text信息 + * + * @param enc Encoding + * @param encoded 编码数组 + * @return 编码数组对应的文本信息 + */ + public static String decode(@NotNull Encoding enc, @NotNull List encoded) { + return enc.decode(encoded); + } + + /** + * 获取一个Encoding对象,通过Encoding类型 + * + * @param encodingType encodingType + * @return Encoding + */ + public static Encoding getEncoding(@NotNull EncodingType encodingType) { + return registry.getEncoding(encodingType); + } + + /** + * 获取encode的编码数组 + * + * @param text 文本信息 + * @return 编码数组 + */ + public static List encode(@NotNull EncodingType encodingType, String text) { + if (StrUtil.isBlank(text)) { + return new ArrayList<>(); + } + Encoding enc = getEncoding(encodingType); + return enc.encode(text); + } + + /** + * 计算指定字符串的tokens,通过EncodingType + * + * @param encodingType encodingType + * @param text 文本信息 + * @return tokens数量 + */ + public static int tokens(@NotNull EncodingType encodingType, String text) { + return encode(encodingType, text).size(); + } + + + /** + * 通过EncodingType和encoded编码数组,反推字符串文本 + * + * @param encodingType encodingType + * @param encoded 编码数组 + * @return 编码数组对应的字符串 + */ + public static String decode(@NotNull EncodingType encodingType, @NotNull List encoded) { + Encoding enc = getEncoding(encodingType); + return enc.decode(encoded); + } + + + /** + * 获取一个Encoding对象,通过模型名称 + * + * @param modelName 模型名称 + * @return Encoding + */ + public static Encoding getEncoding(@NotNull String modelName) { + return modelMap.get(modelName); + } + + /** + * 获取encode的编码数组,通过模型名称 + * + * @param text 文本信息 + * @return 编码数组 + */ + public static List encode(@NotNull String modelName, String text) { + if (StrUtil.isBlank(text)) { + return new ArrayList<>(); + } + Encoding enc = getEncoding(modelName); + if (Objects.isNull(enc)) { + log.warn("[{}]模型不存在或者暂不支持计算tokens,直接返回tokens==0",modelName); + return new ArrayList<>(); + } + return enc.encode(text); + } + + /** + * 通过模型名称, 计算指定字符串的tokens + * + * @param modelName 模型名称 + * @param text 文本信息 + * @return tokens数量 + */ + public static int tokens(@NotNull String modelName, String text) { + return encode(modelName, text).size(); + } + + + /** + * 通过模型名称计算messages获取编码数组 + * 参考官方的处理逻辑: + * https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb + * + * @param modelName 模型名称 + * @param messages 消息体 + * @return tokens数量 + */ + public static int tokens(@NotNull String modelName, @NotNull List messages) { + Encoding encoding = getEncoding(modelName); + int tokensPerMessage = 0; + int tokensPerName = 0; + if (modelName.equals(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName()) + || modelName.equals(ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName()) + || modelName.equals(ChatCompletion.Model.GPT_4_0613.getName()) + || modelName.equals(ChatCompletion.Model.GPT_4_32K_0613.getName()) + || modelName.equals(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName()) + || modelName.equals(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName()) + ) { + tokensPerMessage = 3; + tokensPerName = 1; + }else if(modelName.contains(ChatCompletion.Model.GPT_3_5_TURBO.getName())){ + //"gpt-3.5-turbo" in model: + log.warn("Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613."); + tokensPerMessage = 3; + tokensPerName = 1; + }else if(modelName.contains(ChatCompletion.Model.GPT_4.getName())){ + log.warn("Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613."); + tokensPerMessage = 3; + tokensPerName = 1; + }else { + log.warn("不支持的model {}. See https://github.com/openai/openai-python/blob/main/chatml.md 更多信息.",modelName); + } + int sum = 0; + for (Message msg : messages) { + sum += tokensPerMessage; + sum += tokens(encoding, msg.getContent()); + sum += tokens(encoding, msg.getRole()); + sum += tokens(encoding, msg.getName()); + FunctionCall functionCall = msg.getFunctionCall(); + sum += Objects.isNull(functionCall) ? 0 : tokens(encoding, functionCall.toString()); + if (StrUtil.isNotBlank(msg.getName())) { + sum += tokensPerName; + } + } + sum += 3; + return sum; + } + + /** + * 通过模型名称和encoded编码数组,反推字符串文本 + * + * @param modelName 模型名 + * @param encoded 编码数组 + * @return 返回源文本 + */ + public static String decode(@NotNull String modelName, @NotNull List encoded) { + Encoding enc = getEncoding(modelName); + return enc.decode(encoded); + } + +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/WebSocketUtils.java b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/WebSocketUtils.java new file mode 100644 index 00000000..1e71341f --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/utils/WebSocketUtils.java @@ -0,0 +1,98 @@ +package com.xmzs.common.chat.utils; + +import cn.hutool.core.collection.CollUtil; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.chat.entity.dto.WebSocketMessageDto; +import com.xmzs.common.chat.holder.WebSocketSessionHolder; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.socket.PongMessage; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.WebSocketSession; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import static com.xmzs.common.chat.constant.WebSocketConstants.WEB_SOCKET_TOPIC; + +/** + * 工具类 + * + * @author zendwang + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class WebSocketUtils { + + /** + * 发送消息 + * + * @param sessionKey session主键 一般为用户id + * @param message 消息文本 + */ + public static void sendMessage(Long sessionKey, String message) { + WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey); + sendMessage(session, message); + } + + /** + * 订阅消息 + * + * @param consumer 自定义处理 + */ + public static void subscribeMessage(Consumer consumer) { + RedisUtils.subscribe(WEB_SOCKET_TOPIC, WebSocketMessageDto.class, consumer); + } + + /** + * 发布订阅的消息 + * + * @param webSocketMessage 消息对象 + */ + public static void publishMessage(WebSocketMessageDto webSocketMessage) { + List unsentSessionKeys = new ArrayList<>(); + // 当前服务内session,直接发送消息 + for (Long sessionKey : webSocketMessage.getSessionKeys()) { + if (WebSocketSessionHolder.existSession(sessionKey)) { + WebSocketUtils.sendMessage(sessionKey, webSocketMessage.getMessage()); + continue; + } + unsentSessionKeys.add(sessionKey); + } + // 不在当前服务内session,发布订阅消息 + if (CollUtil.isNotEmpty(unsentSessionKeys)) { + WebSocketMessageDto broadcastMessage = new WebSocketMessageDto(); + broadcastMessage.setMessage(webSocketMessage.getMessage()); + broadcastMessage.setSessionKeys(unsentSessionKeys); + RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> { + log.info(" WebSocket发送主题订阅消息topic:{} session keys:{} message:{}", + WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage()); + }); + } + } + + public static void sendPongMessage(WebSocketSession session) { + sendMessage(session, new PongMessage()); + } + + public static void sendMessage(WebSocketSession session, String message) { + sendMessage(session, new TextMessage(message)); + } + + private static void sendMessage(WebSocketSession session, WebSocketMessage message) { + if (session == null || !session.isOpen()) { + log.error("[send] session会话已经关闭"); + } else { + try { + // 获取当前会话中的用户 + session.sendMessage(message); + } catch (IOException e) { + log.error("[send] session({}) 发送消息({}) 异常", session, message, e); + } + } + } +} diff --git a/ruoyi-common/ruoyi-common-chat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-chat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..25060231 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.chat.config.WebSocketConfig diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml new file mode 100644 index 00000000..020c13ea --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -0,0 +1,129 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-core + + + ruoyi-common-core 核心模块 + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + jakarta.servlet + jakarta.servlet-api + + + + cn.hutool + hutool-core + + + + cn.hutool + hutool-http + + + + cn.hutool + hutool-extra + + + + cn.hutool + hutool-json + provided + + + + org.projectlombok + lombok + + + + + org.springframework.boot + spring-boot-configuration-processor + + + + org.springframework.boot + spring-boot-properties-migrator + runtime + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + + + + + org.lionsoul + ip2region + + + + + com.github.binarywang + weixin-java-miniapp + ${weixin-java-miniapp.version} + + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.squareup.okhttp3 + okhttp + + + + com.squareup.okhttp3 + okhttp-sse + + + + com.squareup.okhttp3 + logging-interceptor + + + + + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ApplicationConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ApplicationConfig.java new file mode 100644 index 00000000..96c5d3d5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ApplicationConfig.java @@ -0,0 +1,16 @@ +package com.xmzs.common.core.config; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author Lion Li + */ +@AutoConfiguration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +public class ApplicationConfig { + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/AsyncConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/AsyncConfig.java new file mode 100644 index 00000000..294a5165 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/AsyncConfig.java @@ -0,0 +1,54 @@ +package com.xmzs.common.core.config; + +import cn.hutool.core.util.ArrayUtil; +import com.xmzs.common.core.exception.ServiceException; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 异步配置 + * + * @author Lion Li + */ +@EnableAsync(proxyTargetClass = true) +@AutoConfiguration +public class AsyncConfig implements AsyncConfigurer { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + /** + * 自定义 @Async 注解使用系统线程池 + */ + @Override + public Executor getAsyncExecutor() { + return scheduledExecutorService; + } + + /** + * 异步执行异常处理 + */ + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + throwable.printStackTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Exception message - ").append(throwable.getMessage()) + .append(", Method name - ").append(method.getName()); + if (ArrayUtil.isNotEmpty(objects)) { + sb.append(", Parameter value - ").append(Arrays.toString(objects)); + } + throw new ServiceException(sb.toString()); + }; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/RuoYiConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/RuoYiConfig.java new file mode 100644 index 00000000..a3f5195f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/RuoYiConfig.java @@ -0,0 +1,49 @@ +package com.xmzs.common.core.config; + +import lombok.Data; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author Lion Li + */ + +@Data +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig { + + /** + * 项目名称 + */ + private String name; + + /** + * 版本 + */ + private String version; + + /** + * 版权年份 + */ + private String copyrightYear; + + /** + * 实例演示开关 + */ + private boolean demoEnabled; + + /** + * 获取地址开关 + */ + @Getter + private static boolean addressEnabled; + + public void setAddressEnabled(boolean addressEnabled) { + RuoYiConfig.addressEnabled = addressEnabled; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ThreadPoolConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ThreadPoolConfig.java new file mode 100644 index 00000000..e674ce11 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ThreadPoolConfig.java @@ -0,0 +1,57 @@ +package com.xmzs.common.core.config; + +import com.xmzs.common.core.config.properties.ThreadPoolProperties; +import com.xmzs.common.core.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author Lion Li + **/ +@AutoConfiguration +@EnableConfigurationProperties(ThreadPoolProperties.class) +public class ThreadPoolConfig { + + /** + * 核心线程数 = cpu 核心数 + 1 + */ + private final int core = Runtime.getRuntime().availableProcessors() + 1; + + @Bean(name = "threadPoolTaskExecutor") + @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") + public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(core); + executor.setMaxPoolSize(core * 2); + executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); + executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(core, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ValidatorConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ValidatorConfig.java new file mode 100644 index 00000000..a50c9da9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/ValidatorConfig.java @@ -0,0 +1,39 @@ +package com.xmzs.common.core.config; + +import jakarta.validation.Validator; +import org.hibernate.validator.HibernateValidator; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +import java.util.Properties; + +/** + * 校验框架配置类 + * + * @author Lion Li + */ +@AutoConfiguration +public class ValidatorConfig { + + /** + * 配置校验框架 快速返回模式 + */ + @Bean + public Validator validator(MessageSource messageSource) { + LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); + // 国际化 + factoryBean.setValidationMessageSource(messageSource); + // 设置使用 HibernateValidator 校验器 + factoryBean.setProviderClass(HibernateValidator.class); + Properties properties = new Properties(); + // 设置 快速异常返回 + properties.setProperty("hibernate.validator.fail_fast", "true"); + factoryBean.setValidationProperties(properties); + // 加载配置 + factoryBean.afterPropertiesSet(); + return factoryBean.getValidator(); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/properties/ThreadPoolProperties.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/properties/ThreadPoolProperties.java new file mode 100644 index 00000000..50366ace --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/config/properties/ThreadPoolProperties.java @@ -0,0 +1,30 @@ +package com.xmzs.common.core.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 线程池 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "thread-pool") +public class ThreadPoolProperties { + + /** + * 是否开启线程池 + */ + private boolean enabled; + + /** + * 队列最大长度 + */ + private int queueCapacity; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private int keepAliveSeconds; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheConstants.java new file mode 100644 index 00000000..13e0902e --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheConstants.java @@ -0,0 +1,25 @@ +package com.xmzs.common.core.constant; + +/** + * 缓存的key 常量 + * + * @author Lion Li + */ +public interface CacheConstants { + + /** + * 在线用户 redis key + */ + String ONLINE_TOKEN_KEY = "online_tokens:"; + + /** + * 参数管理 cache key + */ + String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + String SYS_DICT_KEY = "sys_dict:"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheNames.java new file mode 100644 index 00000000..b9b48d73 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/CacheNames.java @@ -0,0 +1,63 @@ +package com.xmzs.common.core.constant; + +/** + * 缓存组名称常量 + *

+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize + *

+ * ttl 过期时间 如果设置为0则不过期 默认为0 + * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 + * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + *

+ * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 + * + * @author Lion Li + */ +public interface CacheNames { + + /** + * 演示案例 + */ + String DEMO_CACHE = "demo:cache#60s#10m#20"; + + /** + * 系统配置 + */ + String SYS_CONFIG = "sys_config"; + + /** + * 数据字典 + */ + String SYS_DICT = "sys_dict"; + + /** + * 租户 + */ + String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; + + /** + * 用户账户 + */ + String SYS_USER_NAME = "sys_user_name#30d"; + + /** + * 部门 + */ + String SYS_DEPT = "sys_dept#30d"; + + /** + * OSS内容 + */ + String SYS_OSS = "sys_oss#30d"; + + /** + * OSS配置 + */ + String SYS_OSS_CONFIG = "sys_oss_config"; + + /** + * 在线用户 + */ + String ONLINE_TOKEN = "online_tokens"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/Constants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/Constants.java new file mode 100644 index 00000000..d107f7c1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/Constants.java @@ -0,0 +1,86 @@ +package com.xmzs.common.core.constant; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public interface Constants { + + /** + * UTF-8 字符集 + */ + String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + String GBK = "GBK"; + + /** + * www主域 + */ + String WWW = "www."; + + /** + * http请求 + */ + String HTTP = "http://"; + + /** + * https请求 + */ + String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + String FAIL = "1"; + + /** + * 登录成功 + */ + String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + String LOGOUT = "Logout"; + + /** + * 注册 + */ + String REGISTER = "Register"; + + /** + * 登录失败 + */ + String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + Integer CAPTCHA_EXPIRATION = 20; + + /** + * 令牌 + */ + String TOKEN = "token"; + + /** + * 顶级部门id + */ + Long TOP_PARENT_ID = 0L; + + /** + * 默认租户ID + **/ + String TENANT_ID = "00000"; + +} + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/GlobalConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/GlobalConstants.java new file mode 100644 index 00000000..adedb2a2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/GlobalConstants.java @@ -0,0 +1,34 @@ +package com.xmzs.common.core.constant; + +/** + * 全局的key常量 (业务无关的key) + * + * @author Lion Li + */ +public interface GlobalConstants { + + /** + * 全局 redis key (业务无关的key) + */ + String GLOBAL_REDIS_KEY = "global:"; + + /** + * 验证码 redis key + */ + String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:"; + + /** + * 防重提交 redis key + */ + String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:"; + + /** + * 限流 redis key + */ + String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:"; +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/HttpStatus.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/HttpStatus.java new file mode 100644 index 00000000..7e263c3f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.xmzs.common.core.constant; + +/** + * 返回状态码 + * + * @author Lion Li + */ +public interface HttpStatus { + /** + * 操作成功 + */ + int SUCCESS = 200; + + /** + * 对象创建成功 + */ + int CREATED = 201; + + /** + * 请求已经被接受 + */ + int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + int MOVED_PERM = 301; + + /** + * 重定向 + */ + int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + int BAD_REQUEST = 400; + + /** + * 未授权 + */ + int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + int ERROR = 500; + + /** + * 接口未实现 + */ + int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + int WARN = 601; +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/TenantConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/TenantConstants.java new file mode 100644 index 00000000..0c54462d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/TenantConstants.java @@ -0,0 +1,45 @@ +package com.xmzs.common.core.constant; + +/** + * 租户常量信息 + * + * @author Lion Li + */ +public interface TenantConstants { + + /** + * 租户正常状态 + */ + String NORMAL = "0"; + + /** + * 租户封禁状态 + */ + String DISABLE = "1"; + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 超级管理员角色 roleKey + */ + String SUPER_ADMIN_ROLE_KEY = "superadmin"; + + /** + * 租户管理员角色 roleKey + */ + String TENANT_ADMIN_ROLE_KEY = "admin"; + + /** + * 租户管理员角色名称 + */ + String TENANT_ADMIN_ROLE_NAME = "管理员"; + + /** + * 默认租户ID + */ + String DEFAULT_TENANT_ID = "000000"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/UserConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/UserConstants.java new file mode 100644 index 00000000..5eb5a9d1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/constant/UserConstants.java @@ -0,0 +1,132 @@ +package com.xmzs.common.core.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public interface UserConstants { + + /** + * 平台内系统用户的唯一标志 + */ + String SYS_USER = "SYS_USER"; + + /** + * 正常状态 + */ + String NORMAL = "0"; + + /** + * 异常状态 + */ + String EXCEPTION = "1"; + + /** + * 用户正常状态 + */ + String USER_NORMAL = "0"; + + /** + * 用户封禁状态 + */ + String USER_DISABLE = "1"; + + /** + * 角色正常状态 + */ + String ROLE_NORMAL = "0"; + + /** + * 角色封禁状态 + */ + String ROLE_DISABLE = "1"; + + /** + * 部门正常状态 + */ + String DEPT_NORMAL = "0"; + + /** + * 部门停用状态 + */ + String DEPT_DISABLE = "1"; + + /** + * 字典正常状态 + */ + String DICT_NORMAL = "0"; + + /** + * 是否为系统默认(是) + */ + String YES = "Y"; + + /** + * 是否菜单外链(是) + */ + String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + String NO_FRAME = "1"; + + /** + * 菜单正常状态 + */ + String MENU_NORMAL = "0"; + + /** + * 菜单停用状态 + */ + String MENU_DISABLE = "1"; + + /** + * 菜单类型(目录) + */ + String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + String INNER_LINK = "InnerLink"; + + /** + * 用户名长度限制 + */ + int USERNAME_MIN_LENGTH = 2; + int USERNAME_MAX_LENGTH = 100; + + /** + * 密码长度限制 + */ + int PASSWORD_MIN_LENGTH = 5; + int PASSWORD_MAX_LENGTH = 20; + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/R.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/R.java new file mode 100644 index 00000000..e614a862 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/R.java @@ -0,0 +1,110 @@ +package com.xmzs.common.core.domain; + +import com.xmzs.common.core.constant.HttpStatus; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class R implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(String msg, T data) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(String msg, T data) { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static R warn(String msg) { + return restResult(null, HttpStatus.WARN, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static R warn(String msg, T data) { + return restResult(data, HttpStatus.WARN, msg); + } + + private static R restResult(T data, int code, String msg) { + R r = new R<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + return r; + } + + public static Boolean isError(R ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/RoleDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/RoleDTO.java new file mode 100644 index 00000000..32713730 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/RoleDTO.java @@ -0,0 +1,38 @@ +package com.xmzs.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 角色 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class RoleDTO implements Serializable { + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) + */ + private String dataScope; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/UserOnlineDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/UserOnlineDTO.java new file mode 100644 index 00000000..deea41da --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/dto/UserOnlineDTO.java @@ -0,0 +1,62 @@ +package com.xmzs.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 当前在线会话 + * + * @author ruoyi + */ + +@Data +@NoArgsConstructor +public class UserOnlineDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/EmailLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/EmailLoginBody.java new file mode 100644 index 00000000..1c5553cd --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/EmailLoginBody.java @@ -0,0 +1,35 @@ +package com.xmzs.common.core.domain.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +public class EmailLoginBody { + + /** + * 租户ID + */ + @NotBlank(message = "{tenant.number.not.blank}") + private String tenantId; + + /** + * 邮箱 + */ + @NotBlank(message = "{user.email.not.blank}") + @Email(message = "{user.email.not.valid}") + private String email; + + /** + * 邮箱code + */ + @NotBlank(message = "{email.code.not.blank}") + private String emailCode; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginBody.java new file mode 100644 index 00000000..c36905cf --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginBody.java @@ -0,0 +1,47 @@ +package com.xmzs.common.core.domain.model; + +import com.xmzs.common.core.constant.UserConstants; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.NotBlank; + +/** + * 用户登录对象 + * + * @author Lion Li + */ + +@Data +public class LoginBody { + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户名 + */ + @NotBlank(message = "{user.username.not.blank}") + // @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") + private String username; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + // @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginUser.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginUser.java new file mode 100644 index 00000000..9fd909bd --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/LoginUser.java @@ -0,0 +1,133 @@ +package com.xmzs.common.core.domain.model; + +import com.xmzs.common.core.domain.dto.RoleDTO; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class LoginUser implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名 + */ + private String deptName; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户类型 + */ + private String userType; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 菜单权限 + */ + private Set menuPermission; + + /** + * 角色权限 + */ + private Set rolePermission; + + /** + * 用户名 + */ + private String username; + + /** + * 用户名 + */ + private String nickName; + + /** + * 微信头像 + */ + private String avatar; + + /** + * 角色对象 + */ + private List roles; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 获取登录id + */ + public String getLoginId() { + if (userType == null) { + throw new IllegalArgumentException("用户类型不能为空"); + } + if (userId == null) { + throw new IllegalArgumentException("用户ID不能为空"); + } + return userType + ":" + userId; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/RegisterBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/RegisterBody.java new file mode 100644 index 00000000..cb6ef6af --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/RegisterBody.java @@ -0,0 +1,17 @@ +package com.xmzs.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户注册对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RegisterBody extends LoginBody { + + private String userType; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/SmsLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/SmsLoginBody.java new file mode 100644 index 00000000..818098d5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/SmsLoginBody.java @@ -0,0 +1,34 @@ +package com.xmzs.common.core.domain.model; + +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +public class SmsLoginBody { + + /** + * 租户ID + */ + @NotBlank(message = "{tenant.number.not.blank}") + private String tenantId; + + /** + * 手机号 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + + /** + * 短信code + */ + @NotBlank(message = "{sms.code.not.blank}") + private String smsCode; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginBody.java new file mode 100644 index 00000000..94564b16 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginBody.java @@ -0,0 +1,25 @@ +package com.xmzs.common.core.domain.model; + +import lombok.Data; + +import java.io.Serial; + +/** + * 游客登录用户身份权限 + * + * @author Lion Li + */ +@Data +public class VisitorLoginBody { + + @Serial + private static final long serialVersionUID = 1L; + + private String code; + + /** + * 登录类型(1.小程序访客 2.pc访客) + */ + private String type; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginUser.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginUser.java new file mode 100644 index 00000000..e346a4a5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/domain/model/VisitorLoginUser.java @@ -0,0 +1,28 @@ +package com.xmzs.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 小程序登录用户身份权限 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class VisitorLoginUser extends LoginUser { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * openid + */ + private String openid; + + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/DeviceType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/DeviceType.java new file mode 100644 index 00000000..be899884 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/DeviceType.java @@ -0,0 +1,32 @@ +package com.xmzs.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * 针对一套 用户体系 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DeviceType { + + /** + * pc端 + */ + PC("pc"), + + /** + * app端 + */ + APP("app"), + + /** + * 小程序端 + */ + XCX("xcx"); + + private final String device; +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginType.java new file mode 100644 index 00000000..ec123eb9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginType.java @@ -0,0 +1,44 @@ +package com.xmzs.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum LoginType { + + /** + * 密码登录 + */ + PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), + + /** + * 短信登录 + */ + SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + + /** + * 邮箱登录 + */ + EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), + + /** + * 小程序登录 + */ + XCX("", ""); + + /** + * 登录重试超出限制提示 + */ + final String retryLimitExceed; + + /** + * 登录重试限制计数提示 + */ + final String retryLimitCount; +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginUserType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginUserType.java new file mode 100644 index 00000000..e0753edd --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/LoginUserType.java @@ -0,0 +1,21 @@ +package com.xmzs.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 游客登录类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum LoginUserType { + + PC("1", "PC端用户"), + + XCX("2", "小程序用户"); + + private final String code; + private final String content; +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/TenantStatus.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/TenantStatus.java new file mode 100644 index 00000000..7e7050ef --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/TenantStatus.java @@ -0,0 +1,30 @@ +package com.xmzs.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户状态 + * + * @author LionLi + */ +@Getter +@AllArgsConstructor +public enum TenantStatus { + /** + * 正常 + */ + OK("0", "正常"), + /** + * 停用 + */ + DISABLE("1", "停用"), + /** + * 删除 + */ + DELETED("2", "删除"); + + private final String code; + private final String info; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserStatus.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserStatus.java new file mode 100644 index 00000000..16fe3bc1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.xmzs.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户状态 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum UserStatus { + /** + * 正常 + */ + OK("0", "正常"), + /** + * 停用 + */ + DISABLE("1", "停用"), + /** + * 删除 + */ + DELETED("2", "删除"); + + private final String code; + private final String info; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserType.java new file mode 100644 index 00000000..6ebfba7d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/enums/UserType.java @@ -0,0 +1,37 @@ +package com.xmzs.common.core.enums; + +import com.xmzs.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * 针对多套 用户体系 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum UserType { + + /** + * pc端 + */ + SYS_USER("sys_user"), + + /** + * app端 + */ + APP_USER("app_user"); + + private final String userType; + + public static UserType getUserType(String str) { + for (UserType value : values()) { + if (StringUtils.contains(str, value.getUserType())) { + return value; + } + } + throw new RuntimeException("'UserType' not found By " + str); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/DemoModeException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/DemoModeException.java new file mode 100644 index 00000000..063bb4e4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/DemoModeException.java @@ -0,0 +1,17 @@ +package com.xmzs.common.core.exception; + +import java.io.Serial; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + public DemoModeException() { + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/GlobalException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/GlobalException.java new file mode 100644 index 00000000..0b5ef2ae --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/GlobalException.java @@ -0,0 +1,53 @@ +package com.xmzs.common.core.exception; + +import java.io.Serial; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + 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/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/ServiceException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/ServiceException.java new file mode 100644 index 00000000..ff85efbf --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/ServiceException.java @@ -0,0 +1,67 @@ +package com.xmzs.common.core.exception; + +import java.io.Serial; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + 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; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/UtilException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/UtilException.java new file mode 100644 index 00000000..e2455364 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.xmzs.common.core.exception; + +import java.io.Serial; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException { + + @Serial + 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/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/base/BaseException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/base/BaseException.java new file mode 100644 index 00000000..a92efa84 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/base/BaseException.java @@ -0,0 +1,79 @@ +package com.xmzs.common.core.exception.base; + +import com.xmzs.common.core.utils.MessageUtils; +import com.xmzs.common.core.utils.StringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 基础异常 + * + * @author ruoyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class BaseException extends RuntimeException { + + @Serial + 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); + } + + @Override + public String getMessage() { + String message = null; + if (!StringUtils.isEmpty(code)) { + message = MessageUtils.message(code, args); + } + if (message == null) { + message = defaultMessage; + } + return message; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileException.java new file mode 100644 index 00000000..eb559996 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileException.java @@ -0,0 +1,21 @@ +package com.xmzs.common.core.exception.file; + +import com.xmzs.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 00000000..905883fc --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 00000000..f39046ea --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaException.java new file mode 100644 index 00000000..1a4b885e --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaException.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaException() { + super("user.jcaptcha.error"); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaExpireException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 00000000..5b7193f6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() { + super("user.jcaptcha.expire"); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserException.java new file mode 100644 index 00000000..43c4b730 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserException.java @@ -0,0 +1,20 @@ +package com.xmzs.common.core.exception.user; + +import com.xmzs.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public UserException(String code, Object... args) { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 00000000..12970b0d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.exception.user; + +import java.io.Serial; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() { + super("user.password.not.match"); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 00000000..80f3674a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,19 @@ +package com.xmzs.common.core.exception.user; + +import java.io.Serial; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) { + super("user.password.retry.limit.exceed", retryLimitCount, lockTime); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/manager/ShutdownManager.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/manager/ShutdownManager.java new file mode 100644 index 00000000..9773f285 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/manager/ShutdownManager.java @@ -0,0 +1,41 @@ +package com.xmzs.common.core.manager; + +import com.xmzs.common.core.utils.Threads; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import jakarta.annotation.PreDestroy; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author Lion Li + */ +@Slf4j +@Component +public class ShutdownManager { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + @PreDestroy + public void destroy() { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() { + try { + log.info("====关闭后台任务任务线程池===="); + Threads.shutdownAndAwaitTermination(scheduledExecutorService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/ConfigService.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/ConfigService.java new file mode 100644 index 00000000..e8d9eacc --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/ConfigService.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.service; + +/** + * 通用 参数配置服务 + * + * @author Lion Li + */ +public interface ConfigService { + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + String getConfigValue(String configKey); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DeptService.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DeptService.java new file mode 100644 index 00000000..74be3b5f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DeptService.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.service; + +/** + * 通用 部门服务 + * + * @author Lion Li + */ +public interface DeptService { + + /** + * 通过部门ID查询部门名称 + * + * @param deptIds 部门ID串逗号分隔 + * @return 部门名称串逗号分隔 + */ + String selectDeptNameByIds(String deptIds); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DictService.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DictService.java new file mode 100644 index 00000000..8716ccf1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/DictService.java @@ -0,0 +1,57 @@ +package com.xmzs.common.core.service; + +/** + * 通用 字典服务 + * + * @author Lion Li + */ +public interface DictService { + + /** + * 分隔符 + */ + String SEPARATOR = ","; + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + default String getDictLabel(String dictType, String dictValue) { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + default String getDictValue(String dictType, String dictLabel) { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + String getDictLabel(String dictType, String dictValue, String separator); + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + String getDictValue(String dictType, String dictLabel, String separator); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/OssService.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/OssService.java new file mode 100644 index 00000000..f3524aa5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/OssService.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.service; + +/** + * 通用 OSS服务 + * + * @author Lion Li + */ +public interface OssService { + + /** + * 通过ossId查询对应的url + * + * @param ossIds ossId串逗号分隔 + * @return url串逗号分隔 + */ + String selectUrlByIds(String ossIds); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/UserService.java new file mode 100644 index 00000000..3bd2de59 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/service/UserService.java @@ -0,0 +1,18 @@ +package com.xmzs.common.core.service; + +/** + * 通用 用户服务 + * + * @author Lion Li + */ +public interface UserService { + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ + String selectUserNameById(Long userId); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/DateUtils.java new file mode 100644 index 00000000..35718dba --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/DateUtils.java @@ -0,0 +1,168 @@ +package com.xmzs.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; + +/** + * 时间工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + + public static final String YYYY = "yyyy"; + + public static final String YYYY_MM = "yyyy-MM"; + + public static final String YYYY_MM_DD = "yyyy-MM-dd"; + + public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static final String[] PARSE_PATTERNS = { + "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 String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static 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 String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static 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(), PARSE_PATTERNS); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.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/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/JsonUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/JsonUtils.java new file mode 100644 index 00000000..4827aa7b --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/JsonUtils.java @@ -0,0 +1,28 @@ +package com.xmzs.common.core.utils; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * @author Binary Wang + */ +public class JsonUtils { + private static final ObjectMapper JSON = new ObjectMapper(); + + static { + JSON.setSerializationInclusion(Include.NON_NULL); + JSON.configure(SerializationFeature.INDENT_OUTPUT, Boolean.TRUE); + } + + public static String toJson(Object obj) { + try { + return JSON.writeValueAsString(obj); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MapstructUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MapstructUtils.java new file mode 100644 index 00000000..0c5820bf --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MapstructUtils.java @@ -0,0 +1,92 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import io.github.linpeilie.Converter; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * Mapstruct 工具类 + *

参考文档:mapstruct-plus

+ * + * @author Michelle.Chung + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MapstructUtils { + + private final static Converter CONVERTER = SpringUtils.getBean(Converter.class); + + /** + * 将 T 类型对象,转换为 desc 类型的对象并返回 + * + * @param source 数据来源实体 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static V convert(T source, Class desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象 + * + * @param source 数据来源实体 + * @param desc 转换后的对象 + * @return desc + */ + public static V convert(T source, V desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型的集合,转换为 desc 类型的集合并返回 + * + * @param sourceList 数据来源实体列表 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static List convert(List sourceList, Class desc) { + if (ObjectUtil.isNull(sourceList)) { + return null; + } + if (CollUtil.isEmpty(sourceList)) { + return CollUtil.newArrayList(); + } + return CONVERTER.convert(sourceList, desc); + } + + /** + * 将 Map 转换为 beanClass 类型的集合并返回 + * + * @param map 数据来源 + * @param beanClass bean类 + * @return bean对象 + */ + public static T convert(Map map, Class beanClass) { + if (MapUtil.isEmpty(map)) { + return null; + } + if (ObjectUtil.isNull(beanClass)) { + return null; + } + return CONVERTER.convert(map, beanClass); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MessageUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MessageUtils.java new file mode 100644 index 00000000..c4936585 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/MessageUtils.java @@ -0,0 +1,28 @@ +package com.xmzs.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MessageUtils { + + private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class); + + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) { + return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ServletUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ServletUtils.java new file mode 100644 index 00000000..262b9efc --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ServletUtils.java @@ -0,0 +1,193 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.core.convert.Convert; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.http.HttpStatus; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ServletUtils extends JakartaServletUtil { + + /** + * 获取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(), StringUtils.SEPARATOR)); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + 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(MediaType.APPLICATION_JSON_VALUE)) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); + } + + public static String getClientIP() { + return getClientIP(getRequest()); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) { + return URLEncoder.encode(str, StandardCharsets.UTF_8); + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/SpringUtils.java new file mode 100644 index 00000000..d69af178 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/SpringUtils.java @@ -0,0 +1,62 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * spring工具类 + * + * @author Lion Li + */ +@Component +public final class SpringUtils extends SpringUtil { + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } + + /** + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } + + /** + * 获取aop代理对象 + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + + /** + * 获取spring上下文 + */ + public static ApplicationContext context() { + return getApplicationContext(); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StreamUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StreamUtils.java new file mode 100644 index 00000000..8f6161fa --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StreamUtils.java @@ -0,0 +1,254 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * stream 流工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StreamUtils { + + /** + * 将collection过滤 + * + * @param collection 需要转化的集合 + * @param function 过滤方法 + * @return 过滤后的list + */ + public static List filter(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + return collection.stream().filter(function).collect(Collectors.toList()); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function) { + return join(collection, function, StringUtils.SEPARATOR); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @param delimiter 拼接符 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function, CharSequence delimiter) { + if (CollUtil.isEmpty(collection)) { + return StringUtils.EMPTY; + } + return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); + } + + /** + * 将collection排序 + * + * @param collection 需要转化的集合 + * @param comparing 排序方法 + * @return 排序后的list + */ + public static List sorted(Collection collection, Comparator comparing) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + return collection.stream().sorted(comparing).collect(Collectors.toList()); + } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + */ + public static Map toIdentityMap(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + + /** + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + * + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + */ + public static Map toMap(Collection collection, Function key, Function value) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + */ + public static Map> groupByKey(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + */ + public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + */ + public static Map> group2Map(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + */ + public static List toList(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); + } + + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + */ + public static Set toSet(Collection collection, Function function) { + if (CollUtil.isEmpty(collection) || function == null) { + return CollUtil.newHashSet(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + + /** + * 合并两个相同key类型的map + * + * @param map1 第一个需要合并的 map + * @param map2 第二个需要合并的 map + * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况 + * @param map中的key类型 + * @param 第一个 map的value类型 + * @param 第二个 map的value类型 + * @param 最终map的value类型 + * @return 合并后的map + */ + public static Map merge(Map map1, Map map2, BiFunction merge) { + if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) { + return MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map1)) { + map1 = MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map2)) { + map2 = MapUtil.newHashMap(); + } + Set key = new HashSet<>(); + key.addAll(map1.keySet()); + key.addAll(map2.keySet()); + Map map = new HashMap<>(); + for (K t : key) { + X x = map1.get(t); + Y y = map2.get(t); + V z = merge.apply(x, y); + if (z != null) { + map.put(t, z); + } + } + return map; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StringUtils.java new file mode 100644 index 00000000..a225d704 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/StringUtils.java @@ -0,0 +1,321 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.util.AntPathMatcher; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 字符串工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + public static final String SEPARATOR = ","; + + /** + * 获取参数不为空值 + * + * @param str defaultValue 要判断的value + * @return value 返回值 + */ + public static String blankToDefault(String str, String defaultValue) { + return StrUtil.blankToDefault(str, defaultValue); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) { + return StrUtil.isEmpty(str); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * 去空格 + */ + public static String trim(String str) { + return StrUtil.trim(str); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) { + return substring(str, start, str.length()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) { + return StrUtil.sub(str, start, 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 template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) { + return StrUtil.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) { + return Validator.isUrl(link); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static Set str2Set(String str, String sep) { + return new HashSet<>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList<>(); + if (isEmpty(str)) { + return list; + } + + // 过滤空白字符串 + if (filterBlank && isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && isBlank(string)) { + continue; + } + if (trim) { + string = trim(string); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { + return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences); + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) { + return StrUtil.toUnderlineCase(str); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + return StrUtil.equalsAnyIgnoreCase(str, strs); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) { + return StrUtil.upperFirst(StrUtil.toCamelCase(name)); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) { + return StrUtil.toCamelCase(s); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || CollUtil.isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static 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 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) { + sb.append(String.valueOf(c).repeat(size - len)); + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + sb.append(String.valueOf(c).repeat(Math.max(0, size))); + } + return sb.toString(); + } + + /** + * 切分字符串(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @return 分割后的数据列表 + */ + public static List splitList(String str) { + return splitTo(str, Convert::toStr); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @return 分割后的数据列表 + */ + public static List splitList(String str, String separator) { + return splitTo(str, separator, Convert::toStr); + } + + /** + * 切分字符串自定义转换(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, Function mapper) { + return splitTo(str, SEPARATOR, mapper); + } + + /** + * 切分字符串自定义转换 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, String separator, Function mapper) { + if (isBlank(str)) { + return new ArrayList<>(0); + } + return StrUtil.split(str, separator) + .stream() + .filter(Objects::nonNull) + .map(mapper) + .collect(Collectors.toList()); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/Threads.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/Threads.java new file mode 100644 index 00000000..be9c255f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/Threads.java @@ -0,0 +1,75 @@ +package com.xmzs.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.*; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Threads { + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/TreeBuildUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/TreeBuildUtils.java new file mode 100644 index 00000000..6cec55b0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/TreeBuildUtils.java @@ -0,0 +1,35 @@ +package com.xmzs.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import cn.hutool.core.lang.tree.parser.NodeParser; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 扩展 hutool TreeUtil 封装系统树构建 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TreeBuildUtils extends TreeUtil { + + /** + * 根据前端定制差异化字段 + */ + public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); + + public static List> build(List list, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return null; + } + K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); + return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ValidatorUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ValidatorUtils.java new file mode 100644 index 00000000..9c94de2f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ValidatorUtils.java @@ -0,0 +1,28 @@ +package com.xmzs.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; +import java.util.Set; + +/** + * Validator 校验框架工具 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ValidatorUtils { + + private static final Validator VALID = SpringUtils.getBean(Validator.class); + + public static void validate(T object, Class... groups) { + Set> validate = VALID.validate(object, groups); + if (!validate.isEmpty()) { + throw new ConstraintViolationException("参数校验异常", validate); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/FileUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/FileUtils.java new file mode 100644 index 00000000..a21d3cbe --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/FileUtils.java @@ -0,0 +1,43 @@ +package com.xmzs.common.core.utils.file; + +import cn.hutool.core.io.FileUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileUtils extends FileUtil { + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) { + String percentEncodedFileName = percentEncode(realFileName); + String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/MimeTypeUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 00000000..403a1bf4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,40 @@ +package com.xmzs.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +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"}; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/AddressUtils.java new file mode 100644 index 00000000..ff86003d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/AddressUtils.java @@ -0,0 +1,33 @@ +package com.xmzs.common.core.utils.ip; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.http.HtmlUtil; +import com.xmzs.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 获取地址类 + * + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AddressUtils { + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) { + if (StringUtils.isBlank(ip)) { + return UNKNOWN; + } + // 内网不查询 + ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); + if (NetUtil.isInnerIP(ip)) { + return "内网IP"; + } + return RegionUtils.getCityInfo(ip); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/RegionUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/RegionUtils.java new file mode 100644 index 00000000..c618d93d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/ip/RegionUtils.java @@ -0,0 +1,67 @@ +package com.xmzs.common.core.utils.ip; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.file.FileUtils; +import lombok.extern.slf4j.Slf4j; +import org.lionsoul.ip2region.xdb.Searcher; + +import java.io.File; + +/** + * 根据ip地址定位工具类,离线方式 + * 参考地址:集成 ip2region 实现离线IP地址定位库 + * + * @author lishuyan + */ +@Slf4j +public class RegionUtils { + + private static final Searcher SEARCHER; + + static { + String fileName = "/ip2region.xdb"; + File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName); + if (!FileUtils.exist(existFile)) { + ClassPathResource fileStream = new ClassPathResource(fileName); + if (ObjectUtil.isEmpty(fileStream.getStream())) { + throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); + } + FileUtils.writeFromStream(fileStream.getStream(), existFile); + } + + String dbPath = existFile.getPath(); + + // 1、从 dbPath 加载整个 xdb 到内存。 + byte[] cBuff; + try { + cBuff = Searcher.loadContentFromFile(dbPath); + } catch (Exception e) { + throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage()); + } + // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 + try { + SEARCHER = Searcher.newWithBuffer(cBuff); + } catch (Exception e) { + throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); + } + } + + /** + * 根据IP地址离线获取城市 + */ + public static String getCityInfo(String ip) { + try { + ip = ip.trim(); + // 3、执行查询 + String region = SEARCHER.search(ip); + return region.replace("0|", "").replace("|0", ""); + } catch (Exception e) { + log.error("IP地址离线获取城市异常 {}", ip); + return "未知"; + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/reflect/ReflectUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 00000000..13392587 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,56 @@ +package com.xmzs.common.core.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import com.xmzs.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.lang.reflect.Method; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Lion Li + */ +@SuppressWarnings("rawtypes") +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用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 = invoke(object, getterMethodName); + } + 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 = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/sql/SqlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/sql/SqlUtil.java new file mode 100644 index 00000000..46fa9255 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,57 @@ +package com.xmzs.common.core.utils.sql; + +import com.xmzs.common.core.exception.UtilException; +import com.xmzs.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SqlUtil { + + /** + * 定义常用的 sql关键字 + */ + public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + 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/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/AddGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/AddGroup.java new file mode 100644 index 00000000..0fa2a72c --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/AddGroup.java @@ -0,0 +1,9 @@ +package com.xmzs.common.core.validate; + +/** + * 校验分组 add + * + * @author Lion Li + */ +public interface AddGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/EditGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/EditGroup.java new file mode 100644 index 00000000..85a780ec --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/EditGroup.java @@ -0,0 +1,9 @@ +package com.xmzs.common.core.validate; + +/** + * 校验分组 edit + * + * @author Lion Li + */ +public interface EditGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/QueryGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/QueryGroup.java new file mode 100644 index 00000000..89edfeea --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/validate/QueryGroup.java @@ -0,0 +1,9 @@ +package com.xmzs.common.core.validate; + +/** + * 校验分组 query + * + * @author Lion Li + */ +public interface QueryGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/Xss.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/Xss.java new file mode 100644 index 00000000..595a4248 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/Xss.java @@ -0,0 +1,26 @@ +package com.xmzs.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 Lion Li + */ +@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/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/XssValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/XssValidator.java new file mode 100644 index 00000000..902b900b --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/xmzs/common/core/xss/XssValidator.java @@ -0,0 +1,21 @@ +package com.xmzs.common.core.xss; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.http.HtmlUtil; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * 自定义xss校验注解实现 + * + * @author Lion Li + */ +public class XssValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..9e63be5b --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,6 @@ +com.xmzs.common.core.config.ApplicationConfig +com.xmzs.common.core.config.AsyncConfig +com.xmzs.common.core.config.RuoYiConfig +com.xmzs.common.core.config.ThreadPoolConfig +com.xmzs.common.core.config.ValidatorConfig +com.xmzs.common.core.utils.SpringUtils diff --git a/ruoyi-common/ruoyi-common-doc/pom.xml b/ruoyi-common/ruoyi-common-doc/pom.xml new file mode 100644 index 00000000..f2904cb8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/pom.xml @@ -0,0 +1,42 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-doc + + + ruoyi-common-doc 系统接口 + + + + + com.xmzs + ruoyi-common-core + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + + + + com.github.therapi + therapi-runtime-javadoc + + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + + diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/SwaggerConfig.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/SwaggerConfig.java new file mode 100644 index 00000000..19bb10d1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/SwaggerConfig.java @@ -0,0 +1,126 @@ +package com.xmzs.common.doc.config; + +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.doc.config.properties.SwaggerProperties; +import com.xmzs.common.doc.handler.OpenApiHandler; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.configuration.SpringDocConfiguration; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.service.SecurityService; +import org.springdoc.core.utils.PropertyResolverUtils; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Swagger 文档配置 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@AutoConfiguration(before = SpringDocConfiguration.class) +@EnableConfigurationProperties(SwaggerProperties.class) +@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true) +public class SwaggerConfig { + + private final ServerProperties serverProperties; + + @Bean + @ConditionalOnMissingBean(OpenAPI.class) + public OpenAPI openApi(SwaggerProperties swaggerProperties) { + OpenAPI openApi = new OpenAPI(); + // 文档基本信息 + SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo(); + Info info = convertInfo(infoProperties); + openApi.info(info); + // 扩展文档信息 + openApi.externalDocs(swaggerProperties.getExternalDocs()); + openApi.tags(swaggerProperties.getTags()); + openApi.paths(swaggerProperties.getPaths()); + openApi.components(swaggerProperties.getComponents()); + Set keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet(); + List list = new ArrayList<>(); + SecurityRequirement securityRequirement = new SecurityRequirement(); + keySet.forEach(securityRequirement::addList); + list.add(securityRequirement); + openApi.security(list); + + return openApi; + } + + private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) { + Info info = new Info(); + info.setTitle(infoProperties.getTitle()); + info.setDescription(infoProperties.getDescription()); + info.setContact(infoProperties.getContact()); + info.setLicense(infoProperties.getLicense()); + info.setVersion(infoProperties.getVersion()); + return info; + } + + /** + * 自定义 openapi 处理器 + */ + @Bean + public OpenAPIService openApiBuilder(Optional openAPI, + SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers, Optional javadocProvider) { + return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); + } + + /** + * 对已经生成好的 OpenApi 进行自定义操作 + */ + @Bean + public OpenApiCustomizer openApiCustomizer() { + String contextPath = serverProperties.getServlet().getContextPath(); + String finalContextPath; + if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) { + finalContextPath = ""; + } else { + finalContextPath = contextPath; + } + // 对所有路径增加前置上下文路径 + return openApi -> { + Paths oldPaths = openApi.getPaths(); + if (oldPaths instanceof PlusPaths) { + return; + } + PlusPaths newPaths = new PlusPaths(); + oldPaths.forEach((k, v) -> newPaths.addPathItem(finalContextPath + k, v)); + openApi.setPaths(newPaths); + }; + } + + /** + * 单独使用一个类便于判断 解决springdoc路径拼接重复问题 + * + * @author Lion Li + */ + static class PlusPaths extends Paths { + + public PlusPaths() { + super(); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/properties/SwaggerProperties.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/properties/SwaggerProperties.java new file mode 100644 index 00000000..b94519c4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/config/properties/SwaggerProperties.java @@ -0,0 +1,94 @@ +package com.xmzs.common.doc.config.properties; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +import java.util.List; + +/** + * swagger 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "swagger") +public class SwaggerProperties { + + /** + * 文档基本信息 + */ + @NestedConfigurationProperty + private InfoProperties info = new InfoProperties(); + + /** + * 扩展文档地址 + */ + @NestedConfigurationProperty + private ExternalDocumentation externalDocs; + + /** + * 标签 + */ + private List tags = null; + + /** + * 路径 + */ + @NestedConfigurationProperty + private Paths paths = null; + + /** + * 组件 + */ + @NestedConfigurationProperty + private Components components = null; + + /** + *

+ * 文档的基础属性信息 + *

+ * + * @see io.swagger.v3.oas.models.info.Info + * + * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 + */ + @Data + public static class InfoProperties { + + /** + * 标题 + */ + private String title = null; + + /** + * 描述 + */ + private String description = null; + + /** + * 联系人信息 + */ + @NestedConfigurationProperty + private Contact contact = null; + + /** + * 许可证 + */ + @NestedConfigurationProperty + private License license = null; + + /** + * 版本 + */ + private String version = null; + + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/handler/OpenApiHandler.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/handler/OpenApiHandler.java new file mode 100644 index 00000000..ddf888ff --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/xmzs/common/doc/handler/OpenApiHandler.java @@ -0,0 +1,252 @@ +package com.xmzs.common.doc.handler; + +import cn.hutool.core.io.IoUtil; +import io.swagger.v3.core.jackson.TypeNameResolver; +import io.swagger.v3.core.util.AnnotationsUtils; +import io.swagger.v3.oas.annotations.tags.Tags; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.service.SecurityService; +import org.springdoc.core.utils.PropertyResolverUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.method.HandlerMethod; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 自定义 openapi 处理器 + * 对源码功能进行修改 增强使用 + */ +@Slf4j +@SuppressWarnings("all") +public class OpenApiHandler extends OpenAPIService { + + /** + * The Basic error controller. + */ + private static Class basicErrorController; + + /** + * The Security parser. + */ + private final SecurityService securityParser; + + /** + * The Mappings map. + */ + private final Map mappingsMap = new HashMap<>(); + + /** + * The Springdoc tags. + */ + private final Map springdocTags = new HashMap<>(); + + /** + * The Open api builder customisers. + */ + private final Optional> openApiBuilderCustomisers; + + /** + * The server base URL customisers. + */ + private final Optional> serverBaseUrlCustomizers; + + /** + * The Spring doc config properties. + */ + private final SpringDocConfigProperties springDocConfigProperties; + + /** + * The Cached open api map. + */ + private final Map cachedOpenAPI = new HashMap<>(); + + /** + * The Property resolver utils. + */ + private final PropertyResolverUtils propertyResolverUtils; + + /** + * The javadoc provider. + */ + private final Optional javadocProvider; + + /** + * The Context. + */ + private ApplicationContext context; + + /** + * The Open api. + */ + private OpenAPI openAPI; + + /** + * The Is servers present. + */ + private boolean isServersPresent; + + /** + * The Server base url. + */ + private String serverBaseUrl; + + /** + * Instantiates a new Open api builder. + * + * @param openAPI the open api + * @param securityParser the security parser + * @param springDocConfigProperties the spring doc config properties + * @param propertyResolverUtils the property resolver utils + * @param openApiBuilderCustomizers the open api builder customisers + * @param serverBaseUrlCustomizers the server base url customizers + * @param javadocProvider the javadoc provider + */ + public OpenApiHandler(Optional openAPI, SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomizers, + Optional> serverBaseUrlCustomizers, + Optional javadocProvider) { + super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); + if (openAPI.isPresent()) { + this.openAPI = openAPI.get(); + if (this.openAPI.getComponents() == null) + this.openAPI.setComponents(new Components()); + if (this.openAPI.getPaths() == null) + this.openAPI.setPaths(new Paths()); + if (!CollectionUtils.isEmpty(this.openAPI.getServers())) + this.isServersPresent = true; + } + this.propertyResolverUtils = propertyResolverUtils; + this.securityParser = securityParser; + this.springDocConfigProperties = springDocConfigProperties; + this.openApiBuilderCustomisers = openApiBuilderCustomizers; + this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; + this.javadocProvider = javadocProvider; + if (springDocConfigProperties.isUseFqn()) + TypeNameResolver.std.setUseFqn(true); + } + + @Override + public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { + + Set tags = new HashSet<>(); + Set tagsStr = new HashSet<>(); + + buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); + buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); + + if (!CollectionUtils.isEmpty(tagsStr)) + tagsStr = tagsStr.stream() + .map(str -> propertyResolverUtils.resolve(str, locale)) + .collect(Collectors.toSet()); + + if (springdocTags.containsKey(handlerMethod)) { + io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); + tagsStr.add(tag.getName()); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + + if (!CollectionUtils.isEmpty(tagsStr)) { + if (CollectionUtils.isEmpty(operation.getTags())) + operation.setTags(new ArrayList<>(tagsStr)); + else { + Set operationTagsSet = new HashSet<>(operation.getTags()); + operationTagsSet.addAll(tagsStr); + operation.getTags().clear(); + operation.getTags().addAll(operationTagsSet); + } + } + + if (isAutoTagClasses(operation)) { + + + if (javadocProvider.isPresent()) { + String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); + if (StringUtils.isNotBlank(description)) { + io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); + + // 自定义部分 修改使用java注释当tag名 + List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); + // tag.setName(tagAutoName); + tag.setName(list.get(0)); + operation.addTagsItem(list.get(0)); + + tag.setDescription(description); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + } else { + String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); + operation.addTagsItem(tagAutoName); + } + } + + if (!CollectionUtils.isEmpty(tags)) { + // Existing tags + List openApiTags = openAPI.getTags(); + if (!CollectionUtils.isEmpty(openApiTags)) + tags.addAll(openApiTags); + openAPI.setTags(new ArrayList<>(tags)); + } + + // Handle SecurityRequirement at operation level + io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser + .getSecurityRequirements(handlerMethod); + if (securityRequirements != null) { + if (securityRequirements.length == 0) + operation.setSecurity(Collections.emptyList()); + else + securityParser.buildSecurityRequirement(securityRequirements, operation); + } + + return operation; + } + + private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { + // method tags + Set tagsSet = AnnotatedElementUtils + .findAllMergedAnnotations(method, Tags.class); + Set methodTags = tagsSet.stream() + .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); + methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); + if (!CollectionUtils.isEmpty(methodTags)) { + tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); + List allTags = new ArrayList<>(methodTags); + addTags(allTags, tags, locale); + } + } + + private void addTags(List sourceTags, Set tags, Locale locale) { + Optional> optionalTagSet = AnnotationsUtils + .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); + optionalTagSet.ifPresent(tagsSet -> { + tagsSet.forEach(tag -> { + tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); + tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); + if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) + tags.add(tag); + }); + }); + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..11e07a44 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.doc.config.SwaggerConfig diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml new file mode 100644 index 00000000..d3265d6a --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml @@ -0,0 +1,43 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-encrypt + + + ruoyi-common-encrypt 数据加解密模块 + + + + + + com.xmzs + ruoyi-common-core + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + org.bouncycastle + bcprov-jdk15to18 + + + + cn.hutool + hutool-crypto + + + + + diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/annotation/EncryptField.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/annotation/EncryptField.java new file mode 100644 index 00000000..536a07bf --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/annotation/EncryptField.java @@ -0,0 +1,44 @@ +package com.xmzs.common.encrypt.annotation; + +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +import java.lang.annotation.*; + +/** + * 字段加密注解 + * + * @author 老马 + */ +@Documented +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EncryptField { + + /** + * 加密算法 + */ + AlgorithmType algorithm() default AlgorithmType.DEFAULT; + + /** + * 秘钥。AES、SM4需要 + */ + String password() default ""; + + /** + * 公钥。RSA、SM2需要 + */ + String publicKey() default ""; + + /** + * 公钥。RSA、SM2需要 + */ + String privateKey() default ""; + + /** + * 编码方式。对加密算法为BASE64的不起作用 + */ + EncodeType encode() default EncodeType.DEFAULT; + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/config/EncryptorAutoConfiguration.java new file mode 100644 index 00000000..3d7f0930 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/config/EncryptorAutoConfiguration.java @@ -0,0 +1,41 @@ +package com.xmzs.common.encrypt.config; + +import com.xmzs.common.encrypt.core.EncryptorManager; +import com.xmzs.common.encrypt.interceptor.MybatisDecryptInterceptor; +import com.xmzs.common.encrypt.interceptor.MybatisEncryptInterceptor; +import com.xmzs.common.encrypt.properties.EncryptorProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * 加解密配置 + * + * @author 老马 + * @version 4.6.0 + */ +@AutoConfiguration +@EnableConfigurationProperties(EncryptorProperties.class) +@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") +public class EncryptorAutoConfiguration { + + @Autowired + private EncryptorProperties properties; + + @Bean + public EncryptorManager encryptorManager() { + return new EncryptorManager(); + } + + @Bean + public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisEncryptInterceptor(encryptorManager, properties); + } + + @Bean + public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisDecryptInterceptor(encryptorManager, properties); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptContext.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptContext.java new file mode 100644 index 00000000..9dc9ae51 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptContext.java @@ -0,0 +1,41 @@ +package com.xmzs.common.encrypt.core; + +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; +import lombok.Data; + +/** + * 加密上下文 用于encryptor传递必要的参数。 + * + * @author 老马 + * @version 4.6.0 + */ +@Data +public class EncryptContext { + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptorManager.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptorManager.java new file mode 100644 index 00000000..088fb5bc --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/EncryptorManager.java @@ -0,0 +1,94 @@ +package com.xmzs.common.encrypt.core; + +import cn.hutool.core.util.ReflectUtil; +import com.xmzs.common.encrypt.annotation.EncryptField; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 加密管理类 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +public class EncryptorManager { + + /** + * 缓存加密器 + */ + Map encryptorMap = new ConcurrentHashMap<>(); + + /** + * 类加密字段缓存 + */ + Map, Set> fieldCache = new ConcurrentHashMap<>(); + + /** + * 获取类加密字段缓存 + */ + public Set getFieldCache(Class sourceClazz) { + return fieldCache.computeIfAbsent(sourceClazz, clazz -> { + Field[] declaredFields = clazz.getDeclaredFields(); + Set fieldSet = Arrays.stream(declaredFields).filter(field -> + field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) + .collect(Collectors.toSet()); + for (Field field : fieldSet) { + field.setAccessible(true); + } + return fieldSet; + }); + } + + /** + * 注册加密执行者到缓存 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { + if (encryptorMap.containsKey(encryptContext)) { + return encryptorMap.get(encryptContext); + } + IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); + encryptorMap.put(encryptContext, encryptor); + return encryptor; + } + + /** + * 移除缓存中的加密执行者 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public void removeEncryptor(EncryptContext encryptContext) { + this.encryptorMap.remove(encryptContext); + } + + /** + * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。 + * + * @param value 待加密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String encrypt(String value, EncryptContext encryptContext) { + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + return encryptor.encrypt(value, encryptContext.getEncode()); + } + + /** + * 根据配置进行解密 + * + * @param value 待解密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String decrypt(String value, EncryptContext encryptContext) { + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + return encryptor.decrypt(value); + } + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/IEncryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/IEncryptor.java new file mode 100644 index 00000000..f9fef582 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/IEncryptor.java @@ -0,0 +1,35 @@ +package com.xmzs.common.encrypt.core; + +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +/** + * 加解者 + * + * @author 老马 + * @version 4.6.0 + */ +public interface IEncryptor { + + /** + * 获得当前算法 + */ + AlgorithmType algorithm(); + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + * @return 加密后的字符串 + */ + String encrypt(String value, EncodeType encodeType); + + /** + * 解密 + * + * @param value 待加密字符串 + * @return 解密后的字符串 + */ + String decrypt(String value); +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AbstractEncryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AbstractEncryptor.java new file mode 100644 index 00000000..6f6c7400 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AbstractEncryptor.java @@ -0,0 +1,18 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.core.IEncryptor; + +/** + * 所有加密执行者的基类 + * + * @author 老马 + * @version 4.6.0 + */ +public abstract class AbstractEncryptor implements IEncryptor { + + public AbstractEncryptor(EncryptContext context) { + // 用户配置校验与配置注入 + } + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AesEncryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AesEncryptor.java new file mode 100644 index 00000000..02957ba7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/AesEncryptor.java @@ -0,0 +1,69 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +import java.nio.charset.StandardCharsets; + +/** + * AES算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class AesEncryptor extends AbstractEncryptor { + + private final AES aes; + + public AesEncryptor(EncryptContext context) { + super(context); + String password = context.getPassword(); + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES没有获得秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位"); + } + aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8)); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.AES; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return aes.encryptHex(value); + } else { + return aes.encryptBase64(value); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return this.aes.decryptStr(value); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Base64Encryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Base64Encryptor.java new file mode 100644 index 00000000..f4567e00 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Base64Encryptor.java @@ -0,0 +1,48 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import cn.hutool.core.codec.Base64; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +/** + * Base64算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Base64Encryptor extends AbstractEncryptor { + + public Base64Encryptor(EncryptContext context) { + super(context); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.BASE64; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + return Base64.encode(value); + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return Base64.decodeStr(value); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/RsaEncryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/RsaEncryptor.java new file mode 100644 index 00000000..f21a8540 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/RsaEncryptor.java @@ -0,0 +1,65 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + + +/** + * RSA算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class RsaEncryptor extends AbstractEncryptor { + + private final RSA rsa; + + public RsaEncryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。"); + } + this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey)); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.RSA; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return rsa.encryptHex(value, KeyType.PublicKey); + } else { + return rsa.encryptBase64(value, KeyType.PublicKey); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return this.rsa.decryptStr(value, KeyType.PrivateKey); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm2Encryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm2Encryptor.java new file mode 100644 index 00000000..149a65f6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm2Encryptor.java @@ -0,0 +1,64 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.SM2; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +/** + * sm2算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Sm2Encryptor extends AbstractEncryptor { + + private final SM2 sm2; + + public Sm2Encryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。"); + } + this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey)); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM2; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return sm2.encryptHex(value, KeyType.PublicKey); + } else { + return sm2.encryptBase64(value, KeyType.PublicKey); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return this.sm2.decryptStr(value, KeyType.PrivateKey); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm4Encryptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm4Encryptor.java new file mode 100644 index 00000000..f2374ace --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/core/encryptor/Sm4Encryptor.java @@ -0,0 +1,67 @@ +package com.xmzs.common.encrypt.core.encryptor; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.symmetric.SM4; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; + +import java.nio.charset.StandardCharsets; + +/** + * sm4算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Sm4Encryptor extends AbstractEncryptor { + + private final SM4 sm4; + + public Sm4Encryptor(EncryptContext context) { + super(context); + String password = context.getPassword(); + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4没有获得秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + if (16 != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位"); + } + this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM4; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return sm4.encryptHex(value); + } else { + return sm4.encryptBase64(value); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return this.sm4.decryptStr(value); + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/AlgorithmType.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/AlgorithmType.java new file mode 100644 index 00000000..fe90715c --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/AlgorithmType.java @@ -0,0 +1,48 @@ +package com.xmzs.common.encrypt.enumd; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.xmzs.common.encrypt.core.encryptor.*; + +/** + * 算法名称 + * + * @author 老马 + * @version 4.6.0 + */ +@Getter +@AllArgsConstructor +public enum AlgorithmType { + + /** + * 默认走yml配置 + */ + DEFAULT(null), + + /** + * base64 + */ + BASE64(Base64Encryptor.class), + + /** + * aes + */ + AES(AesEncryptor.class), + + /** + * rsa + */ + RSA(RsaEncryptor.class), + + /** + * sm2 + */ + SM2(Sm2Encryptor.class), + + /** + * sm4 + */ + SM4(Sm4Encryptor.class); + + private final Class clazz; +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/EncodeType.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/EncodeType.java new file mode 100644 index 00000000..e41dcd0c --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/enumd/EncodeType.java @@ -0,0 +1,26 @@ +package com.xmzs.common.encrypt.enumd; + +/** + * 编码类型 + * + * @author 老马 + * @version 4.6.0 + */ +public enum EncodeType { + + /** + * 默认使用yml配置 + */ + DEFAULT, + + /** + * base64编码 + */ + BASE64, + + /** + * 16进制编码 + */ + HEX; + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisDecryptInterceptor.java new file mode 100644 index 00000000..c64465d6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -0,0 +1,115 @@ +package com.xmzs.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.resultset.ResultSetHandler; +import org.apache.ibatis.plugin.*; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.encrypt.annotation.EncryptField; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.core.EncryptorManager; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; +import com.xmzs.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.Statement; +import java.util.*; + +/** + * 出参解密拦截器 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ResultSetHandler.class, + method = "handleResultSets", + args = {Statement.class}) +}) +@AllArgsConstructor +public class MybatisDecryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + // 获取执行mysql执行结果 + Object result = invocation.proceed(); + if (result == null) { + return null; + } + decryptHandler(result); + return result; + } + + /** + * 解密对象 + * + * @param sourceObject 待加密对象 + */ + private void decryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::decryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::decryptHandler); + return; + } + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + try { + for (Field field : fields) { + field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理解密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String decryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.decrypt(value, encryptContext); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisEncryptInterceptor.java new file mode 100644 index 00000000..c79baadf --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -0,0 +1,119 @@ +package com.xmzs.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.encrypt.annotation.EncryptField; +import com.xmzs.common.encrypt.core.EncryptContext; +import com.xmzs.common.encrypt.core.EncryptorManager; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; +import com.xmzs.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.PreparedStatement; +import java.util.*; + +/** + * 入参加密拦截器 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ParameterHandler.class, + method = "setParameters", + args = {PreparedStatement.class}) +}) +@AllArgsConstructor +public class MybatisEncryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + return invocation; + } + + @Override + public Object plugin(Object target) { + if (target instanceof ParameterHandler parameterHandler) { + // 进行加密操作 + Object parameterObject = parameterHandler.getParameterObject(); + if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { + this.encryptHandler(parameterObject); + } + } + return target; + } + + /** + * 加密对象 + * + * @param sourceObject 待加密对象 + */ + private void encryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::encryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::encryptHandler); + return; + } + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + try { + for (Field field : fields) { + field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理加密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String encryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.encrypt(value, encryptContext); + } + + + @Override + public void setProperties(Properties properties) { + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/properties/EncryptorProperties.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/properties/EncryptorProperties.java new file mode 100644 index 00000000..ab67a6dc --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/properties/EncryptorProperties.java @@ -0,0 +1,48 @@ +package com.xmzs.common.encrypt.properties; + +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import com.xmzs.common.encrypt.enumd.EncodeType; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 加解密属性配置类 + * + * @author 老马 + * @version 4.6.0 + */ +@Data +@ConfigurationProperties(prefix = "mybatis-encryptor") +public class EncryptorProperties { + + /** + * 过滤开关 + */ + private Boolean enable; + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/utils/EncryptUtils.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/utils/EncryptUtils.java new file mode 100644 index 00000000..bc92cc2c --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/com/xmzs/common/encrypt/utils/EncryptUtils.java @@ -0,0 +1,243 @@ +package com.xmzs.common.encrypt.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.asymmetric.SM2; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * 安全相关工具类 + * + * @author 老马 + */ +public class EncryptUtils { + /** + * 公钥 + */ + public static final String PUBLIC_KEY = "publicKey"; + /** + * 私钥 + */ + public static final String PRIVATE_KEY = "privateKey"; + + /** + * Base64加密 + * + * @param data 待加密数据 + * @return 加密后字符串 + */ + public static String encryptByBase64(String data) { + return Base64.encode(data, StandardCharsets.UTF_8); + } + + /** + * Base64解密 + * + * @param data 待解密数据 + * @return 解密后字符串 + */ + public static String decryptByBase64(String data) { + return Base64.decodeStr(data, StandardCharsets.UTF_8); + } + + /** + * AES加密 + * + * @param data 待解密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * AES解密 + * + * @param data 待解密数据 + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * sm4加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * sm4解密 + * + * @param data 待解密数据 + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * 产生sm2加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateSm2Key() { + Map keyMap = new HashMap<>(2); + SM2 sm2 = SmUtil.sm2(); + keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); + return keyMap; + } + + /** + * sm2公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm2(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("SM2需要传入公钥进行加密"); + } + SM2 sm2 = SmUtil.sm2(null, publicKey); + return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * sm2私钥解密 + * + * @param data 待加密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptBySm2(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("SM2需要传入私钥进行解密"); + } + SM2 sm2 = SmUtil.sm2(privateKey, null); + return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * 产生RSA加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateRsaKey() { + Map keyMap = new HashMap<>(2); + RSA rsa = SecureUtil.rsa(); + keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64()); + return keyMap; + } + + /** + * rsa公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByRsa(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("RSA需要传入公钥进行加密"); + } + RSA rsa = SecureUtil.rsa(null, publicKey); + return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * rsa私钥解密 + * + * @param data 待加密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptByRsa(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("RSA需要传入私钥进行解密"); + } + RSA rsa = SecureUtil.rsa(privateKey, null); + return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * md5加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByMd5(String data) { + return SecureUtil.md5(data); + } + + /** + * sha256加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySha256(String data) { + return SecureUtil.sha256(data); + } + + /** + * sm3加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm3(String data) { + return SmUtil.sm3(data); + } + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..9a15a755 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.encrypt.config.EncryptorAutoConfiguration diff --git a/ruoyi-common/ruoyi-common-excel/pom.xml b/ruoyi-common/ruoyi-common-excel/pom.xml new file mode 100644 index 00000000..cffda703 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/pom.xml @@ -0,0 +1,31 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-excel + + + ruoyi-common-excel + + + + + com.xmzs + ruoyi-common-json + + + + com.alibaba + easyexcel + + + + diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/CellMerge.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/CellMerge.java new file mode 100644 index 00000000..6893a689 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/CellMerge.java @@ -0,0 +1,24 @@ +package com.xmzs.common.excel.annotation; + +import com.xmzs.common.excel.core.CellMergeStrategy; + +import java.lang.annotation.*; + +/** + * excel 列单元格合并(合并列相同项) + * + * 需搭配 {@link CellMergeStrategy} 策略使用 + * + * @author Lion Li + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CellMerge { + + /** + * col index + */ + int index() default -1; + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelDictFormat.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelDictFormat.java new file mode 100644 index 00000000..2458d3d5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelDictFormat.java @@ -0,0 +1,32 @@ +package com.xmzs.common.excel.annotation; + +import com.xmzs.common.core.utils.StringUtils; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * @author Lion Li + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelDictFormat { + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + String separator() default StringUtils.SEPARATOR; + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelEnumFormat.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelEnumFormat.java new file mode 100644 index 00000000..b0822954 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/annotation/ExcelEnumFormat.java @@ -0,0 +1,30 @@ +package com.xmzs.common.excel.annotation; + +import java.lang.annotation.*; + +/** + * 枚举格式化 + * + * @author Liang + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelEnumFormat { + + /** + * 字典枚举类型 + */ + Class> enumClass(); + + /** + * 字典枚举类中对应的code属性名称,默认为code + */ + String codeField() default "code"; + + /** + * 字典枚举类中对应的text属性名称,默认为text + */ + String textField() default "text"; + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelBigNumberConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelBigNumberConvert.java new file mode 100644 index 00000000..f3a61199 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelBigNumberConvert.java @@ -0,0 +1,52 @@ +package com.xmzs.common.excel.convert; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; + +/** + * 大数值转换 + * Excel 数值长度位15位 大于15位的数值转换位字符串 + * + * @author Lion Li + */ +@Slf4j +public class ExcelBigNumberConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return Convert.toLong(cellData.getData()); + } + + @Override + public WriteCellData convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNotNull(object)) { + String str = Convert.toStr(object); + if (str.length() > 15) { + return new WriteCellData<>(str); + } + } + WriteCellData cellData = new WriteCellData<>(new BigDecimal(object)); + cellData.setType(CellDataTypeEnum.NUMBER); + return cellData; + } + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelDictConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelDictConvert.java new file mode 100644 index 00000000..1136c78c --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelDictConvert.java @@ -0,0 +1,73 @@ +package com.xmzs.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.core.service.DictService; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.excel.utils.ExcelUtil; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; + +/** + * 字典格式化转换处理 + * + * @author Lion Li + */ +@Slf4j +public class ExcelDictConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String label = cellData.getStringValue(); + String value; + if (StringUtils.isBlank(type)) { + value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); + } else { + value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); + } + return Convert.convert(contentProperty.getField().getType(), value); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String value = Convert.toStr(object); + String label; + if (StringUtils.isBlank(type)) { + label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); + } else { + label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); + } + return new WriteCellData<>(label); + } + + private ExcelDictFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelEnumConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelEnumConvert.java new file mode 100644 index 00000000..c7f48e27 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/convert/ExcelEnumConvert.java @@ -0,0 +1,75 @@ +package com.xmzs.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import com.xmzs.common.excel.annotation.ExcelEnumFormat; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 枚举格式化转换处理 + * + * @author Liang + */ +@Slf4j +public class ExcelEnumConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + Object codeValue = cellData.getData(); + // 如果是空值 + if (ObjectUtil.isNull(codeValue)) { + return null; + } + Map enumValueMap = beforeConvert(contentProperty); + String textValue = enumValueMap.get(codeValue); + return Convert.convert(contentProperty.getField().getType(), textValue); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + Map enumValueMap = beforeConvert(contentProperty); + String value = Convert.toStr(enumValueMap.get(object), ""); + return new WriteCellData<>(value); + } + + private Map beforeConvert(ExcelContentProperty contentProperty) { + ExcelEnumFormat anno = getAnnotation(contentProperty.getField()); + Map enumValueMap = new HashMap<>(); + Enum[] enumConstants = anno.enumClass().getEnumConstants(); + for (Enum enumConstant : enumConstants) { + Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField()); + String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField()); + enumValueMap.put(codeValue, textValue); + } + return enumValueMap; + } + + private ExcelEnumFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class); + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/CellMergeStrategy.java new file mode 100644 index 00000000..f6852cc1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/CellMergeStrategy.java @@ -0,0 +1,122 @@ +package com.xmzs.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import com.xmzs.common.excel.annotation.CellMerge; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 列值重复合并策略 + * + * @author Lion Li + */ +@Slf4j +public class CellMergeStrategy extends AbstractMergeStrategy { + + private final List list; + private final boolean hasTitle; + private int rowIndex; + + public CellMergeStrategy(List list, boolean hasTitle) { + this.list = list; + this.hasTitle = hasTitle; + // 行合并开始下标 + this.rowIndex = hasTitle ? 1 : 0; + } + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + List cellList = handle(list, hasTitle); + // judge the list is not null + if (CollUtil.isNotEmpty(cellList)) { + // the judge is necessary + if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == 0) { + for (CellRangeAddress item : cellList) { + sheet.addMergedRegion(item); + } + } + } + } + + @SneakyThrows + private List handle(List list, boolean hasTitle) { + List cellList = new ArrayList<>(); + if (CollUtil.isEmpty(list)) { + return cellList; + } + Field[] fields = ReflectUtils.getFields(list.get(0).getClass(), field -> !"serialVersionUID".equals(field.getName())); + + // 有注解的字段 + List mergeFields = new ArrayList<>(); + List mergeFieldsIndex = new ArrayList<>(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (field.isAnnotationPresent(CellMerge.class)) { + CellMerge cm = field.getAnnotation(CellMerge.class); + mergeFields.add(field); + mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index()); + if (hasTitle) { + ExcelProperty property = field.getAnnotation(ExcelProperty.class); + rowIndex = Math.max(rowIndex, property.value().length); + } + } + } + + Map map = new HashMap<>(); + // 生成两两合并单元格 + for (int i = 0; i < list.size(); i++) { + for (int j = 0; j < mergeFields.size(); j++) { + Field field = mergeFields.get(j); + Object val = ReflectUtils.invokeGetter(list.get(i), field.getName()); + + int colNum = mergeFieldsIndex.get(j); + if (!map.containsKey(field)) { + map.put(field, new RepeatCell(val, i)); + } else { + RepeatCell repeatCell = map.get(field); + Object cellValue = repeatCell.getValue(); + if (cellValue == null || "".equals(cellValue)) { + // 空值跳过不合并 + continue; + } + if (!cellValue.equals(val)) { + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); + } else if (i == list.size() - 1) { + if (i > repeatCell.getCurrent()) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); + } + } + } + } + } + return cellList; + } + + @Data + @AllArgsConstructor + static class RepeatCell { + + private Object value; + + private int current; + + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelListener.java new file mode 100644 index 00000000..cb267bcb --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelListener.java @@ -0,0 +1,104 @@ +package com.xmzs.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.ValidatorUtils; +import com.xmzs.common.json.utils.JsonUtils; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.Set; + +/** + * Excel 导入监听 + * + * @author Yjoioooo + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor +public class DefaultExcelListener extends AnalysisEventListener implements ExcelListener { + + /** + * 是否Validator检验,默认为是 + */ + private Boolean isValidate = Boolean.TRUE; + + /** + * excel 表头数据 + */ + private Map headMap; + + /** + * 导入回执 + */ + private ExcelResult excelResult; + + public DefaultExcelListener(boolean isValidate) { + this.excelResult = new DefaultExcelResult<>(); + this.isValidate = isValidate; + } + + /** + * 处理异常 + * + * @param exception ExcelDataConvertException + * @param context Excel 上下文 + */ + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + String errMsg = null; + if (exception instanceof ExcelDataConvertException excelDataConvertException) { + // 如果是某一个单元格的转换异常 能获取到具体行号 + Integer rowIndex = excelDataConvertException.getRowIndex(); + Integer columnIndex = excelDataConvertException.getColumnIndex(); + errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", + rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + if (exception instanceof ConstraintViolationException constraintViolationException) { + Set> constraintViolations = constraintViolationException.getConstraintViolations(); + String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", "); + errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + excelResult.getErrorList().add(errMsg); + throw new ExcelAnalysisException(errMsg); + } + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.headMap = headMap; + log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap)); + } + + @Override + public void invoke(T data, AnalysisContext context) { + if (isValidate) { + ValidatorUtils.validate(data); + } + excelResult.getList().add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.debug("所有数据解析完成!"); + } + + @Override + public ExcelResult getExcelResult() { + return excelResult; + } + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelResult.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelResult.java new file mode 100644 index 00000000..4c3302c1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/DefaultExcelResult.java @@ -0,0 +1,73 @@ +package com.xmzs.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 默认excel返回对象 + * + * @author Yjoioooo + * @author Lion Li + */ +public class DefaultExcelResult implements ExcelResult { + + /** + * 数据对象list + */ + @Setter + private List list; + + /** + * 错误信息列表 + */ + @Setter + private List errorList; + + public DefaultExcelResult() { + this.list = new ArrayList<>(); + this.errorList = new ArrayList<>(); + } + + public DefaultExcelResult(List list, List errorList) { + this.list = list; + this.errorList = errorList; + } + + public DefaultExcelResult(ExcelResult excelResult) { + this.list = excelResult.getList(); + this.errorList = excelResult.getErrorList(); + } + + @Override + public List getList() { + return list; + } + + @Override + public List getErrorList() { + return errorList; + } + + /** + * 获取导入回执 + * + * @return 导入回执 + */ + @Override + public String getAnalysis() { + int successCount = list.size(); + int errorCount = errorList.size(); + if (successCount == 0) { + return "读取失败,未解析到数据"; + } else { + if (errorCount == 0) { + return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount); + } else { + return ""; + } + } + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelListener.java new file mode 100644 index 00000000..7e9fa448 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelListener.java @@ -0,0 +1,14 @@ +package com.xmzs.common.excel.core; + +import com.alibaba.excel.read.listener.ReadListener; + +/** + * Excel 导入监听 + * + * @author Lion Li + */ +public interface ExcelListener extends ReadListener { + + ExcelResult getExcelResult(); + +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelResult.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelResult.java new file mode 100644 index 00000000..105304e3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/core/ExcelResult.java @@ -0,0 +1,26 @@ +package com.xmzs.common.excel.core; + +import java.util.List; + +/** + * excel返回对象 + * + * @author Lion Li + */ +public interface ExcelResult { + + /** + * 对象列表 + */ + List getList(); + + /** + * 错误列表 + */ + List getErrorList(); + + /** + * 导入回执 + */ + String getAnalysis(); +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/utils/ExcelUtil.java new file mode 100644 index 00000000..8351849b --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/com/xmzs/common/excel/utils/ExcelUtil.java @@ -0,0 +1,327 @@ +package com.xmzs.common.excel.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.IdUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.file.FileUtils; +import com.xmzs.common.excel.convert.ExcelBigNumberConvert; +import com.xmzs.common.excel.core.CellMergeStrategy; +import com.xmzs.common.excel.core.DefaultExcelListener; +import com.xmzs.common.excel.core.ExcelListener; +import com.xmzs.common.excel.core.ExcelResult; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Excel相关处理 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelUtil { + + /** + * 同步导入(适用于小数据量) + * + * @param is 输入流 + * @return 转换后集合 + */ + public static List importExcel(InputStream is, Class clazz) { + return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + } + + + /** + * 使用校验监听器 异步导入 同步返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param isValidate 是否 Validator 检验 默认为是 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { + DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); + EasyExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 使用自定义监听器 异步导入 自定义返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param listener 自定义监听器 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { + EasyExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { + exportExcel(list, sheetName, clazz, false, os); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { + ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + builder.doWrite(list); + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplate(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplate(List data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + // 单表多数据导出 模板格式为 {.属性} + for (Object d : data) { + excelWriter.fill(d, writeSheet); + } + excelWriter.finish(); + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiList(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + excelWriter.finish(); + } + + /** + * 重置响应体 + */ + private static void resetResponse(String sheetName, HttpServletResponse response) { + String filename = encodingFilename(sheetName); + FileUtils.setAttachmentResponseHeader(response, filename); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + + /** + * 解析导出值 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(StringUtils.SEPARATOR); + 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]).append(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(StringUtils.SEPARATOR); + 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]).append(separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 编码文件名 + */ + public static String encodingFilename(String filename) { + return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; + } + +} diff --git a/ruoyi-common/ruoyi-common-idempotent/pom.xml b/ruoyi-common/ruoyi-common-idempotent/pom.xml new file mode 100644 index 00000000..7380b17d --- /dev/null +++ b/ruoyi-common/ruoyi-common-idempotent/pom.xml @@ -0,0 +1,42 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-idempotent + + + ruoyi-common-idempotent 幂等功能 + + + + + com.xmzs + ruoyi-common-json + + + + com.xmzs + ruoyi-common-redis + + + + cn.hutool + hutool-crypto + + + + cn.dev33 + sa-token-core + + + + + diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/annotation/RepeatSubmit.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/annotation/RepeatSubmit.java new file mode 100644 index 00000000..541ddbcf --- /dev/null +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/annotation/RepeatSubmit.java @@ -0,0 +1,29 @@ +package com.xmzs.common.idempotent.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * 自定义注解防止表单重复提交 + * + * @author Lion Li + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + int interval() default 5000; + + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * 提示消息 支持国际化 格式为 {code} + */ + String message() default "{repeat.submit.message}"; + +} diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/aspectj/RepeatSubmitAspect.java new file mode 100644 index 00000000..91f4b665 --- /dev/null +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,152 @@ +package com.xmzs.common.idempotent.aspectj; + +import cn.dev33.satoken.SaManager; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MessageUtils; +import com.xmzs.common.core.utils.ServletUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.redis.utils.RedisUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +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.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.time.Duration; +import java.util.Collection; +import java.util.Map; + +/** + * 防止重复提交(参考美团GTIS防重系统) + * + * @author Lion Li + */ +@Aspect +public class RepeatSubmitAspect { + + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = 0; + if (repeatSubmit.interval() > 0) { + interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + } + if (interval < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = argsArrayToString(point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); + + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey; + String key = RedisUtils.getCacheObject(cacheRepeatKey); + if (key == null) { + RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval)); + KEY_CACHE.set(cacheRepeatKey); + } else { + String message = repeatSubmit.message(); + if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { + message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); + } + throw new ServiceException(message); + } + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { + if (jsonResult instanceof R r) { + try { + // 成功则不删除redis数据 保证在有效时间内无法重复提交 + if (r.getCode() == R.SUCCESS) { + return; + } + RedisUtils.deleteObject(KEY_CACHE.get()); + } finally { + KEY_CACHE.remove(); + } + } + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { + RedisUtils.deleteObject(KEY_CACHE.get()); + KEY_CACHE.remove(); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + try { + params.append(JsonUtils.toJsonString(o)).append(" "); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return params.toString().trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @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/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/config/IdempotentConfig.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/config/IdempotentConfig.java new file mode 100644 index 00000000..2964617c --- /dev/null +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/com/xmzs/common/idempotent/config/IdempotentConfig.java @@ -0,0 +1,21 @@ +package com.xmzs.common.idempotent.config; + +import com.xmzs.common.idempotent.aspectj.RepeatSubmitAspect; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConfiguration; + +/** + * 幂等功能配置 + * + * @author Lion Li + */ +@AutoConfiguration(after = RedisConfiguration.class) +public class IdempotentConfig { + + @Bean + public RepeatSubmitAspect repeatSubmitAspect() { + return new RepeatSubmitAspect(); + } + +} diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..5498b60b --- /dev/null +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.idempotent.config.IdempotentConfig diff --git a/ruoyi-common/ruoyi-common-job/pom.xml b/ruoyi-common/ruoyi-common-job/pom.xml new file mode 100644 index 00000000..d820f20f --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/pom.xml @@ -0,0 +1,43 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-job + + + ruoyi-common-job 定时任务 + + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + com.xuxueli + xxl-job-core + + + + org.projectlombok + lombok + + + + com.xmzs + ruoyi-common-core + + + + diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/XxlJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/XxlJobConfig.java new file mode 100644 index 00000000..c7d184e9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/XxlJobConfig.java @@ -0,0 +1,38 @@ +package com.xmzs.common.job.config; + +import com.xmzs.common.job.config.properties.XxlJobProperties; +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * xxl-job config + * + * @author Lion Li + */ +@Slf4j +@AutoConfiguration +@EnableConfigurationProperties(XxlJobProperties.class) +@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true") +public class XxlJobConfig { + + @Bean + public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties) { + log.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses()); + xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken()); + XxlJobProperties.Executor executor = xxlJobProperties.getExecutor(); + xxlJobSpringExecutor.setAppname(executor.getAppname()); + xxlJobSpringExecutor.setAddress(executor.getAddress()); + xxlJobSpringExecutor.setIp(executor.getIp()); + xxlJobSpringExecutor.setPort(executor.getPort()); + xxlJobSpringExecutor.setLogPath(executor.getLogPath()); + xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays()); + return xxlJobSpringExecutor; + } + +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/properties/XxlJobProperties.java b/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/properties/XxlJobProperties.java new file mode 100644 index 00000000..5beb4734 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/com/xmzs/common/job/config/properties/XxlJobProperties.java @@ -0,0 +1,40 @@ +package com.xmzs.common.job.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * xxljob配置类 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "xxl.job") +public class XxlJobProperties { + + private Boolean enabled; + + private String adminAddresses; + + private String accessToken; + + private Executor executor; + + @Data + @NoArgsConstructor + public static class Executor { + + private String appname; + + private String address; + + private String ip; + + private int port; + + private String logPath; + + private int logRetentionDays; + } +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/com/xxl/job/core/glue/impl/SpringGlueFactory.java b/ruoyi-common/ruoyi-common-job/src/main/java/com/xxl/job/core/glue/impl/SpringGlueFactory.java new file mode 100644 index 00000000..00cd8228 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/com/xxl/job/core/glue/impl/SpringGlueFactory.java @@ -0,0 +1,80 @@ +package com.xxl.job.core.glue.impl; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import com.xxl.job.core.glue.GlueFactory; +import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.annotation.AnnotationUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +/** + * @author xuxueli 2018-11-01 + */ +public class SpringGlueFactory extends GlueFactory { + private static Logger logger = LoggerFactory.getLogger(SpringGlueFactory.class); + + + /** + * inject action of spring + * @param instance + */ + @Override + public void injectService(Object instance){ + if (instance==null) { + return; + } + + if (XxlJobSpringExecutor.getApplicationContext() == null) { + return; + } + + Field[] fields = instance.getClass().getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + + Object fieldBean = null; + // with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired + + if (AnnotationUtils.getAnnotation(field, Resource.class) != null) { + try { + Resource resource = AnnotationUtils.getAnnotation(field, Resource.class); + if (resource.name()!=null && resource.name().length()>0){ + fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(resource.name()); + } else { + fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getName()); + } + } catch (Exception e) { + } + if (fieldBean==null ) { + fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType()); + } + } else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) { + Qualifier qualifier = AnnotationUtils.getAnnotation(field, Qualifier.class); + if (qualifier!=null && qualifier.value()!=null && qualifier.value().length()>0) { + fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(qualifier.value()); + } else { + fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType()); + } + } + + if (fieldBean!=null) { + field.setAccessible(true); + try { + field.set(instance, fieldBean); + } catch (IllegalArgumentException e) { + logger.error(e.getMessage(), e); + } catch (IllegalAccessException e) { + logger.error(e.getMessage(), e); + } + } + } + } + +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..559a39df --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.job.config.XxlJobConfig diff --git a/ruoyi-common/ruoyi-common-json/pom.xml b/ruoyi-common/ruoyi-common-json/pom.xml new file mode 100644 index 00000000..13dcf387 --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/pom.xml @@ -0,0 +1,44 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-json + + + ruoyi-common-json 序列化模块 + + + + + com.xmzs + ruoyi-common-core + + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + com.alibaba + fastjson + 2.0.32 + + + + + diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/config/JacksonConfig.java b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/config/JacksonConfig.java new file mode 100644 index 00000000..9ed093ea --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/config/JacksonConfig.java @@ -0,0 +1,47 @@ +package com.xmzs.common.json.config; + +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.xmzs.common.json.handler.BigNumberSerializer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.context.annotation.Bean; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; + +/** + * jackson 配置 + * + * @author Lion Li + */ +@Slf4j +@AutoConfiguration(before = JacksonAutoConfiguration.class) +public class JacksonConfig { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> { + // 全局配置序列化返回 JSON 处理 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); + builder.modules(javaTimeModule); + builder.timeZone(TimeZone.getDefault()); + log.info("初始化 jackson 配置"); + }; + } + +} diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/handler/BigNumberSerializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/handler/BigNumberSerializer.java new file mode 100644 index 00000000..256b1973 --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/handler/BigNumberSerializer.java @@ -0,0 +1,42 @@ +package com.xmzs.common.json.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; + +import java.io.IOException; + +/** + * 超出 JS 最大最小值 处理 + * + * @author Lion Li + */ +@JacksonStdImpl +public class BigNumberSerializer extends NumberSerializer { + + /** + * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 + */ + private static final long MAX_SAFE_INTEGER = 9007199254740991L; + private static final long MIN_SAFE_INTEGER = -9007199254740991L; + + /** + * 提供实例 + */ + public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 超出范围 序列化位字符串 + if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } +} diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/utils/JsonUtils.java b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/utils/JsonUtils.java new file mode 100644 index 00000000..904f86bd --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/java/com/xmzs/common/json/utils/JsonUtils.java @@ -0,0 +1,113 @@ +package com.xmzs.common.json.utils; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class); + + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + public static String toJsonString(Object object) { + if (ObjectUtil.isNull(object)) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Dict parseMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class)); + } catch (MismatchedInputException e) { + // 类型不匹配说明不是json + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArrayMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..d1882e80 --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.json.config.JacksonConfig diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml new file mode 100644 index 00000000..a6af821c --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/pom.xml @@ -0,0 +1,38 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-log + + + ruoyi-common-log 日志记录 + + + + + + com.xmzs + ruoyi-common-satoken + + + + com.xmzs + ruoyi-common-json + + + + com.alibaba + transmittable-thread-local + + + + + diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/annotation/Log.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/annotation/Log.java new file mode 100644 index 00000000..2265d464 --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/annotation/Log.java @@ -0,0 +1,48 @@ +package com.xmzs.common.log.annotation; + +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.log.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; + + + /** + * 排除指定的请求参数 + */ + String[] excludeParamNames() default {}; + +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/aspect/LogAspect.java new file mode 100644 index 00000000..41c091c4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/aspect/LogAspect.java @@ -0,0 +1,221 @@ +package com.xmzs.common.log.aspect; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.xmzs.common.core.utils.ServletUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessStatus; +import com.xmzs.common.log.event.OperLogEvent; +import com.xmzs.common.satoken.utils.LoginHelper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +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.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +@AutoConfiguration +public class LogAspect { + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + + /** + * 计算操作消耗时间 + */ + private static final ThreadLocal TIME_THREADLOCAL = new TransmittableThreadLocal<>(); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void boBefore(JoinPoint joinPoint, Log controllerLog) { + StopWatch stopWatch = new StopWatch(); + TIME_THREADLOCAL.set(stopWatch); + stopWatch.start(); + } + + /** + * 处理完请求后执行 + * + * @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 { + + // *========数据库日志=========*// + OperLogEvent operLog = new OperLogEvent(); + operLog.setTenantId(LoginHelper.getTenantId()); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = ServletUtils.getClientIP(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + operLog.setOperName(LoginHelper.getUsername()); + + 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); + // 设置消耗时间 + StopWatch stopWatch = TIME_THREADLOCAL.get(); + stopWatch.stop(); + operLog.setCostTime(stopWatch.getTime()); + // 发布事件保存数据库 + SpringUtils.context().publishEvent(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, OperLogEvent 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() && ObjectUtil.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (MapUtil.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 { + MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); + MapUtil.removeAny(paramsMap, excludeParamNames); + operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + try { + String str = JsonUtils.toJsonString(o); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); + MapUtil.removeAny(dict, excludeParamNames); + str = JsonUtils.toJsonString(dict); + } + params.append(str).append(" "); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return params.toString().trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @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/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessStatus.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessStatus.java new file mode 100644 index 00000000..84109a7f --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package com.xmzs.common.log.enums; + +/** + * 操作状态 + * + * @author ruoyi + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessType.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessType.java new file mode 100644 index 00000000..698123f5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/BusinessType.java @@ -0,0 +1,58 @@ +package com.xmzs.common.log.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/OperatorType.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/OperatorType.java new file mode 100644 index 00000000..f83a8c75 --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.xmzs.common.log.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/LogininforEvent.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/LogininforEvent.java new file mode 100644 index 00000000..10a9340e --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/LogininforEvent.java @@ -0,0 +1,52 @@ +package com.xmzs.common.log.event; + +import lombok.Data; + +import jakarta.servlet.http.HttpServletRequest; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 登录事件 + * + * @author Lion Li + */ + +@Data +public class LogininforEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户账号 + */ + private String username; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 提示消息 + */ + private String message; + + /** + * 请求体 + */ + private HttpServletRequest request; + + /** + * 其他参数 + */ + private Object[] args; + +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/OperLogEvent.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/OperLogEvent.java new file mode 100644 index 00000000..8ab5529d --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/xmzs/common/log/event/OperLogEvent.java @@ -0,0 +1,115 @@ +package com.xmzs.common.log.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志事件 + * + * @author Lion Li + */ + +@Data +public class OperLogEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; +} diff --git a/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..746b32fe --- /dev/null +++ b/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.log.aspect.LogAspect diff --git a/ruoyi-common/ruoyi-common-mail/pom.xml b/ruoyi-common/ruoyi-common-mail/pom.xml new file mode 100644 index 00000000..484c71b4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/pom.xml @@ -0,0 +1,35 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-mail + + + ruoyi-common-mail 邮件模块 + + + + + com.xmzs + ruoyi-common-core + + + + jakarta.mail + jakarta.mail-api + + + org.eclipse.angus + jakarta.mail + + + + diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/MailConfig.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/MailConfig.java new file mode 100644 index 00000000..97046924 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/MailConfig.java @@ -0,0 +1,37 @@ +package com.xmzs.common.mail.config; + +import com.xmzs.common.mail.config.properties.MailProperties; +import com.xmzs.common.mail.utils.MailAccount; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * JavaMail 配置 + * + * @author Michelle.Chung + */ +@AutoConfiguration +@EnableConfigurationProperties(MailProperties.class) +public class MailConfig { + + @Bean + @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") + public MailAccount mailAccount(MailProperties mailProperties) { + MailAccount account = new MailAccount(); + account.setHost(mailProperties.getHost()); + account.setPort(mailProperties.getPort()); + account.setAuth(mailProperties.getAuth()); + account.setFrom(mailProperties.getFrom()); + account.setUser(mailProperties.getUser()); + account.setPass(mailProperties.getPass()); + account.setSocketFactoryPort(mailProperties.getPort()); + account.setStarttlsEnable(mailProperties.getStarttlsEnable()); + account.setSslEnable(mailProperties.getSslEnable()); + account.setTimeout(mailProperties.getTimeout()); + account.setConnectionTimeout(mailProperties.getConnectionTimeout()); + return account; + } + +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/properties/MailProperties.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/properties/MailProperties.java new file mode 100644 index 00000000..c7272b57 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/config/properties/MailProperties.java @@ -0,0 +1,69 @@ +package com.xmzs.common.mail.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * JavaMail 配置属性 + * + * @author Michelle.Chung + */ +@Data +@ConfigurationProperties(prefix = "mail") +public class MailProperties { + + /** + * 过滤开关 + */ + private Boolean enabled; + + /** + * SMTP服务器域名 + */ + private String host; + + /** + * SMTP服务端口 + */ + private Integer port; + + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + + /** + * 用户名 + */ + private String user; + + /** + * 密码 + */ + private String pass; + + /** + * 发送方,遵循RFC-822标准 + */ + private String from; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private Boolean starttlsEnable; + + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private Long timeout; + + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private Long connectionTimeout; +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/GlobalMailAccount.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/GlobalMailAccount.java new file mode 100644 index 00000000..bdc72a87 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/GlobalMailAccount.java @@ -0,0 +1,46 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.io.IORuntimeException; + +/** + * 全局邮件帐户,依赖于邮件配置文件{@link MailAccount#MAIL_SETTING_PATHS} + * + * @author looly + */ +public enum GlobalMailAccount { + INSTANCE; + + private final MailAccount mailAccount; + + /** + * 构造 + */ + GlobalMailAccount() { + mailAccount = createDefaultAccount(); + } + + /** + * 获得邮件帐户 + * + * @return 邮件帐户 + */ + public MailAccount getAccount() { + return this.mailAccount; + } + + /** + * 创建默认帐户 + * + * @return MailAccount + */ + private MailAccount createDefaultAccount() { + for (String mailSettingPath : MailAccount.MAIL_SETTING_PATHS) { + try { + return new MailAccount(mailSettingPath); + } catch (IORuntimeException ignore) { + //ignore + } + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/InternalMailUtil.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/InternalMailUtil.java new file mode 100644 index 00000000..0426754f --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/InternalMailUtil.java @@ -0,0 +1,108 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.util.ArrayUtil; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeUtility; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 邮件内部工具类 + * + * @author looly + * @since 3.2.3 + */ +public class InternalMailUtil { + + /** + * 将多个字符串邮件地址转为{@link InternetAddress}列表
+ * 单个字符串地址可以是多个地址合并的字符串 + * + * @param addrStrs 地址数组 + * @param charset 编码(主要用于中文用户名的编码) + * @return 地址数组 + * @since 4.0.3 + */ + public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { + final List resultList = new ArrayList<>(addrStrs.length); + InternetAddress[] addrs; + for (String addrStr : addrStrs) { + addrs = parseAddress(addrStr, charset); + if (ArrayUtil.isNotEmpty(addrs)) { + Collections.addAll(resultList, addrs); + } + } + return resultList.toArray(new InternetAddress[0]); + } + + /** + * 解析第一个地址 + * + * @param address 地址字符串 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 + * @return 地址列表 + */ + public static InternetAddress parseFirstAddress(String address, Charset charset) { + final InternetAddress[] internetAddresses = parseAddress(address, charset); + if (ArrayUtil.isEmpty(internetAddresses)) { + try { + return new InternetAddress(address); + } catch (AddressException e) { + throw new MailException(e); + } + } + return internetAddresses[0]; + } + + /** + * 将一个地址字符串解析为多个地址
+ * 地址间使用" "、","、";"分隔 + * + * @param address 地址字符串 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 + * @return 地址列表 + */ + public static InternetAddress[] parseAddress(String address, Charset charset) { + InternetAddress[] addresses; + try { + addresses = InternetAddress.parse(address); + } catch (AddressException e) { + throw new MailException(e); + } + //编码用户名 + if (ArrayUtil.isNotEmpty(addresses)) { + final String charsetStr = null == charset ? null : charset.name(); + for (InternetAddress internetAddress : addresses) { + try { + internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr); + } catch (UnsupportedEncodingException e) { + throw new MailException(e); + } + } + } + + return addresses; + } + + /** + * 编码中文字符
+ * 编码失败返回原字符串 + * + * @param text 被编码的文本 + * @param charset 编码 + * @return 编码后的结果 + */ + public static String encodeText(String text, Charset charset) { + try { + return MimeUtility.encodeText(text, charset.name(), null); + } catch (UnsupportedEncodingException e) { + // ignore + } + return text; + } +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/Mail.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/Mail.java new file mode 100644 index 00000000..51d3b477 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/Mail.java @@ -0,0 +1,483 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.builder.Builder; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.activation.FileDataSource; +import jakarta.activation.FileTypeMap; +import jakarta.mail.*; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import jakarta.mail.internet.MimeUtility; +import jakarta.mail.util.ByteArrayDataSource; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.Date; + +/** + * 邮件发送客户端 + * + * @author looly + * @since 3.2.0 + */ +public class Mail implements Builder { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 邮箱帐户信息以及一些客户端配置信息 + */ + private final MailAccount mailAccount; + /** + * 收件人列表 + */ + private String[] tos; + /** + * 抄送人列表(carbon copy) + */ + private String[] ccs; + /** + * 密送人列表(blind carbon copy) + */ + private String[] bccs; + /** + * 回复地址(reply-to) + */ + private String[] reply; + /** + * 标题 + */ + private String title; + /** + * 内容 + */ + private String content; + /** + * 是否为HTML + */ + private boolean isHtml; + /** + * 正文、附件和图片的混合部分 + */ + private final Multipart multipart = new MimeMultipart(); + /** + * 是否使用全局会话,默认为false + */ + private boolean useGlobalSession = false; + + /** + * debug输出位置,可以自定义debug日志 + */ + private PrintStream debugOutput; + + /** + * 创建邮件客户端 + * + * @param mailAccount 邮件帐号 + * @return Mail + */ + public static Mail create(MailAccount mailAccount) { + return new Mail(mailAccount); + } + + /** + * 创建邮件客户端,使用全局邮件帐户 + * + * @return Mail + */ + public static Mail create() { + return new Mail(); + } + + // --------------------------------------------------------------- Constructor start + + /** + * 构造,使用全局邮件帐户 + */ + public Mail() { + this(GlobalMailAccount.INSTANCE.getAccount()); + } + + /** + * 构造 + * + * @param mailAccount 邮件帐户,如果为null使用默认配置文件的全局邮件配置 + */ + public Mail(MailAccount mailAccount) { + mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount(); + this.mailAccount = mailAccount.defaultIfEmpty(); + } + // --------------------------------------------------------------- Constructor end + + // --------------------------------------------------------------- Getters and Setters start + + /** + * 设置收件人 + * + * @param tos 收件人列表 + * @return this + * @see #setTos(String...) + */ + public Mail to(String... tos) { + return setTos(tos); + } + + /** + * 设置多个收件人 + * + * @param tos 收件人列表 + * @return this + */ + public Mail setTos(String... tos) { + this.tos = tos; + return this; + } + + /** + * 设置多个抄送人(carbon copy) + * + * @param ccs 抄送人列表 + * @return this + * @since 4.0.3 + */ + public Mail setCcs(String... ccs) { + this.ccs = ccs; + return this; + } + + /** + * 设置多个密送人(blind carbon copy) + * + * @param bccs 密送人列表 + * @return this + * @since 4.0.3 + */ + public Mail setBccs(String... bccs) { + this.bccs = bccs; + return this; + } + + /** + * 设置多个回复地址(reply-to) + * + * @param reply 回复地址(reply-to)列表 + * @return this + * @since 4.6.0 + */ + public Mail setReply(String... reply) { + this.reply = reply; + return this; + } + + /** + * 设置标题 + * + * @param title 标题 + * @return this + */ + public Mail setTitle(String title) { + this.title = title; + return this; + } + + /** + * 设置正文
+ * 正文可以是普通文本也可以是HTML(默认普通文本),可以通过调用{@link #setHtml(boolean)} 设置是否为HTML + * + * @param content 正文 + * @return this + */ + public Mail setContent(String content) { + this.content = content; + return this; + } + + /** + * 设置是否是HTML + * + * @param isHtml 是否为HTML + * @return this + */ + public Mail setHtml(boolean isHtml) { + this.isHtml = isHtml; + return this; + } + + /** + * 设置正文 + * + * @param content 正文内容 + * @param isHtml 是否为HTML + * @return this + */ + public Mail setContent(String content, boolean isHtml) { + setContent(content); + return setHtml(isHtml); + } + + /** + * 设置文件类型附件,文件可以是图片文件,此时自动设置cid(正文中引用图片),默认cid为文件名 + * + * @param files 附件文件列表 + * @return this + */ + public Mail setFiles(File... files) { + if (ArrayUtil.isEmpty(files)) { + return this; + } + + final DataSource[] attachments = new DataSource[files.length]; + for (int i = 0; i < files.length; i++) { + attachments[i] = new FileDataSource(files[i]); + } + return setAttachments(attachments); + } + + /** + * 增加附件或图片,附件使用{@link DataSource} 形式表示,可以使用{@link FileDataSource}包装文件表示文件附件 + * + * @param attachments 附件列表 + * @return this + * @since 4.0.9 + */ + public Mail setAttachments(DataSource... attachments) { + if (ArrayUtil.isNotEmpty(attachments)) { + final Charset charset = this.mailAccount.getCharset(); + MimeBodyPart bodyPart; + String nameEncoded; + try { + for (DataSource attachment : attachments) { + bodyPart = new MimeBodyPart(); + bodyPart.setDataHandler(new DataHandler(attachment)); + nameEncoded = attachment.getName(); + if (this.mailAccount.isEncodefilename()) { + nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); + } + // 普通附件文件名 + bodyPart.setFileName(nameEncoded); + if (StrUtil.startWith(attachment.getContentType(), "image/")) { + // 图片附件,用于正文中引用图片 + bodyPart.setContentID(nameEncoded); + } + this.multipart.addBodyPart(bodyPart); + } + } catch (MessagingException e) { + throw new MailException(e); + } + } + return this; + } + + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串,图片类型默认为"image/jpeg" + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageStream 图片文件 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream) { + return addImage(cid, imageStream, null); + } + + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串 + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageStream 图片流,不关闭 + * @param contentType 图片类型,null赋值默认的"image/jpeg" + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream, String contentType) { + ByteArrayDataSource imgSource; + try { + imgSource = new ByteArrayDataSource(imageStream, ObjectUtil.defaultIfNull(contentType, "image/jpeg")); + } catch (IOException e) { + throw new IORuntimeException(e); + } + imgSource.setName(cid); + return setAttachments(imgSource); + } + + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串 + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageFile 图片文件 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, File imageFile) { + InputStream in = null; + try { + in = FileUtil.getInputStream(imageFile); + return addImage(cid, in, FileTypeMap.getDefaultFileTypeMap().getContentType(imageFile)); + } finally { + IoUtil.close(in); + } + } + + /** + * 设置字符集编码 + * + * @param charset 字符集编码 + * @return this + * @see MailAccount#setCharset(Charset) + */ + public Mail setCharset(Charset charset) { + this.mailAccount.setCharset(charset); + return this; + } + + /** + * 设置是否使用全局会话,默认为true + * + * @param isUseGlobalSession 是否使用全局会话,默认为true + * @return this + * @since 4.0.2 + */ + public Mail setUseGlobalSession(boolean isUseGlobalSession) { + this.useGlobalSession = isUseGlobalSession; + return this; + } + + /** + * 设置debug输出位置,可以自定义debug日志 + * + * @param debugOutput debug输出位置 + * @return this + * @since 5.5.6 + */ + public Mail setDebugOutput(PrintStream debugOutput) { + this.debugOutput = debugOutput; + return this; + } + // --------------------------------------------------------------- Getters and Setters end + + @Override + public MimeMessage build() { + try { + return buildMsg(); + } catch (MessagingException e) { + throw new MailException(e); + } + } + + /** + * 发送 + * + * @return message-id + * @throws MailException 邮件发送异常 + */ + public String send() throws MailException { + try { + return doSend(); + } catch (MessagingException e) { + if (e instanceof SendFailedException) { + // 当地址无效时,显示更加详细的无效地址信息 + final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses(); + final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses)); + throw new MailException(msg, e); + } + throw new MailException(e); + } + } + + // --------------------------------------------------------------- Private method start + + /** + * 执行发送 + * + * @return message-id + * @throws MessagingException 发送异常 + */ + private String doSend() throws MessagingException { + final MimeMessage mimeMessage = buildMsg(); + Transport.send(mimeMessage); + return mimeMessage.getMessageID(); + } + + /** + * 构建消息 + * + * @return {@link MimeMessage}消息 + * @throws MessagingException 消息异常 + */ + private MimeMessage buildMsg() throws MessagingException { + final Charset charset = this.mailAccount.getCharset(); + final MimeMessage msg = new MimeMessage(getSession()); + // 发件人 + final String from = this.mailAccount.getFrom(); + if (StrUtil.isEmpty(from)) { + // 用户未提供发送方,则从Session中自动获取 + msg.setFrom(); + } else { + msg.setFrom(InternalMailUtil.parseFirstAddress(from, charset)); + } + // 标题 + msg.setSubject(this.title, (null == charset) ? null : charset.name()); + // 发送时间 + msg.setSentDate(new Date()); + // 内容和附件 + msg.setContent(buildContent(charset)); + // 收件人 + msg.setRecipients(MimeMessage.RecipientType.TO, InternalMailUtil.parseAddressFromStrs(this.tos, charset)); + // 抄送人 + if (ArrayUtil.isNotEmpty(this.ccs)) { + msg.setRecipients(MimeMessage.RecipientType.CC, InternalMailUtil.parseAddressFromStrs(this.ccs, charset)); + } + // 密送人 + if (ArrayUtil.isNotEmpty(this.bccs)) { + msg.setRecipients(MimeMessage.RecipientType.BCC, InternalMailUtil.parseAddressFromStrs(this.bccs, charset)); + } + // 回复地址(reply-to) + if (ArrayUtil.isNotEmpty(this.reply)) { + msg.setReplyTo(InternalMailUtil.parseAddressFromStrs(this.reply, charset)); + } + + return msg; + } + + /** + * 构建邮件信息主体 + * + * @param charset 编码,{@code null}则使用{@link MimeUtility#getDefaultJavaCharset()} + * @return 邮件信息主体 + * @throws MessagingException 消息异常 + */ + private Multipart buildContent(Charset charset) throws MessagingException { + final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset(); + // 正文 + final MimeBodyPart body = new MimeBodyPart(); + body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr)); + this.multipart.addBodyPart(body); + + return this.multipart; + } + + /** + * 获取默认邮件会话
+ * 如果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话 + * + * @return 邮件会话 {@link Session} + */ + private Session getSession() { + final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession); + + if (null != this.debugOutput) { + session.setDebugOut(debugOutput); + } + + return session; + } + // --------------------------------------------------------------- Private method end +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailAccount.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailAccount.java new file mode 100644 index 00000000..8e0fbd24 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailAccount.java @@ -0,0 +1,659 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.setting.Setting; + +import java.io.Serial; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * 邮件账户对象 + * + * @author Luxiaolei + */ +public class MailAccount implements Serializable { + @Serial + private static final long serialVersionUID = -6937313421815719204L; + + private static final String MAIL_PROTOCOL = "mail.transport.protocol"; + private static final String SMTP_HOST = "mail.smtp.host"; + private static final String SMTP_PORT = "mail.smtp.port"; + private static final String SMTP_AUTH = "mail.smtp.auth"; + private static final String SMTP_TIMEOUT = "mail.smtp.timeout"; + private static final String SMTP_CONNECTION_TIMEOUT = "mail.smtp.connectiontimeout"; + private static final String SMTP_WRITE_TIMEOUT = "mail.smtp.writetimeout"; + + // SSL + private static final String STARTTLS_ENABLE = "mail.smtp.starttls.enable"; + private static final String SSL_ENABLE = "mail.smtp.ssl.enable"; + private static final String SSL_PROTOCOLS = "mail.smtp.ssl.protocols"; + private static final String SOCKET_FACTORY = "mail.smtp.socketFactory.class"; + private static final String SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; + private static final String SOCKET_FACTORY_PORT = "smtp.socketFactory.port"; + + // System Properties + private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters"; + //private static final String ENCODE_FILE_NAME = "mail.mime.encodefilename"; + //private static final String CHARSET = "mail.mime.charset"; + + // 其他 + private static final String MAIL_DEBUG = "mail.debug"; + + public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"}; + + /** + * SMTP服务器域名 + */ + private String host; + /** + * SMTP服务端口 + */ + private Integer port; + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + /** + * 用户名 + */ + private String user; + /** + * 密码 + */ + private String pass; + /** + * 发送方,遵循RFC-822标准 + */ + private String from; + + /** + * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + */ + private boolean debug; + /** + * 编码用于编码邮件正文和发送人、收件人等中文 + */ + private Charset charset = CharsetUtil.CHARSET_UTF_8; + /** + * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) + */ + private boolean splitlongparameters = false; + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + */ + private boolean encodefilename = true; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private boolean starttlsEnable = false; + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SSL协议,多个协议用空格分隔 + */ + private String sslProtocols; + + /** + * 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + */ + private String socketFactoryClass = "javax.net.ssl.SSLSocketFactory"; + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + */ + private boolean socketFactoryFallback; + /** + * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + */ + private int socketFactoryPort = 465; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private long timeout; + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private long connectionTimeout; + /** + * Socket写出超时值,单位毫秒,缺省值不超时 + */ + private long writeTimeout; + + /** + * 自定义的其他属性,此自定义属性会覆盖默认属性 + */ + private final Map customProperty = new HashMap<>(); + + // -------------------------------------------------------------- Constructor start + + /** + * 构造,所有参数需自行定义或保持默认值 + */ + public MailAccount() { + } + + /** + * 构造 + * + * @param settingPath 配置文件路径 + */ + public MailAccount(String settingPath) { + this(new Setting(settingPath)); + } + + /** + * 构造 + * + * @param setting 配置文件 + */ + public MailAccount(Setting setting) { + setting.toBean(this); + } + + // -------------------------------------------------------------- Constructor end + + /** + * 获得SMTP服务器域名 + * + * @return SMTP服务器域名 + */ + public String getHost() { + return host; + } + + /** + * 设置SMTP服务器域名 + * + * @param host SMTP服务器域名 + * @return this + */ + public MailAccount setHost(String host) { + this.host = host; + return this; + } + + /** + * 获得SMTP服务端口 + * + * @return SMTP服务端口 + */ + public Integer getPort() { + return port; + } + + /** + * 设置SMTP服务端口 + * + * @param port SMTP服务端口 + * @return this + */ + public MailAccount setPort(Integer port) { + this.port = port; + return this; + } + + /** + * 是否需要用户名密码验证 + * + * @return 是否需要用户名密码验证 + */ + public Boolean isAuth() { + return auth; + } + + /** + * 设置是否需要用户名密码验证 + * + * @param isAuth 是否需要用户名密码验证 + * @return this + */ + public MailAccount setAuth(boolean isAuth) { + this.auth = isAuth; + return this; + } + + /** + * 获取用户名 + * + * @return 用户名 + */ + public String getUser() { + return user; + } + + /** + * 设置用户名 + * + * @param user 用户名 + * @return this + */ + public MailAccount setUser(String user) { + this.user = user; + return this; + } + + /** + * 获取密码 + * + * @return 密码 + */ + public String getPass() { + return pass; + } + + /** + * 设置密码 + * + * @param pass 密码 + * @return this + */ + public MailAccount setPass(String pass) { + this.pass = pass; + return this; + } + + /** + * 获取发送方,遵循RFC-822标准 + * + * @return 发送方,遵循RFC-822标准 + */ + public String getFrom() { + return from; + } + + /** + * 设置发送方,遵循RFC-822标准
+ * 发件人可以是以下形式: + * + *
+     * 1. user@xxx.xx
+     * 2.  name <user@xxx.xx>
+     * 
+ * + * @param from 发送方,遵循RFC-822标准 + * @return this + */ + public MailAccount setFrom(String from) { + this.from = from; + return this; + } + + /** + * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * + * @return 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * @since 4.0.2 + */ + public boolean isDebug() { + return debug; + } + + /** + * 设置是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * + * @param debug 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * @return this + * @since 4.0.2 + */ + public MailAccount setDebug(boolean debug) { + this.debug = debug; + return this; + } + + /** + * 获取字符集编码 + * + * @return 编码,可能为{@code null} + */ + public Charset getCharset() { + return charset; + } + + /** + * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: + *
+     * 	System.setProperty("mail.mime.charset", charset);
+     * 
+ * + * @param charset 字符集编码,{@code null} 则表示使用全局设置的默认编码,全局编码为mail.mime.charset系统属性 + * @return this + */ + public MailAccount setCharset(Charset charset) { + this.charset = charset; + return this; + } + + /** + * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) + * + * @return 对于超长参数是否切分为多份 + */ + public boolean isSplitlongparameters() { + return splitlongparameters; + } + + /** + * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
+ * 注意此项为全局设置,此项会调用 + *
+     * System.setProperty("mail.mime.splitlongparameters", true)
+     * 
+ * + * @param splitlongparameters 对于超长参数是否切分为多份 + */ + public void setSplitlongparameters(boolean splitlongparameters) { + this.splitlongparameters = splitlongparameters; + } + + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * + * @return 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * @since 5.7.16 + */ + public boolean isEncodefilename() { + + return encodefilename; + } + + /** + * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置
+ * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: + *
    + *
  • mail.mime.encodefilename 是否编码附件文件名
  • + *
  • mail.mime.charset 编码文件名的编码
  • + *
+ * + * @param encodefilename 对于文件名是否使用{@link #charset}编码 + * @since 5.7.16 + */ + public void setEncodefilename(boolean encodefilename) { + this.encodefilename = encodefilename; + } + + /** + * 是否使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + * + * @return 是否使用 STARTTLS安全连接 + */ + public boolean isStarttlsEnable() { + return this.starttlsEnable; + } + + /** + * 设置是否使用STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + * + * @param startttlsEnable 是否使用STARTTLS安全连接 + * @return this + */ + public MailAccount setStarttlsEnable(boolean startttlsEnable) { + this.starttlsEnable = startttlsEnable; + return this; + } + + /** + * 是否使用 SSL安全连接 + * + * @return 是否使用 SSL安全连接 + */ + public Boolean isSslEnable() { + return this.sslEnable; + } + + /** + * 设置是否使用SSL安全连接 + * + * @param sslEnable 是否使用SSL安全连接 + * @return this + */ + public MailAccount setSslEnable(Boolean sslEnable) { + this.sslEnable = sslEnable; + return this; + } + + /** + * 获取SSL协议,多个协议用空格分隔 + * + * @return SSL协议,多个协议用空格分隔 + * @since 5.5.7 + */ + public String getSslProtocols() { + return sslProtocols; + } + + /** + * 设置SSL协议,多个协议用空格分隔 + * + * @param sslProtocols SSL协议,多个协议用空格分隔 + * @since 5.5.7 + */ + public void setSslProtocols(String sslProtocols) { + this.sslProtocols = sslProtocols; + } + + /** + * 获取指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * + * @return 指定实现javax.net.SocketFactory接口的类的名称, 这个类将被用于创建SMTP的套接字 + */ + public String getSocketFactoryClass() { + return socketFactoryClass; + } + + /** + * 设置指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * + * @param socketFactoryClass 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * @return this + */ + public MailAccount setSocketFactoryClass(String socketFactoryClass) { + this.socketFactoryClass = socketFactoryClass; + return this; + } + + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * + * @return 如果设置为true, 未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + */ + public boolean isSocketFactoryFallback() { + return socketFactoryFallback; + } + + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * + * @param socketFactoryFallback 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * @return this + */ + public MailAccount setSocketFactoryFallback(boolean socketFactoryFallback) { + this.socketFactoryFallback = socketFactoryFallback; + return this; + } + + /** + * 获取指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * + * @return 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + */ + public int getSocketFactoryPort() { + return socketFactoryPort; + } + + /** + * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * + * @param socketFactoryPort 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * @return this + */ + public MailAccount setSocketFactoryPort(int socketFactoryPort) { + this.socketFactoryPort = socketFactoryPort; + return this; + } + + /** + * 设置SMTP超时时长,单位毫秒,缺省值不超时 + * + * @param timeout SMTP超时时长,单位毫秒,缺省值不超时 + * @return this + * @since 4.1.17 + */ + public MailAccount setTimeout(long timeout) { + this.timeout = timeout; + return this; + } + + /** + * 设置Socket连接超时值,单位毫秒,缺省值不超时 + * + * @param connectionTimeout Socket连接超时值,单位毫秒,缺省值不超时 + * @return this + * @since 4.1.17 + */ + public MailAccount setConnectionTimeout(long connectionTimeout) { + this.connectionTimeout = connectionTimeout; + return this; + } + + /** + * 设置Socket写出超时值,单位毫秒,缺省值不超时 + * + * @param writeTimeout Socket写出超时值,单位毫秒,缺省值不超时 + * @return this + * @since 5.8.3 + */ + public MailAccount setWriteTimeout(long writeTimeout) { + this.writeTimeout = writeTimeout; + return this; + } + + /** + * 获取自定义属性列表 + * + * @return 自定义参数列表 + * @since 5.6.4 + */ + public Map getCustomProperty() { + return customProperty; + } + + /** + * 设置自定义属性,如mail.smtp.ssl.socketFactory + * + * @param key 属性名,空白被忽略 + * @param value 属性值, null被忽略 + * @return this + * @since 5.6.4 + */ + public MailAccount setCustomProperty(String key, Object value) { + if (StrUtil.isNotBlank(key) && ObjectUtil.isNotNull(value)) { + this.customProperty.put(key, value); + } + return this; + } + + /** + * 获得SMTP相关信息 + * + * @return {@link Properties} + */ + public Properties getSmtpProps() { + //全局系统参数 + System.setProperty(SPLIT_LONG_PARAMS, String.valueOf(this.splitlongparameters)); + + final Properties p = new Properties(); + p.put(MAIL_PROTOCOL, "smtp"); + p.put(SMTP_HOST, this.host); + p.put(SMTP_PORT, String.valueOf(this.port)); + p.put(SMTP_AUTH, String.valueOf(this.auth)); + if (this.timeout > 0) { + p.put(SMTP_TIMEOUT, String.valueOf(this.timeout)); + } + if (this.connectionTimeout > 0) { + p.put(SMTP_CONNECTION_TIMEOUT, String.valueOf(this.connectionTimeout)); + } + // issue#2355 + if (this.writeTimeout > 0) { + p.put(SMTP_WRITE_TIMEOUT, String.valueOf(this.writeTimeout)); + } + + p.put(MAIL_DEBUG, String.valueOf(this.debug)); + + if (this.starttlsEnable) { + //STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + p.put(STARTTLS_ENABLE, "true"); + + if (null == this.sslEnable) { + //为了兼容旧版本,当用户没有此项配置时,按照starttlsEnable开启状态时对待 + this.sslEnable = true; + } + } + + // SSL + if (null != this.sslEnable && this.sslEnable) { + p.put(SSL_ENABLE, "true"); + p.put(SOCKET_FACTORY, socketFactoryClass); + p.put(SOCKET_FACTORY_FALLBACK, String.valueOf(this.socketFactoryFallback)); + p.put(SOCKET_FACTORY_PORT, String.valueOf(this.socketFactoryPort)); + // issue#IZN95@Gitee,在Linux下需自定义SSL协议版本 + if (StrUtil.isNotBlank(this.sslProtocols)) { + p.put(SSL_PROTOCOLS, this.sslProtocols); + } + } + + // 补充自定义属性,允许自定属性覆盖已经设置的值 + p.putAll(this.customProperty); + + return p; + } + + /** + * 如果某些值为null,使用默认值 + * + * @return this + */ + public MailAccount defaultIfEmpty() { + // 去掉发件人的姓名部分 + final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress(); + + if (StrUtil.isBlank(this.host)) { + // 如果SMTP地址为空,默认使用smtp.<发件人邮箱后缀> + this.host = StrUtil.format("smtp.{}", StrUtil.subSuf(fromAddress, fromAddress.indexOf('@') + 1)); + } + if (StrUtil.isBlank(user)) { + // 如果用户名为空,默认为发件人(issue#I4FYVY@Gitee) + //this.user = StrUtil.subPre(fromAddress, fromAddress.indexOf('@')); + this.user = fromAddress; + } + if (null == this.auth) { + // 如果密码非空白,则使用认证模式 + this.auth = (false == StrUtil.isBlank(this.pass)); + } + if (null == this.port) { + // 端口在SSL状态下默认与socketFactoryPort一致,非SSL状态下默认为25 + this.port = (null != this.sslEnable && this.sslEnable) ? this.socketFactoryPort : 25; + } + if (null == this.charset) { + // 默认UTF-8编码 + this.charset = CharsetUtil.CHARSET_UTF_8; + } + + return this; + } + + @Override + public String toString() { + return "MailAccount [host=" + host + ", port=" + port + ", auth=" + auth + ", user=" + user + ", pass=" + (StrUtil.isEmpty(this.pass) ? "" : "******") + ", from=" + from + ", startttlsEnable=" + + starttlsEnable + ", socketFactoryClass=" + socketFactoryClass + ", socketFactoryFallback=" + socketFactoryFallback + ", socketFactoryPort=" + socketFactoryPort + "]"; + } +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailException.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailException.java new file mode 100644 index 00000000..c5caaf36 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailException.java @@ -0,0 +1,40 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.Serial; + +/** + * 邮件异常 + * + * @author xiaoleilu + */ +public class MailException extends RuntimeException { + @Serial + private static final long serialVersionUID = 8247610319171014183L; + + public MailException(Throwable e) { + super(ExceptionUtil.getMessage(e), e); + } + + public MailException(String message) { + super(message); + } + + public MailException(String messageTemplate, Object... params) { + super(StrUtil.format(messageTemplate, params)); + } + + public MailException(String message, Throwable throwable) { + super(message, throwable); + } + + public MailException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { + super(message, throwable, enableSuppression, writableStackTrace); + } + + public MailException(Throwable throwable, String messageTemplate, Object... params) { + super(StrUtil.format(messageTemplate, params), throwable); + } +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailUtils.java new file mode 100644 index 00000000..8a422b62 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/MailUtils.java @@ -0,0 +1,468 @@ +package com.xmzs.common.mail.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.mail.Authenticator; +import jakarta.mail.Session; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param user 发送人 + * @param pass 授权码 + */ + public static MailAccount getMailAccount(String from, String user, String pass) { + ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); + ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); + return ACCOUNT; + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendText(String to, String subject, String content, File... files) { + return send(to, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, File... files) { + return send(to, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + */ + public static String sendText(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { + return send(to, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { + return send(tos, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, + boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + /** + * 根据配置文件,获取邮件客户端会话 + * + * @param mailAccount 邮件账户配置 + * @param isSingleton 是否单例(全局共享会话) + * @return {@link Session} + * @since 5.5.7 + */ + public static Session getSession(MailAccount mailAccount, boolean isSingleton) { + Authenticator authenticator = null; + if (mailAccount.isAuth()) { + authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + } + + return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // + : Session.getInstance(mailAccount.getSmtpProps(), authenticator); + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method start + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param useGlobalSession 是否全局共享Session + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:${cid} + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, + Map imageMap, boolean isHtml, File... files) { + final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession); + + // 可选抄送人 + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + // 可选密送人 + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + + mail.setTos(tos.toArray(new String[0])); + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(isHtml); + mail.setFiles(files); + + // 图片 + if (MapUtil.isNotEmpty(imageMap)) { + for (Map.Entry entry : imageMap.entrySet()) { + mail.addImage(entry.getKey(), entry.getValue()); + // 关闭流 + IoUtil.close(entry.getValue()); + } + } + + return mail.send(); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method end + +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/UserPassAuthenticator.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/UserPassAuthenticator.java new file mode 100644 index 00000000..9f3ebac2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/xmzs/common/mail/utils/UserPassAuthenticator.java @@ -0,0 +1,33 @@ +package com.xmzs.common.mail.utils; + +import jakarta.mail.Authenticator; +import jakarta.mail.PasswordAuthentication; + +/** + * 用户名密码验证器 + * + * @author looly + * @since 3.1.2 + */ +public class UserPassAuthenticator extends Authenticator { + + private final String user; + private final String pass; + + /** + * 构造 + * + * @param user 用户名 + * @param pass 密码 + */ + public UserPassAuthenticator(String user, String pass) { + this.user = user; + this.pass = pass; + } + + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(this.user, this.pass); + } + +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..a9d39906 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.mail.config.MailConfig diff --git a/ruoyi-common/ruoyi-common-mybatis/pom.xml b/ruoyi-common/ruoyi-common-mybatis/pom.xml new file mode 100644 index 00000000..227b902e --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/pom.xml @@ -0,0 +1,48 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-mybatis + + + ruoyi-common-mybatis 数据库服务 + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-satoken + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + + + + com.baomidou + mybatis-plus-boot-starter + + + + + p6spy + p6spy + + + + diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java new file mode 100644 index 00000000..f0a50a2d --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2018 organization baomidou + * + * 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. + */ +package com.baomidou.dynamic.datasource.processor.jakarta; + +import com.baomidou.dynamic.datasource.processor.DsProcessor; +import jakarta.servlet.http.HttpServletRequest; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * @author TaoYu + * @since 3.6.0 + */ +public class DsJakartaHeaderProcessor extends DsProcessor { + + /** + * header prefix + */ + private static final String HEADER_PREFIX = "#header"; + + @Override + public boolean matches(String key) { + return key.startsWith(HEADER_PREFIX); + } + + @Override + public String doDetermineDatasource(MethodInvocation invocation, String key) { + HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + return request.getHeader(key.substring(8)); + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java new file mode 100644 index 00000000..0ea8a130 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java @@ -0,0 +1,46 @@ +/* + * Copyright © 2018 organization baomidou + * + * 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. + */ +package com.baomidou.dynamic.datasource.processor.jakarta; + +import com.baomidou.dynamic.datasource.processor.DsProcessor; +import jakarta.servlet.http.HttpServletRequest; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + + +/** + * @author TaoYu + * @since 3.6.0 + */ +public class DsJakartaSessionProcessor extends DsProcessor { + + /** + * session开头 + */ + private static final String SESSION_PREFIX = "#session"; + + @Override + public boolean matches(String key) { + return key.startsWith(SESSION_PREFIX); + } + + @Override + public String doDetermineDatasource(MethodInvocation invocation, String key) { + HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + return request.getSession().getAttribute(key.substring(9)).toString(); + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataColumn.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataColumn.java new file mode 100644 index 00000000..5b86fa1f --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataColumn.java @@ -0,0 +1,28 @@ +package com.xmzs.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限 + * + * 一个注解只能对应一个模板 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataColumn { + + /** + * 占位符关键字 + */ + String[] key() default "deptName"; + + /** + * 占位符替换值 + */ + String[] value() default "dept_id"; + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataPermission.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataPermission.java new file mode 100644 index 00000000..8737eee6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/annotation/DataPermission.java @@ -0,0 +1,18 @@ +package com.xmzs.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限组 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + DataColumn[] value(); + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/config/MybatisPlusConfig.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/config/MybatisPlusConfig.java new file mode 100644 index 00000000..9ffde5e5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/config/MybatisPlusConfig.java @@ -0,0 +1,102 @@ +package com.xmzs.common.mybatis.config; + +import cn.hutool.core.net.NetUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.xmzs.common.mybatis.handler.InjectionMetaObjectHandler; +import com.xmzs.common.mybatis.interceptor.PlusDataPermissionInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * mybatis-plus配置类(下方注释有插件介绍) + * + * @author Lion Li + */ +@EnableTransactionManagement(proxyTargetClass = true) +@AutoConfiguration +@MapperScan("${mybatis-plus.mapperPackage}") +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 数据权限处理 + interceptor.addInnerInterceptor(dataPermissionInterceptor()); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + return interceptor; + } + + /** + * 数据权限拦截器 + */ + public PlusDataPermissionInterceptor dataPermissionInterceptor() { + return new PlusDataPermissionInterceptor(); + } + + /** + * 分页插件,自动识别数据库类型 + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + // 分页合理化 + paginationInnerInterceptor.setOverflow(true); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 元对象字段填充控制器 + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new InjectionMetaObjectHandler(); + } + + /** + * 使用网卡信息绑定雪花生成器 + * 防止集群雪花ID重复 + */ + @Bean + public IdentifierGenerator idGenerator() { + return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); + } + + /** + * PaginationInnerInterceptor 分页插件,自动识别数据库类型 + * https://baomidou.com/pages/97710a/ + * OptimisticLockerInnerInterceptor 乐观锁插件 + * https://baomidou.com/pages/0d93c0/ + * MetaObjectHandler 元对象字段填充控制器 + * https://baomidou.com/pages/4c6bcf/ + * ISqlInjector sql注入器 + * https://baomidou.com/pages/42ea4a/ + * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作 + * https://baomidou.com/pages/f9a237/ + * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截) + * IdentifierGenerator 自定义主键策略 + * https://baomidou.com/pages/568eb2/ + * TenantLineInnerInterceptor 多租户插件 + * https://baomidou.com/pages/aef2f2/ + * DynamicTableNameInnerInterceptor 动态表名插件 + * https://baomidou.com/pages/2a45ff/ + */ + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/domain/BaseEntity.java new file mode 100644 index 00000000..732f414e --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/domain/BaseEntity.java @@ -0,0 +1,71 @@ +package com.xmzs.common.mybatis.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author Lion Li + */ + +@Data +public class BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建部门 + */ + @TableField(fill = FieldFill.INSERT) + private Long createDept; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private Long createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/mapper/BaseMapperPlus.java new file mode 100644 index 00000000..8db817b3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/mapper/BaseMapperPlus.java @@ -0,0 +1,198 @@ +package com.xmzs.common.mybatis.core.mapper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import com.xmzs.common.core.utils.MapstructUtils; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @param table 泛型 + * @param vo 泛型 + * @author Lion Li + * @since 2021-05-13 + */ +@SuppressWarnings("unchecked") +public interface BaseMapperPlus extends BaseMapper { + + Log log = LogFactory.getLog(BaseMapperPlus.class); + + default Class currentVoClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1); + } + + default Class currentModelClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0); + } + + default List selectList() { + return this.selectList(new QueryWrapper<>()); + } + + /** + * 批量插入 + */ + default boolean insertBatch(Collection entityList) { + return Db.saveBatch(entityList); + } + + /** + * 批量更新 + */ + default boolean updateBatchById(Collection entityList) { + return Db.updateBatchById(entityList); + } + + /** + * 批量插入或更新 + */ + default boolean insertOrUpdateBatch(Collection entityList) { + return Db.saveOrUpdateBatch(entityList); + } + + /** + * 批量插入(包含限制条数) + */ + default boolean insertBatch(Collection entityList, int batchSize) { + return Db.saveBatch(entityList, batchSize); + } + + /** + * 批量更新(包含限制条数) + */ + default boolean updateBatchById(Collection entityList, int batchSize) { + return Db.updateBatchById(entityList, batchSize); + } + + /** + * 批量插入或更新(包含限制条数) + */ + default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { + return Db.saveOrUpdateBatch(entityList, batchSize); + } + + /** + * 插入或更新(包含限制条数) + */ + default boolean insertOrUpdate(T entity) { + return Db.saveOrUpdate(entity); + } + + default V selectVoById(Serializable id) { + return selectVoById(id, this.currentVoClass()); + } + + /** + * 根据 ID 查询 + */ + default C selectVoById(Serializable id, Class voClass) { + T obj = this.selectById(id); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + default List selectVoBatchIds(Collection idList) { + return selectVoBatchIds(idList, this.currentVoClass()); + } + + /** + * 查询(根据ID 批量查询) + */ + default List selectVoBatchIds(Collection idList, Class voClass) { + List list = this.selectBatchIds(idList); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + default List selectVoByMap(Map map) { + return selectVoByMap(map, this.currentVoClass()); + } + + /** + * 查询(根据 columnMap 条件) + */ + default List selectVoByMap(Map map, Class voClass) { + List list = this.selectByMap(map); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + default V selectVoOne(Wrapper wrapper) { + return selectVoOne(wrapper, this.currentVoClass()); + } + + /** + * 根据 entity 条件,查询一条记录 + */ + default C selectVoOne(Wrapper wrapper, Class voClass) { + T obj = this.selectOne(wrapper); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + default List selectVoList() { + return selectVoList(new QueryWrapper<>(), this.currentVoClass()); + } + + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + /** + * 根据 entity 条件,查询全部记录 + */ + default List selectVoList(Wrapper wrapper, Class voClass) { + List list = this.selectList(wrapper); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + default

> P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + /** + * 分页查询VO + */ + default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + IPage pageData = this.selectPage(page, wrapper); + IPage voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()); + if (CollUtil.isEmpty(pageData.getRecords())) { + return (P) voPage; + } + voPage.setRecords(MapstructUtils.convert(pageData.getRecords(), voClass)); + return (P) voPage; + } + + default List selectObjs(Wrapper wrapper, Function mapper) { + return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList()); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/PageQuery.java new file mode 100644 index 00000000..4e43f11d --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/PageQuery.java @@ -0,0 +1,114 @@ +package com.xmzs.common.mybatis.core.page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.sql.SqlUtil; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 分页查询实体类 + * + * @author Lion Li + */ + +@Data +public class PageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 分页大小 + */ + private Integer pageSize; + + /** + * 当前页数 + */ + private Integer pageNum; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc; + + /** + * 当前记录起始索引 默认值 + */ + public static final int DEFAULT_PAGE_NUM = 1; + + /** + * 每页显示记录数 默认值 默认查全部 + */ + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + + public Page build() { + Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); + Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); + if (pageNum <= 0) { + pageNum = DEFAULT_PAGE_NUM; + } + Page page = new Page<>(pageNum, pageSize); + List orderItems = buildOrderItem(); + if (CollUtil.isNotEmpty(orderItems)) { + page.addOrder(orderItems); + } + return page; + } + + /** + * 构建排序 + * + * 支持的用法如下: + * {isAsc:"asc",orderByColumn:"id"} order by id asc + * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc + * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc + * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc + */ + private List buildOrderItem() { + if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { + return null; + } + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + orderBy = StringUtils.toUnderScoreCase(orderBy); + + // 兼容前端排序类型 + isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); + + String[] orderByArr = orderBy.split(StringUtils.SEPARATOR); + String[] isAscArr = isAsc.split(StringUtils.SEPARATOR); + if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { + throw new ServiceException("排序参数有误"); + } + + List list = new ArrayList<>(); + // 每个字段各自排序 + for (int i = 0; i < orderByArr.length; i++) { + String orderByStr = orderByArr[i]; + String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i]; + if ("asc".equals(isAscStr)) { + list.add(OrderItem.asc(orderByStr)); + } else if ("desc".equals(isAscStr)) { + list.add(OrderItem.desc(orderByStr)); + } else { + throw new ServiceException("排序参数有误"); + } + } + return list; + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/TableDataInfo.java new file mode 100644 index 00000000..631f83b8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/core/page/TableDataInfo.java @@ -0,0 +1,81 @@ +package com.xmzs.common.mybatis.core.page; + +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class TableDataInfo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) { + this.rows = list; + this.total = total; + } + + public static TableDataInfo build(IPage page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + public static TableDataInfo build(List list) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(list.size()); + return rspData; + } + + public static TableDataInfo build() { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + return rspData; + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataBaseType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataBaseType.java new file mode 100644 index 00000000..5e7d47ce --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataBaseType.java @@ -0,0 +1,49 @@ +package com.xmzs.common.mybatis.enums; + +import com.xmzs.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据库类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DataBaseType { + + /** + * MySQL + */ + MY_SQL("MySQL"), + + /** + * Oracle + */ + ORACLE("Oracle"), + + /** + * PostgreSQL + */ + POSTGRE_SQL("PostgreSQL"), + + /** + * SQL Server + */ + SQL_SERVER("Microsoft SQL Server"); + + private final String type; + + public static DataBaseType find(String databaseProductName) { + if (StringUtils.isBlank(databaseProductName)) { + return null; + } + for (DataBaseType type : values()) { + if (type.getType().equals(databaseProductName)) { + return type; + } + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataScopeType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataScopeType.java new file mode 100644 index 00000000..7b566e9d --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/enums/DataScopeType.java @@ -0,0 +1,73 @@ +package com.xmzs.common.mybatis.enums; + +import com.xmzs.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.xmzs.common.mybatis.helper.DataPermissionHelper; + +/** + * 数据权限类型 + *

+ * 语法支持 spel 模板表达式 + *

+ * 内置数据 user 当前用户 内容参考 LoginUser + * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作 + * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService + * 如需扩展更多自定义服务 可以参考 sdss 自行编写 + * + * @author Lion Li + * @version 3.5.0 + */ +@Getter +@AllArgsConstructor +public enum DataScopeType { + + /** + * 全部数据权限 + */ + ALL("1", "", ""), + + /** + * 自定数据权限 + */ + CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""), + + /** + * 部门数据权限 + */ + DEPT("3", " #{#deptName} = #{#user.deptId} ", ""), + + /** + * 部门及以下数据权限 + */ + DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""), + + /** + * 仅本人数据权限 + */ + SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "); + + private final String code; + + /** + * 语法 采用 spel 模板表达式 + */ + private final String sqlTemplate; + + /** + * 不满足 sqlTemplate 则填充 + */ + private final String elseSql; + + public static DataScopeType findCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + for (DataScopeType type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/InjectionMetaObjectHandler.java new file mode 100644 index 00000000..cc37dcd5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -0,0 +1,81 @@ +package com.xmzs.common.mybatis.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.common.satoken.utils.LoginHelper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; + +import java.util.Date; + +/** + * MP注入处理器 + * + * @author Lion Li + * @date 2021/4/25 + */ +@Slf4j +public class InjectionMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) + ? baseEntity.getCreateTime() : new Date(); + baseEntity.setCreateTime(current); + baseEntity.setUpdateTime(current); + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = ObjectUtil.isNotNull(baseEntity.getCreateBy()) + ? baseEntity.getCreateBy() : loginUser.getUserId(); + // 当前已登录 且 创建人为空 则填充 + baseEntity.setCreateBy(userId); + // 当前已登录 且 更新人为空 则填充 + baseEntity.setUpdateBy(userId); + baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) + ? baseEntity.getCreateDept() : loginUser.getDeptId()); + } + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + Date current = new Date(); + // 更新时间填充(不管为不为空) + baseEntity.setUpdateTime(current); + LoginUser loginUser = getLoginUser(); + // 当前已登录 更新人填充(不管为不为空) + if (ObjectUtil.isNotNull(loginUser)) { + baseEntity.setUpdateBy(loginUser.getUserId()); + } + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取登录用户名 + */ + private LoginUser getLoginUser() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + log.warn("自动注入警告 => 用户未登录"); + return null; + } + return loginUser; + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/MybatisExceptionHandler.java new file mode 100644 index 00000000..b072fa1d --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/MybatisExceptionHandler.java @@ -0,0 +1,46 @@ +package com.xmzs.common.mybatis.handler; + +import com.xmzs.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * Mybatis异常处理器 + * + * @author Lion Li + */ +@Slf4j +@RestControllerAdvice +public class MybatisExceptionHandler { + + /** + * 主键或UNIQUE索引,数据重复异常 + */ + @ExceptionHandler(DuplicateKeyException.class) + public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); + return R.fail("数据库中已存在该记录,请联系管理员确认"); + } + + /** + * Mybatis系统异常 通用处理 + */ + @ExceptionHandler(MyBatisSystemException.class) + public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + String message = e.getMessage(); + if (message.contains("CannotFindDataSourceException")) { + log.error("请求地址'{}', 未找到数据源", requestURI); + return R.fail("未找到数据源,请联系管理员确认"); + } + log.error("请求地址'{}', Mybatis系统异常", requestURI, e); + return R.fail(message); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/PlusDataPermissionHandler.java new file mode 100644 index 00000000..87c312ee --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/handler/PlusDataPermissionHandler.java @@ -0,0 +1,198 @@ +package com.xmzs.common.mybatis.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ConcurrentHashSet; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.common.core.domain.dto.RoleDTO; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.enums.DataScopeType; +import com.xmzs.common.mybatis.helper.DataPermissionHelper; +import com.xmzs.common.satoken.utils.LoginHelper; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Parenthesis; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.expression.BeanResolver; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * 数据权限过滤 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionHandler { + + /** + * 方法或类(名称) 与 注解的映射关系缓存 + */ + private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); + + /** + * 无效注解方法缓存用于快速返回 + */ + private final Set invalidCacheSet = new ConcurrentHashSet<>(); + + /** + * spel 解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + /** + * bean解析器 用于处理 spel 表达式中对 bean 的调用 + */ + private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + + + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + DataColumn[] dataColumns = findAnnotation(mappedStatementId); + if (ArrayUtil.isEmpty(dataColumns)) { + invalidCacheSet.add(mappedStatementId); + return where; + } + LoginUser currentUser = DataPermissionHelper.getVariable("user"); + if (ObjectUtil.isNull(currentUser)) { + currentUser = LoginHelper.getLoginUser(); + DataPermissionHelper.setVariable("user", currentUser); + } + // 如果是超级管理员或租户管理员,则不过滤数据 + if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { + return where; + } + String dataFilterSql = buildDataFilter(dataColumns, isSelect); + if (StringUtils.isBlank(dataFilterSql)) { + return where; + } + try { + Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); + // 数据权限使用单独的括号 防止与其他条件冲突 + Parenthesis parenthesis = new Parenthesis(expression); + if (ObjectUtil.isNotNull(where)) { + return new AndExpression(where, parenthesis); + } else { + return parenthesis; + } + } catch (JSQLParserException e) { + throw new ServiceException("数据权限解析异常 => " + e.getMessage()); + } + } + + /** + * 构造数据过滤sql + */ + private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { + // 更新或删除需满足所有条件 + String joinStr = isSelect ? " OR " : " AND "; + LoginUser user = DataPermissionHelper.getVariable("user"); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.setBeanResolver(beanResolver); + DataPermissionHelper.getContext().forEach(context::setVariable); + Set conditions = new HashSet<>(); + for (RoleDTO role : user.getRoles()) { + user.setRoleId(role.getRoleId()); + // 获取角色权限泛型 + DataScopeType type = DataScopeType.findCode(role.getDataScope()); + if (ObjectUtil.isNull(type)) { + throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); + } + // 全部数据权限直接返回 + if (type == DataScopeType.ALL) { + return ""; + } + boolean isSuccess = false; + for (DataColumn dataColumn : dataColumns) { + if (dataColumn.key().length != dataColumn.value().length) { + throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); + } + // 不包含 key 变量 则不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), + Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new) + )) { + continue; + } + // 设置注解变量 key 为表达式变量 value 为变量值 + for (int i = 0; i < dataColumn.key().length; i++) { + context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); + } + + // 解析sql模板并填充 + String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class); + conditions.add(joinStr + sql); + isSuccess = true; + } + // 未处理成功则填充兜底方案 + if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { + conditions.add(joinStr + type.getElseSql()); + } + } + + if (CollUtil.isNotEmpty(conditions)) { + String sql = StreamUtils.join(conditions, Function.identity(), ""); + return sql.substring(joinStr.length()); + } + return ""; + } + + private DataColumn[] findAnnotation(String mappedStatementId) { + StringBuilder sb = new StringBuilder(mappedStatementId); + int index = sb.lastIndexOf("."); + String clazzName = sb.substring(0, index); + String methodName = sb.substring(index + 1, sb.length()); + Class clazz = ClassUtil.loadClass(clazzName); + List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) + .filter(method -> method.getName().equals(methodName)).toList(); + DataPermission dataPermission; + // 获取方法注解 + for (Method method : methods) { + dataPermission = dataPermissionCacheMap.get(mappedStatementId); + if (ObjectUtil.isNotNull(dataPermission)) { + return dataPermission.value(); + } + if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); + dataPermissionCacheMap.put(mappedStatementId, dataPermission); + return dataPermission.value(); + } + } + dataPermission = dataPermissionCacheMap.get(clazz.getName()); + if (ObjectUtil.isNotNull(dataPermission)) { + return dataPermission.value(); + } + // 获取类注解 + if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); + dataPermissionCacheMap.put(clazz.getName(), dataPermission); + return dataPermission.value(); + } + return null; + } + + /** + * 是否为无效方法 无数据权限 + */ + public boolean isInvalid(String mappedStatementId) { + return invalidCacheSet.contains(mappedStatementId); + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataBaseHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataBaseHelper.java new file mode 100644 index 00000000..6306857d --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataBaseHelper.java @@ -0,0 +1,72 @@ +package com.xmzs.common.mybatis.helper; + +import cn.hutool.core.convert.Convert; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.mybatis.enums.DataBaseType; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * 数据库助手 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataBaseHelper { + + private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); + + /** + * 获取当前数据库类型 + */ + public static DataBaseType getDataBaseType() { + DataSource dataSource = DS.determineDataSource(); + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new ServiceException(e.getMessage()); + } + } + + public static boolean isMySql() { + return DataBaseType.MY_SQL == getDataBaseType(); + } + + public static boolean isOracle() { + return DataBaseType.ORACLE == getDataBaseType(); + } + + public static boolean isPostgerSql() { + return DataBaseType.POSTGRE_SQL == getDataBaseType(); + } + + public static boolean isSqlServer() { + return DataBaseType.SQL_SERVER == getDataBaseType(); + } + + public static String findInSet(Object var1, String var2) { + DataBaseType dataBasyType = getDataBaseType(); + String var = Convert.toStr(var1); + if (dataBasyType == DataBaseType.SQL_SERVER) { + // charindex(',100,' , ',0,100,101,') <> 0 + return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2); + } else if (dataBasyType == DataBaseType.POSTGRE_SQL) { + // (select position(',100,' in ',0,100,101,')) <> 0 + return "(select position(',%s,' in ','||%s||',')) <> 0".formatted(var, var2); + } else if (dataBasyType == DataBaseType.ORACLE) { + // instr(',0,100,101,' , ',100,') <> 0 + return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var); + } + // find_in_set(100 , '0,100,101') + return "find_in_set('%s' , %s) <> 0".formatted(var, var2); + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataPermissionHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataPermissionHelper.java new file mode 100644 index 00000000..74e5a210 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/helper/DataPermissionHelper.java @@ -0,0 +1,93 @@ +package com.xmzs.common.mybatis.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 数据权限助手 + * + * @author Lion Li + * @version 3.5.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked cast") +public class DataPermissionHelper { + + private static final String DATA_PERMISSION_KEY = "data:permission"; + + public static T getVariable(String key) { + Map context = getContext(); + return (T) context.get(key); + } + + + public static void setVariable(String key, Object value) { + Map context = getContext(); + context.put(key, value); + } + + public static Map getContext() { + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); + if (ObjectUtil.isNull(attribute)) { + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); + } + if (attribute instanceof Map map) { + return map; + } + throw new NullPointerException("data permission context type exception"); + } + + /** + * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) + */ + public static void enableIgnore() { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } + + /** + * 关闭忽略数据权限 + */ + public static void disableIgnore() { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 00000000..e434c241 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/xmzs/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -0,0 +1,107 @@ +package com.xmzs.common.mybatis.interceptor; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import com.xmzs.common.mybatis.handler.PlusDataPermissionHandler; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author Lion Li + * @version 3.5.0 + */ +public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { + + private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 检查忽略注解 + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否无效 无数据权限注解 + if (dataPermissionHandler.isInvalid(ms.getId())) { + return; + } + // 解析 sql 分配对应方法 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + SqlCommandType sct = ms.getSqlCommandType(); + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + SelectBody selectBody = select.getSelectBody(); + if (selectBody instanceof PlainSelect plainSelect) { + this.setWhere(plainSelect, (String) obj); + } else if (selectBody instanceof SetOperationList setOperationList) { + List selectBodyList = setOperationList.getSelects(); + selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj)); + } + } + + @Override + protected void processUpdate(Update update, int index, String sql, Object obj) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); + if (null != sqlSegment) { + update.setWhere(sqlSegment); + } + } + + @Override + protected void processDelete(Delete delete, int index, String sql, Object obj) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); + if (null != sqlSegment) { + delete.setWhere(sqlSegment); + } + } + + /** + * 设置 where 条件 + * + * @param plainSelect 查询对象 + * @param mappedStatementId 执行方法id + */ + protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); + if (null != sqlSegment) { + plainSelect.setWhere(sqlSegment); + } + } + +} + diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..70d6416b --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.mybatis.config.MybatisPlusConfig diff --git a/ruoyi-common/ruoyi-common-oss/pom.xml b/ruoyi-common/ruoyi-common-oss/pom.xml new file mode 100644 index 00000000..c5b0e7c8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/pom.xml @@ -0,0 +1,42 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-oss + + + ruoyi-common-oss oss服务 + + + + + com.xmzs + ruoyi-common-json + + + + com.xmzs + ruoyi-common-redis + + + + com.amazonaws + aws-java-sdk-s3 + + + + com.qcloud + cos_api + 5.6.75 + + + + diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/constant/OssConstant.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/constant/OssConstant.java new file mode 100644 index 00000000..5b7d456f --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/constant/OssConstant.java @@ -0,0 +1,38 @@ +package com.xmzs.common.oss.constant; + +import java.util.Arrays; +import java.util.List; + +/** + * 对象存储常量 + * + * @author Lion Li + */ +public interface OssConstant { + + /** + * 默认配置KEY + */ + String DEFAULT_CONFIG_KEY = "sys_oss:default_config"; + + /** + * 预览列表资源开关Key + */ + String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource"; + + /** + * 系统数据ids + */ + List SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L); + + /** + * 云服务商 + */ + String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"}; + + /** + * https 状态 + */ + String IS_HTTPS = "Y"; + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/core/OssClient.java new file mode 100644 index 00000000..3eda58a6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/core/OssClient.java @@ -0,0 +1,245 @@ +package com.xmzs.common.oss.core; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import com.amazonaws.ClientConfiguration; +import com.amazonaws.HttpMethod; +import com.amazonaws.Protocol; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.*; +import com.xmzs.common.core.utils.DateUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.oss.constant.OssConstant; +import com.xmzs.common.oss.entity.UploadResult; +import com.xmzs.common.oss.enumd.AccessPolicyType; +import com.xmzs.common.oss.enumd.PolicyType; +import com.xmzs.common.oss.exception.OssException; +import com.xmzs.common.oss.properties.OssProperties; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.Date; + +/** + * S3 存储协议 所有兼容S3协议的云厂商均支持 + * 阿里云 腾讯云 七牛云 minio + * + * @author Lion Li + */ +public class OssClient { + + private final String configKey; + + private final OssProperties properties; + + private final AmazonS3 client; + + public OssClient(String configKey, OssProperties ossProperties) { + this.configKey = configKey; + this.properties = ossProperties; + try { + AwsClientBuilder.EndpointConfiguration endpointConfig = + new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion()); + + AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); + AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials); + ClientConfiguration clientConfig = new ClientConfiguration(); + if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) { + clientConfig.setProtocol(Protocol.HTTPS); + } else { + clientConfig.setProtocol(Protocol.HTTP); + } + AmazonS3ClientBuilder build = AmazonS3Client.builder() + .withEndpointConfiguration(endpointConfig) + .withClientConfiguration(clientConfig) + .withCredentials(credentialsProvider) + .disableChunkedEncoding(); + if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) { + // minio 使用https限制使用域名访问 需要此配置 站点填域名 + build.enablePathStyleAccess(); + } + this.client = build.build(); + + createBucket(); + } catch (Exception e) { + if (e instanceof OssException) { + throw e; + } + throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]"); + } + } + + public void createBucket() { + try { + String bucketName = properties.getBucketName(); + if (client.doesBucketExistV2(bucketName)) { + return; + } + CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); + AccessPolicyType accessPolicy = getAccessPolicy(); + createBucketRequest.setCannedAcl(accessPolicy.getAcl()); + client.createBucket(createBucketRequest); + client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType())); + } catch (Exception e) { + throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]"); + } + } + + public UploadResult upload(byte[] data, String path, String contentType) { + return upload(new ByteArrayInputStream(data), path, contentType); + } + + public UploadResult upload(InputStream inputStream, String path, String contentType) { + if (!(inputStream instanceof ByteArrayInputStream)) { + inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream)); + } + try { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(contentType); + metadata.setContentLength(inputStream.available()); + PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata); + // 设置上传对象的 Acl 为公共读 + putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); + client.putObject(putObjectRequest); + } catch (Exception e) { + throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); + } + return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); + } + + public void delete(String path) { + path = path.replace(getUrl() + "/", ""); + try { + client.deleteObject(properties.getBucketName(), path); + } catch (Exception e) { + throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]"); + } + } + + public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { + return upload(data, getPath(properties.getPrefix(), suffix), contentType); + } + + public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) { + return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType); + } + + /** + * 获取文件元数据 + * + * @param path 完整文件路径 + */ + public ObjectMetadata getObjectMetadata(String path) { + path = path.replace(getUrl() + "/", ""); + S3Object object = client.getObject(properties.getBucketName(), path); + return object.getObjectMetadata(); + } + + public InputStream getObjectContent(String path) { + path = path.replace(getUrl() + "/", ""); + S3Object object = client.getObject(properties.getBucketName(), path); + return object.getObjectContent(); + } + + public String getUrl() { + String domain = properties.getDomain(); + String endpoint = properties.getEndpoint(); + String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://"; + // 云服务商直接返回 + if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { + if (StringUtils.isNotBlank(domain)) { + return header + domain; + } + return header + properties.getBucketName() + "." + endpoint; + } + // minio 单独处理 + if (StringUtils.isNotBlank(domain)) { + return header + domain + "/" + properties.getBucketName(); + } + return header + endpoint + "/" + properties.getBucketName(); + } + + public String getPath(String prefix, String suffix) { + // 生成uuid + String uuid = IdUtil.fastSimpleUUID(); + // 文件路径 + String path = DateUtils.datePath() + "/" + uuid; + if (StringUtils.isNotBlank(prefix)) { + path = prefix + "/" + path; + } + return path + suffix; + } + + + public String getConfigKey() { + return configKey; + } + + /** + * 获取私有URL链接 + * + * @param objectKey 对象KEY + * @param second 授权时间 + */ + public String getPrivateUrl(String objectKey, Integer second) { + GeneratePresignedUrlRequest generatePresignedUrlRequest = + new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey) + .withMethod(HttpMethod.GET) + .withExpiration(new Date(System.currentTimeMillis() + 1000L * second)); + URL url = client.generatePresignedUrl(generatePresignedUrlRequest); + return url.toString(); + } + + /** + * 检查配置是否相同 + */ + public boolean checkPropertiesSame(OssProperties properties) { + return this.properties.equals(properties); + } + + /** + * 获取当前桶权限类型 + * + * @return 当前桶权限类型code + */ + public AccessPolicyType getAccessPolicy() { + return AccessPolicyType.getByType(properties.getAccessPolicy()); + } + + private static String getPolicy(String bucketName, PolicyType policyType) { + StringBuilder builder = new StringBuilder(); + builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n"); + builder.append(switch (policyType) { + case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n"; + case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n"; + default -> "\"s3:GetBucketLocation\"\n"; + }); + builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); + builder.append(bucketName); + builder.append("\"\n},\n"); + if (policyType == PolicyType.READ) { + builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); + builder.append(bucketName); + builder.append("\"\n},\n"); + } + builder.append("{\n\"Action\": "); + builder.append(switch (policyType) { + case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; + case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; + default -> "\"s3:GetObject\",\n"; + }); + builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); + builder.append(bucketName); + builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n"); + return builder.toString(); + } + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/entity/UploadResult.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/entity/UploadResult.java new file mode 100644 index 00000000..df554459 --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/entity/UploadResult.java @@ -0,0 +1,24 @@ +package com.xmzs.common.oss.entity; + +import lombok.Builder; +import lombok.Data; + +/** + * 上传返回体 + * + * @author Lion Li + */ +@Data +@Builder +public class UploadResult { + + /** + * 文件路径 + */ + private String url; + + /** + * 文件名 + */ + private String filename; +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/AccessPolicyType.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/AccessPolicyType.java new file mode 100644 index 00000000..0d71f94d --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/AccessPolicyType.java @@ -0,0 +1,55 @@ +package com.xmzs.common.oss.enumd; + +import com.amazonaws.services.s3.model.CannedAccessControlList; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 桶访问策略配置 + * + * @author 陈賝 + */ +@Getter +@AllArgsConstructor +public enum AccessPolicyType { + + /** + * private + */ + PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE), + + /** + * public + */ + PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ), + + /** + * custom + */ + CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ); + + /** + * 桶 权限类型 + */ + private final String type; + + /** + * 文件对象 权限类型 + */ + private final CannedAccessControlList acl; + + /** + * 桶策略类型 + */ + private final PolicyType policyType; + + public static AccessPolicyType getByType(String type) { + for (AccessPolicyType value : values()) { + if (value.getType().equals(type)) { + return value; + } + } + throw new RuntimeException("'type' not found By " + type); + } + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/PolicyType.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/PolicyType.java new file mode 100644 index 00000000..ad9d30a3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/enumd/PolicyType.java @@ -0,0 +1,35 @@ +package com.xmzs.common.oss.enumd; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * minio策略配置 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum PolicyType { + + /** + * 只读 + */ + READ("read-only"), + + /** + * 只写 + */ + WRITE("write-only"), + + /** + * 读写 + */ + READ_WRITE("read-write"); + + /** + * 类型 + */ + private final String type; + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/exception/OssException.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/exception/OssException.java new file mode 100644 index 00000000..0029ca6a --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/exception/OssException.java @@ -0,0 +1,19 @@ +package com.xmzs.common.oss.exception; + +import java.io.Serial; + +/** + * OSS异常类 + * + * @author Lion Li + */ +public class OssException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + public OssException(String msg) { + super(msg); + } + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/factory/OssFactory.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/factory/OssFactory.java new file mode 100644 index 00000000..220c092c --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/factory/OssFactory.java @@ -0,0 +1,63 @@ +package com.xmzs.common.oss.factory; + +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.oss.constant.OssConstant; +import com.xmzs.common.oss.core.OssClient; +import com.xmzs.common.oss.exception.OssException; +import com.xmzs.common.oss.properties.OssProperties; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.common.redis.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 文件上传Factory + * + * @author Lion Li + */ +@Slf4j +public class OssFactory { + + private static final Map CLIENT_CACHE = new ConcurrentHashMap<>(); + + /** + * 获取默认实例 + */ + public static OssClient instance() { + // 获取redis 默认类型 + String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY); + if (StringUtils.isEmpty(configKey)) { + throw new OssException("文件存储服务类型无法找到!"); + } + return instance(configKey); + } + + /** + * 根据类型获取实例 + */ + public static OssClient instance(String configKey) { + String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); + if (json == null) { + throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); + } + OssProperties properties = JsonUtils.parseObject(json, OssProperties.class); + OssClient client = CLIENT_CACHE.get(configKey); + if (client == null) { + CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); + log.info("创建OSS实例 key => {}", configKey); + return CLIENT_CACHE.get(configKey); + } + // 配置不相同则重新构建 + if (!client.checkPropertiesSame(properties)) { + CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); + log.info("重载OSS实例 key => {}", configKey); + return CLIENT_CACHE.get(configKey); + } + return client; + } + +} diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/properties/OssProperties.java b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/properties/OssProperties.java new file mode 100644 index 00000000..cbc5df7e --- /dev/null +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/com/xmzs/common/oss/properties/OssProperties.java @@ -0,0 +1,58 @@ +package com.xmzs.common.oss.properties; + +import lombok.Data; + +/** + * OSS对象存储 配置属性 + * + * @author Lion Li + */ +@Data +public class OssProperties { + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 前缀 + */ + private String prefix; + + /** + * ACCESS_KEY + */ + private String accessKey; + + /** + * SECRET_KEY + */ + private String secretKey; + + /** + * 存储空间名 + */ + private String bucketName; + + /** + * 存储区域 + */ + private String region; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; + +} diff --git a/ruoyi-common/ruoyi-common-pay/pom.xml b/ruoyi-common/ruoyi-common-pay/pom.xml new file mode 100644 index 00000000..ff90424f --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + + ruoyi-common-pay + + + com.xmzs + ruoyi-common-web + + + com.google.zxing + core + 3.3.3 + + + org.springframework.boot + spring-boot-starter-test + test + + + cn.hutool + hutool-all + 5.8.12 + + + + 17 + 17 + UTF-8 + + + diff --git a/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/config/PayConfig.java b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/config/PayConfig.java new file mode 100644 index 00000000..1c06aad7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/config/PayConfig.java @@ -0,0 +1,51 @@ +package com.xmzs.common.config; + +/** + * 支付配置信息 + * + * @author Admin + */ +public class PayConfig { + + /** + * 商户ID + */ + public static String pid = "xx"; + + /** + * 支付方式 + */ + public static String type = "wxpay"; + + /** + * 接口地址 + */ + public static String payUrl = "https://pay.bluetuo.com/mapi.php"; + + /** + * 服务器异步通知地址 + */ + public static String notify_url = "http://xx/pay/returnUrl"; + + /** + * 页面跳转通知地址 + */ + public static String return_url = "http://xx/pay/notifyUrl"; + + /** + * 设备类型 + */ + public static String device = "pc"; + + /** + * 加密方式默认MD5 + */ + + public static String sign_type = "MD5"; + + /** + * 私钥 + */ + public static String key = "xx"; + +} diff --git a/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/response/PayResponse.java b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/response/PayResponse.java new file mode 100644 index 00000000..de1d7829 --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/response/PayResponse.java @@ -0,0 +1,71 @@ +package com.xmzs.common.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; + +/** + * 支付结果响应 + * + * @author: wangle + * @date: 2023/7/3 + */ +@Data +public class PayResponse { + + /** + * 商户ID + */ + private String pid; + + /** + * 易支付订单号 + */ + + @JsonProperty("trade_no") + private String trade_no; + + /** + * 商户订单号 + */ + @JsonProperty("out_trade_no") + private String out_trade_no; + + /** + * 支付方式 + */ + private String type; + + /** + * 商品名称 + */ + private String name; + + /** + * 商品金额 + */ + private String money; + + /** + * 支付状态 + */ + @JsonProperty("trade_status") + private String trade_status; + + /** + * 业务扩展参数 + */ + private String param; + + /** + * 签名字符串 + */ + private String sign; + + /** + * 签名类型 + */ + @JsonProperty("sign_type") + private String signType; + +} diff --git a/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/PayService.java b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/PayService.java new file mode 100644 index 00000000..21c00f56 --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/PayService.java @@ -0,0 +1,23 @@ +package com.xmzs.common.service; + +/** + * 支付服务 + * + * @author: wangle + * @date: 2023/7/3 + */ +public interface PayService { + + /** + * 获取支付地址 + * + * @Date 2023/7/3 + * @param orderNo + * @param name + * @param money + * @param clientIp + * @return String + **/ + String getPayUrl(String orderNo, String name, double money, String clientIp); + +} diff --git a/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/impl/PayServiceImpl.java b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/impl/PayServiceImpl.java new file mode 100644 index 00000000..ed215275 --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/service/impl/PayServiceImpl.java @@ -0,0 +1,44 @@ +package com.xmzs.common.service.impl; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import com.xmzs.common.config.PayConfig; +import com.xmzs.common.service.PayService; +import com.xmzs.common.utils.MD5Util; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * 支付服务 + * @author Admin + */ +@Service +public class PayServiceImpl implements PayService { + @Override + public String getPayUrl(String orderNo, String name, double money, String clientIp) { + String out_trade_no = orderNo, sign = ""; + //封装请求参数 + String mdString = "clientip=" + clientIp + "&device=" + PayConfig.device + "&money=" + money + "&name=" + name + "&" + + "notify_url=" + PayConfig.notify_url + "&out_trade_no=" + out_trade_no + "&pid=" + PayConfig.pid + "&return_url=" + PayConfig.return_url + + "&type=" + PayConfig.type + PayConfig.key; + sign = MD5Util.GetMD5Code(mdString); + Map map = new HashMap<>(10); + map.put("clientip", clientIp); + map.put("device", PayConfig.device); + map.put("money", money); + map.put("name", name); + map.put("notify_url", PayConfig.notify_url); + map.put("out_trade_no", out_trade_no); + map.put("pid", PayConfig.pid); + map.put("return_url", PayConfig.return_url); + map.put("sign_type", PayConfig.sign_type); + map.put("type", PayConfig.type); + map.put("sign", sign); + String body = HttpUtil.post(PayConfig.payUrl, map); + JSONObject jsonObject = new JSONObject(body); + return (String) jsonObject.get("qrcode"); + } + +} diff --git a/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/utils/MD5Util.java b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/utils/MD5Util.java new file mode 100644 index 00000000..1327cca3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-pay/src/main/java/com/xmzs/common/utils/MD5Util.java @@ -0,0 +1,107 @@ +package com.xmzs.common.utils; + +import cn.hutool.core.util.StrUtil; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.TreeMap; + +/** + * MD5 算法 + * + * @author Admin + */ +public class MD5Util { + + /** + * 全局数组 + */ + public final static String[] strDigits = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + + public MD5Util() { + } + + /** + * 返回形式为数字跟字符串 + * + * @Date 2023/7/3 + * @param bByte + * @return String + **/ + public static String byteToArrayString(byte bByte) { + int iRet = bByte; + if (iRet < 0) { + iRet += 256; + } + int iD1 = iRet / 16; + int iD2 = iRet % 16; + return strDigits[iD1] + strDigits[iD2]; + } + + /** + * 转换字节数组为16进制字串 + * + * @Date 2023/7/3 + * @param bByte + * @return String + **/ + public static String byteToString(byte[] bByte) { + StringBuffer sBuffer = new StringBuffer(); + for (int i = 0; i < bByte.length; i++) { + sBuffer.append(byteToArrayString(bByte[i])); + } + return sBuffer.toString(); + } + + /** + * 生成md5代码 + * + * @Date 2023/7/3 + * @param strObj + * @return String + **/ + public static String GetMD5Code(String strObj) { + String resultString = null; + try { + resultString = new String(strObj); + MessageDigest md = MessageDigest.getInstance("MD5"); + resultString = byteToString(md.digest(strObj.getBytes())); + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + } + return resultString; + } + + /** + * 组装签名的字段 + * + * @param params 参数 + * @param urlEncoder 是否urlEncoder + * @return {String} + */ + public static String packageSign(Map params, boolean urlEncoder) { + // 先将参数以其参数名的字典序升序进行排序 + TreeMap sortedParams = new TreeMap(params); + // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起 + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Map.Entry param : sortedParams.entrySet()) { + String value = String.valueOf(param.getValue()); + if (StrUtil.isBlank(value)) { + continue; + } + if (first) { + first = false; + } else { + sb.append("&"); + } + sb.append(param.getKey()).append("="); + sb.append(value); + } + return sb.toString(); + } + +} diff --git a/ruoyi-common/ruoyi-common-ratelimiter/pom.xml b/ruoyi-common/ruoyi-common-ratelimiter/pom.xml new file mode 100644 index 00000000..f08316b4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/pom.xml @@ -0,0 +1,31 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-ratelimiter + + + ruoyi-common-ratelimiter 限流功能 + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-redis + + + + diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/annotation/RateLimiter.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/annotation/RateLimiter.java new file mode 100644 index 00000000..94e5969c --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/annotation/RateLimiter.java @@ -0,0 +1,41 @@ +package com.xmzs.common.ratelimiter.annotation; + +import com.xmzs.common.ratelimiter.enums.LimitType; + +import java.lang.annotation.*; + +/** + * 限流注解 + * + * @author Lion Li + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter { + /** + * 限流key,支持使用Spring el表达式来动态获取方法上的参数值 + * 格式类似于 #code.id #{#code} + */ + String key() default ""; + + /** + * 限流时间,单位秒 + */ + int time() default 60; + + /** + * 限流次数 + */ + int count() default 100; + + /** + * 限流类型 + */ + LimitType limitType() default LimitType.DEFAULT; + + /** + * 提示消息 支持国际化 格式为 {code} + */ + String message() default "{rate.limiter.message}"; +} diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/aspectj/RateLimiterAspect.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/aspectj/RateLimiterAspect.java new file mode 100644 index 00000000..ec9ac999 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/aspectj/RateLimiterAspect.java @@ -0,0 +1,127 @@ +package com.xmzs.common.ratelimiter.aspectj; + +import cn.hutool.core.util.ArrayUtil; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MessageUtils; +import com.xmzs.common.core.utils.ServletUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.ratelimiter.annotation.RateLimiter; +import com.xmzs.common.ratelimiter.enums.LimitType; +import com.xmzs.common.redis.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.redisson.api.RateType; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import java.lang.reflect.Method; + +/** + * 限流处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +public class RateLimiterAspect { + + /** + * 定义spel表达式解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + /** + * 定义spel解析模版 + */ + private final ParserContext parserContext = new TemplateParserContext(); + /** + * 定义spel上下文对象进行解析 + */ + private final EvaluationContext context = new StandardEvaluationContext(); + /** + * 方法参数解析器 + */ + private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + String combineKey = getCombineKey(rateLimiter, point); + try { + RateType rateType = RateType.OVERALL; + if (rateLimiter.limitType() == LimitType.CLUSTER) { + rateType = RateType.PER_CLIENT; + } + long number = RedisUtils.rateLimiter(combineKey, rateType, count, time); + if (number == -1) { + String message = rateLimiter.message(); + if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { + message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); + } + throw new ServiceException(message); + } + log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); + } catch (Exception e) { + if (e instanceof ServiceException) { + throw e; + } else { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { + String key = rateLimiter.key(); + // 获取方法(通过方法签名来获取) + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + // 判断是否是spel格式 + if (StringUtils.containsAny(key, "#")) { + // 获取参数值 + Object[] args = point.getArgs(); + // 获取方法上参数的名称 + String[] parameterNames = pnd.getParameterNames(method); + if (ArrayUtil.isEmpty(parameterNames)) { + throw new ServiceException("限流key解析异常!请联系管理员!"); + } + for (int i = 0; i < parameterNames.length; i++) { + context.setVariable(parameterNames[i], args[i]); + } + // 解析返回给key + try { + Expression expression; + if (StringUtils.startsWith(key, parserContext.getExpressionPrefix()) + && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) { + expression = parser.parseExpression(key, parserContext); + } else { + expression = parser.parseExpression(key); + } + key = expression.getValue(context, String.class) + ":"; + } catch (Exception e) { + throw new ServiceException("限流key解析异常!请联系管理员!"); + } + } + StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY); + stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":"); + if (rateLimiter.limitType() == LimitType.IP) { + // 获取请求ip + stringBuffer.append(ServletUtils.getClientIP()).append(":"); + } else if (rateLimiter.limitType() == LimitType.CLUSTER) { + // 获取客户端实例id + stringBuffer.append(RedisUtils.getClient().getId()).append(":"); + } + return stringBuffer.append(key).toString(); + } +} diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/config/RateLimiterConfig.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/config/RateLimiterConfig.java new file mode 100644 index 00000000..32a3c907 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/config/RateLimiterConfig.java @@ -0,0 +1,20 @@ +package com.xmzs.common.ratelimiter.config; + +import com.xmzs.common.ratelimiter.aspectj.RateLimiterAspect; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConfiguration; + +/** + * @author guangxin + * @date 2023/1/18 + */ +@AutoConfiguration(after = RedisConfiguration.class) +public class RateLimiterConfig { + + @Bean + public RateLimiterAspect rateLimiterAspect() { + return new RateLimiterAspect(); + } + +} diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/enums/LimitType.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/enums/LimitType.java new file mode 100644 index 00000000..df9c8e62 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/xmzs/common/ratelimiter/enums/LimitType.java @@ -0,0 +1,24 @@ +package com.xmzs.common.ratelimiter.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType { + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP, + + /** + * 实例限流(集群多后端实例) + */ + CLUSTER +} diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..46fa24a2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.ratelimiter.config.RateLimiterConfig diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml new file mode 100644 index 00000000..c085069b --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/pom.xml @@ -0,0 +1,38 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-redis + + + ruoyi-common-redis 缓存服务 + + + + + + com.xmzs + ruoyi-common-core + + + + + org.redisson + redisson-spring-boot-starter + + + + com.baomidou + lock4j-redisson-spring-boot-starter + + + + diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/RedisConfig.java new file mode 100644 index 00000000..cd57b82d --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/RedisConfig.java @@ -0,0 +1,130 @@ +package com.xmzs.common.redis.config; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.redis.config.properties.RedissonProperties; +import com.xmzs.common.redis.handler.KeyPrefixHandler; +import com.xmzs.common.redis.manager.PlusSpringCacheManager; +import lombok.extern.slf4j.Slf4j; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; + +/** + * redis配置 + * + * @author Lion Li + */ +@Slf4j +@AutoConfiguration +@EnableCaching +@EnableConfigurationProperties(RedissonProperties.class) +public class RedisConfig { + + @Autowired + private RedissonProperties redissonProperties; + + @Autowired + private ObjectMapper objectMapper; + + @Bean + public RedissonAutoConfigurationCustomizer redissonCustomizer() { + return config -> { + config.setThreads(redissonProperties.getThreads()) + .setNettyThreads(redissonProperties.getNettyThreads()) + .setCodec(new JsonJacksonCodec(objectMapper)); + RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); + if (ObjectUtil.isNotNull(singleServerConfig)) { + // 使用单机模式 + config.useSingleServer() + //设置redis key前缀 + .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) + .setTimeout(singleServerConfig.getTimeout()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); + } + // 集群配置方式 参考下方注释 + RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); + if (ObjectUtil.isNotNull(clusterServersConfig)) { + config.useClusterServers() + //设置redis key前缀 + .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) + .setTimeout(clusterServersConfig.getTimeout()) + .setClientName(clusterServersConfig.getClientName()) + .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) + .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) + .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) + .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) + .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) + .setReadMode(clusterServersConfig.getReadMode()) + .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()); + } + log.info("初始化 redis 配置"); + }; + } + + /** + * 自定义缓存管理器 整合spring-cache + */ + @Bean + public CacheManager cacheManager() { + return new PlusSpringCacheManager(); + } + + /** + * redis集群配置 yml + * + * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) + * spring: + * redis: + * cluster: + * nodes: + * - 192.168.0.100:6379 + * - 192.168.0.101:6379 + * - 192.168.0.102:6379 + * # 密码 + * password: + * # 连接超时时间 + * timeout: 10s + * # 是否开启ssl + * ssl: false + * + * redisson: + * # 线程池数量 + * threads: 16 + * # Netty线程池数量 + * nettyThreads: 32 + * # 集群配置 + * clusterServersConfig: + * # 客户端名称 + * clientName: ${ruoyi.name} + * # master最小空闲连接数 + * masterConnectionMinimumIdleSize: 32 + * # master连接池大小 + * masterConnectionPoolSize: 64 + * # slave最小空闲连接数 + * slaveConnectionMinimumIdleSize: 32 + * # slave连接池大小 + * slaveConnectionPoolSize: 64 + * # 连接空闲超时,单位:毫秒 + * idleConnectionTimeout: 10000 + * # 命令等待超时,单位:毫秒 + * timeout: 3000 + * # 发布和订阅连接池大小 + * subscriptionConnectionPoolSize: 50 + * # 读取模式 + * readMode: "SLAVE" + * # 订阅模式 + * subscriptionMode: "MASTER" + */ + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/properties/RedissonProperties.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/properties/RedissonProperties.java new file mode 100644 index 00000000..18c5d212 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/config/properties/RedissonProperties.java @@ -0,0 +1,135 @@ +package com.xmzs.common.redis.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.redisson.config.ReadMode; +import org.redisson.config.SubscriptionMode; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Redisson 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "redisson") +public class RedissonProperties { + + /** + * redis缓存key前缀 + */ + private String keyPrefix; + + /** + * 线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int threads; + + /** + * Netty线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int nettyThreads; + + /** + * 单机服务配置 + */ + private SingleServerConfig singleServerConfig; + + /** + * 集群服务配置 + */ + private ClusterServersConfig clusterServersConfig; + + @Data + @NoArgsConstructor + public static class SingleServerConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * 最小空闲连接数 + */ + private int connectionMinimumIdleSize; + + /** + * 连接池大小 + */ + private int connectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + } + + @Data + @NoArgsConstructor + public static class ClusterServersConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * master最小空闲连接数 + */ + private int masterConnectionMinimumIdleSize; + + /** + * master连接池大小 + */ + private int masterConnectionPoolSize; + + /** + * slave最小空闲连接数 + */ + private int slaveConnectionMinimumIdleSize; + + /** + * slave连接池大小 + */ + private int slaveConnectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + /** + * 读取模式 + */ + private ReadMode readMode; + + /** + * 订阅模式 + */ + private SubscriptionMode subscriptionMode; + + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/handler/KeyPrefixHandler.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/handler/KeyPrefixHandler.java new file mode 100644 index 00000000..2d06db47 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/handler/KeyPrefixHandler.java @@ -0,0 +1,50 @@ +package com.xmzs.common.redis.handler; + +import com.xmzs.common.core.utils.StringUtils; +import org.redisson.api.NameMapper; + +/** + * redis缓存key前缀处理 + * + * @author ye + * @date 2022/7/14 17:44 + * @since 4.3.0 + */ +public class KeyPrefixHandler implements NameMapper { + + private final String keyPrefix; + + public KeyPrefixHandler(String keyPrefix) { + //前缀为空 则返回空前缀 + this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":"; + } + + /** + * 增加前缀 + */ + @Override + public String map(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) { + return keyPrefix + name; + } + return name; + } + + /** + * 去除前缀 + */ + @Override + public String unmap(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) { + return name.substring(keyPrefix.length()); + } + return name; + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/manager/PlusSpringCacheManager.java new file mode 100644 index 00000000..ce618d93 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/manager/PlusSpringCacheManager.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2013-2021 Nikita Koksharov + * + * 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. + */ +package com.xmzs.common.redis.manager; + +import com.xmzs.common.redis.utils.RedisUtils; +import org.redisson.api.RMap; +import org.redisson.api.RMapCache; +import org.redisson.spring.cache.CacheConfig; +import org.redisson.spring.cache.RedissonCache; +import org.springframework.boot.convert.DurationStyle; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.transaction.TransactionAwareCacheDecorator; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * A {@link org.springframework.cache.CacheManager} implementation + * backed by Redisson instance. + *

+ * 修改 RedissonSpringCacheManager 源码 + * 重写 cacheName 处理方法 支持多参数 + * + * @author Nikita Koksharov + * + */ +@SuppressWarnings("unchecked") +public class PlusSpringCacheManager implements CacheManager { + + private boolean dynamic = true; + + private boolean allowNullValues = true; + + private boolean transactionAware = true; + + Map configMap = new ConcurrentHashMap<>(); + ConcurrentMap instanceMap = new ConcurrentHashMap<>(); + + /** + * Creates CacheManager supplied by Redisson instance + */ + public PlusSpringCacheManager() { + } + + + /** + * Defines possibility of storing {@code null} values. + *

+ * Default is true + * + * @param allowNullValues stores if true + */ + public void setAllowNullValues(boolean allowNullValues) { + this.allowNullValues = allowNullValues; + } + + /** + * Defines if cache aware of Spring-managed transactions. + * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase. + *

+ * Default is false + * + * @param transactionAware cache is transaction aware if true + */ + public void setTransactionAware(boolean transactionAware) { + this.transactionAware = transactionAware; + } + + /** + * Defines 'fixed' cache names. + * A new cache instance will not be created in dynamic for non-defined names. + *

+ * `null` parameter setups dynamic mode + * + * @param names of caches + */ + public void setCacheNames(Collection names) { + if (names != null) { + for (String name : names) { + getCache(name); + } + dynamic = false; + } else { + dynamic = true; + } + } + + /** + * Set cache config mapped by cache name + * + * @param config object + */ + public void setConfig(Map config) { + this.configMap = (Map) config; + } + + protected CacheConfig createDefaultConfig() { + return new CacheConfig(); + } + + @Override + public Cache getCache(String name) { + Cache cache = instanceMap.get(name); + if (cache != null) { + return cache; + } + if (!dynamic) { + return cache; + } + + CacheConfig config = configMap.get(name); + if (config == null) { + config = createDefaultConfig(); + configMap.put(name, config); + } + + // 重写 cacheName 支持多参数 + String[] array = StringUtils.delimitedListToStringArray(name, "#"); + name = array[0]; + if (array.length > 1) { + config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis()); + } + if (array.length > 2) { + config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis()); + } + if (array.length > 3) { + config.setMaxSize(Integer.parseInt(array[3])); + } + + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(name, config); + } + + return createMapCache(name, config); + } + + private Cache createMap(String name, CacheConfig config) { + RMap map = RedisUtils.getClient().getMap(name); + + Cache cache = new RedissonCache(map, allowNullValues); + if (transactionAware) { + cache = new TransactionAwareCacheDecorator(cache); + } + Cache oldCache = instanceMap.putIfAbsent(name, cache); + if (oldCache != null) { + cache = oldCache; + } + return cache; + } + + private Cache createMapCache(String name, CacheConfig config) { + RMapCache map = RedisUtils.getClient().getMapCache(name); + + Cache cache = new RedissonCache(map, config, allowNullValues); + if (transactionAware) { + cache = new TransactionAwareCacheDecorator(cache); + } + Cache oldCache = instanceMap.putIfAbsent(name, cache); + if (oldCache != null) { + cache = oldCache; + } else { + map.setMaxSize(config.getMaxSize()); + } + return cache; + } + + @Override + public Collection getCacheNames() { + return Collections.unmodifiableSet(configMap.keySet()); + } + + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/CacheUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/CacheUtils.java new file mode 100644 index 00000000..0358d3b5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/CacheUtils.java @@ -0,0 +1,75 @@ +package com.xmzs.common.redis.utils; + +import com.xmzs.common.core.utils.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.RMap; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +import java.util.Set; + +/** + * 缓存操作工具类 {@link } + * + * @author Michelle.Chung + * @date 2022/8/13 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings(value = {"unchecked"}) +public class CacheUtils { + + private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); + + /** + * 获取缓存组内所有的KEY + * + * @param cacheNames 缓存组名称 + */ + public static Set keys(String cacheNames) { + RMap rmap = (RMap) CACHE_MANAGER.getCache(cacheNames).getNativeCache(); + return rmap.keySet(); + } + + /** + * 获取缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + */ + public static T get(String cacheNames, Object key) { + Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); + return wrapper != null ? (T) wrapper.get() : null; + } + + /** + * 保存缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + * @param value 缓存值 + */ + public static void put(String cacheNames, Object key, Object value) { + CACHE_MANAGER.getCache(cacheNames).put(key, value); + } + + /** + * 删除缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + */ + public static void evict(String cacheNames, Object key) { + CACHE_MANAGER.getCache(cacheNames).evict(key); + } + + /** + * 清空缓存值 + * + * @param cacheNames 缓存组名称 + */ + public static void clear(String cacheNames) { + CACHE_MANAGER.getCache(cacheNames).clear(); + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/QueueUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/QueueUtils.java new file mode 100644 index 00000000..8f1c6505 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/QueueUtils.java @@ -0,0 +1,180 @@ +package com.xmzs.common.redis.utils; + +import com.xmzs.common.core.utils.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.*; + +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * 分布式队列工具 + * 轻量级队列 重量级数据量 请使用 MQ + * 要求 redis 5.X 以上 + * + * @author Lion Li + * @version 3.6.0 新增 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class QueueUtils { + + private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); + + + /** + * 获取客户端实例 + */ + public static RedissonClient getClient() { + return CLIENT; + } + + /** + * 添加普通队列数据 + * + * @param queueName 队列名 + * @param data 数据 + */ + public static boolean addQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.offer(data); + } + + /** + * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列) + * + * @param queueName 队列名 + */ + public static T getQueueObject(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.poll(); + } + + /** + * 通用删除队列数据(不支持延迟队列) + */ + public static boolean removeQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.remove(data); + } + + /** + * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列) + */ + public static boolean destroyQueue(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.delete(); + } + + /** + * 添加延迟队列数据 默认毫秒 + * + * @param queueName 队列名 + * @param data 数据 + * @param time 延迟时间 + */ + public static void addDelayedQueueObject(String queueName, T data, long time) { + addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS); + } + + /** + * 添加延迟队列数据 + * + * @param queueName 队列名 + * @param data 数据 + * @param time 延迟时间 + * @param timeUnit 单位 + */ + public static void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + delayedQueue.offer(data, time, timeUnit); + } + + /** + * 获取一个延迟队列数据 没有数据返回 null + * + * @param queueName 队列名 + */ + public static T getDelayedQueueObject(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + return delayedQueue.poll(); + } + + /** + * 删除延迟队列数据 + */ + public static boolean removeDelayedQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + return delayedQueue.remove(data); + } + + /** + * 销毁延迟队列 所有阻塞监听 报错 + */ + public static void destroyDelayedQueue(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + delayedQueue.destroy(); + } + + /** + * 添加优先队列数据 + * + * @param queueName 队列名 + * @param data 数据 + */ + public static boolean addPriorityQueueObject(String queueName, T data) { + RPriorityBlockingQueue priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName); + return priorityBlockingQueue.offer(data); + } + + /** + * 尝试设置 有界队列 容量 用于限制数量 + * + * @param queueName 队列名 + * @param capacity 容量 + */ + public static boolean trySetBoundedQueueCapacity(String queueName, int capacity) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + return boundedBlockingQueue.trySetCapacity(capacity); + } + + /** + * 尝试设置 有界队列 容量 用于限制数量 + * + * @param queueName 队列名 + * @param capacity 容量 + * @param destroy 已存在是否销毁 + */ + public static boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + if (boundedBlockingQueue.isExists() && destroy) { + destroyQueue(queueName); + } + return boundedBlockingQueue.trySetCapacity(capacity); + } + + /** + * 添加有界队列数据 + * + * @param queueName 队列名 + * @param data 数据 + * @return 添加成功 true 已达到界限 false + */ + public static boolean addBoundedQueueObject(String queueName, T data) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + return boundedBlockingQueue.offer(data); + } + + /** + * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) + */ + public static void subscribeBlockingQueue(String queueName, Consumer consumer) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + queue.subscribeOnElements(consumer); + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/RedisUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/RedisUtils.java new file mode 100644 index 00000000..c549d1e9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/xmzs/common/redis/utils/RedisUtils.java @@ -0,0 +1,462 @@ +package com.xmzs.common.redis.utils; + +import com.xmzs.common.core.utils.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.*; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * redis 工具类 + * + * @author Lion Li + * @version 3.1.0 新增 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +public class RedisUtils { + + private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); + + /** + * 限流 + * + * @param key 限流key + * @param rateType 限流类型 + * @param rate 速率 + * @param rateInterval 速率间隔 + * @return -1 表示失败 + */ + public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) { + RRateLimiter rateLimiter = CLIENT.getRateLimiter(key); + rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS); + if (rateLimiter.tryAcquire()) { + return rateLimiter.availablePermits(); + } else { + return -1L; + } + } + + /** + * 获取客户端实例 + */ + public static RedissonClient getClient() { + return CLIENT; + } + + /** + * 发布通道消息 + * + * @param channelKey 通道key + * @param msg 发送数据 + * @param consumer 自定义处理 + */ + public static void publish(String channelKey, T msg, Consumer consumer) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.publish(msg); + consumer.accept(msg); + } + + public static void publish(String channelKey, T msg) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.publish(msg); + } + + /** + * 订阅通道接收消息 + * + * @param channelKey 通道key + * @param clazz 消息类型 + * @param consumer 自定义处理 + */ + public static void subscribe(String channelKey, Class clazz, Consumer consumer) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public static void setCacheObject(final String key, final T value) { + setCacheObject(key, value, false); + } + + /** + * 缓存基本的对象,保留当前对象 TTL 有效期 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90) + * @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案 + */ + public static void setCacheObject(final String key, final T value, final boolean isSaveTtl) { + RBucket bucket = CLIENT.getBucket(key); + if (isSaveTtl) { + try { + bucket.setAndKeepTTL(value); + } catch (Exception e) { + long timeToLive = bucket.remainTimeToLive(); + setCacheObject(key, value, Duration.ofMillis(timeToLive)); + } + } else { + bucket.set(value); + } + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param duration 时间 + */ + public static void setCacheObject(final String key, final T value, final Duration duration) { + RBatch batch = CLIENT.createBatch(); + RBucketAsync bucket = batch.getBucket(key); + bucket.setAsync(value); + bucket.expireAsync(duration); + batch.execute(); + } + + /** + * 注册对象监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addObjectListener(final String key, final ObjectListener listener) { + RBucket result = CLIENT.getBucket(key); + result.addListener(listener); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final long timeout) { + return expire(key, Duration.ofSeconds(timeout)); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param duration 超时时间 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final Duration duration) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.expire(duration); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public static T getCacheObject(final String key) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.get(); + } + + /** + * 获得key剩余存活时间 + * + * @param key 缓存键值 + * @return 剩余存活时间 + */ + public static long getTimeToLive(final String key) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.remainTimeToLive(); + } + + /** + * 删除单个对象 + * + * @param key 缓存的键值 + */ + public static boolean deleteObject(final String key) { + return CLIENT.getBucket(key).delete(); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + */ + public static void deleteObject(final Collection collection) { + RBatch batch = CLIENT.createBatch(); + collection.forEach(t -> { + batch.getBucket(t.toString()).deleteAsync(); + }); + batch.execute(); + } + + /** + * 检查缓存对象是否存在 + * + * @param key 缓存的键值 + */ + public static boolean isExistsObject(final String key) { + return CLIENT.getBucket(key).isExists(); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public static boolean setCacheList(final String key, final List dataList) { + RList rList = CLIENT.getList(key); + return rList.addAll(dataList); + } + + /** + * 注册List监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addListListener(final String key, final ObjectListener listener) { + RList rList = CLIENT.getList(key); + rList.addListener(listener); + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public static List getCacheList(final String key) { + RList rList = CLIENT.getList(key); + return rList.readAll(); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public static boolean setCacheSet(final String key, final Set dataSet) { + RSet rSet = CLIENT.getSet(key); + return rSet.addAll(dataSet); + } + + /** + * 注册Set监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addSetListener(final String key, final ObjectListener listener) { + RSet rSet = CLIENT.getSet(key); + rSet.addListener(listener); + } + + /** + * 获得缓存的set + * + * @param key 缓存的key + * @return set对象 + */ + public static Set getCacheSet(final String key) { + RSet rSet = CLIENT.getSet(key); + return rSet.readAll(); + } + + /** + * 缓存Map + * + * @param key 缓存的键值 + * @param dataMap 缓存的数据 + */ + public static void setCacheMap(final String key, final Map dataMap) { + if (dataMap != null) { + RMap rMap = CLIENT.getMap(key); + rMap.putAll(dataMap); + } + } + + /** + * 注册Map监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addMapListener(final String key, final ObjectListener listener) { + RMap rMap = CLIENT.getMap(key); + rMap.addListener(listener); + } + + /** + * 获得缓存的Map + * + * @param key 缓存的键值 + * @return map对象 + */ + public static Map getCacheMap(final String key) { + RMap rMap = CLIENT.getMap(key); + return rMap.getAll(rMap.keySet()); + } + + /** + * 获得缓存Map的key列表 + * + * @param key 缓存的键值 + * @return key列表 + */ + public static Set getCacheMapKeySet(final String key) { + RMap rMap = CLIENT.getMap(key); + return rMap.keySet(); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public static void setCacheMapValue(final String key, final String hKey, final T value) { + RMap rMap = CLIENT.getMap(key); + rMap.put(hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public static T getCacheMapValue(final String key, final String hKey) { + RMap rMap = CLIENT.getMap(key); + return rMap.get(hKey); + } + + /** + * 删除Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public static T delCacheMapValue(final String key, final String hKey) { + RMap rMap = CLIENT.getMap(key); + return rMap.remove(hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public static Map getMultiCacheMapValue(final String key, final Set hKeys) { + RMap rMap = CLIENT.getMap(key); + return rMap.getAll(hKeys); + } + + /** + * 设置原子值 + * + * @param key Redis键 + * @param value 值 + */ + public static void setAtomicValue(String key, long value) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + atomic.set(value); + } + + /** + * 获取原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long getAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.get(); + } + + /** + * 递增原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long incrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.incrementAndGet(); + } + + /** + * 递减原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long decrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.decrementAndGet(); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public static Collection keys(final String pattern) { + Stream stream = CLIENT.getKeys().getKeysStreamByPattern(pattern); + return stream.collect(Collectors.toList()); + } + + /** + * 删除缓存的基本对象列表 + * + * @param pattern 字符串前缀 + */ + public static void deleteKeys(final String pattern) { + CLIENT.getKeys().deleteByPattern(pattern); + } + + /** + * 检查redis中是否存在key + * + * @param key 键 + */ + public static Boolean hasKey(String key) { + RKeys rKeys = CLIENT.getKeys(); + return rKeys.countExists(key) > 0; + } +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..0fdeef66 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.redis.config.RedisConfig diff --git a/ruoyi-common/ruoyi-common-satoken/pom.xml b/ruoyi-common/ruoyi-common-satoken/pom.xml new file mode 100644 index 00000000..10ecb486 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/pom.xml @@ -0,0 +1,42 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-satoken + + + + + com.xmzs + ruoyi-common-core + + + + + com.xmzs + ruoyi-common-redis + + + + + cn.dev33 + sa-token-spring-boot3-starter + + + + + cn.dev33 + sa-token-jwt + + + + + diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/config/SaTokenConfig.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/config/SaTokenConfig.java new file mode 100644 index 00000000..c947ab61 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/config/SaTokenConfig.java @@ -0,0 +1,43 @@ +package com.xmzs.common.satoken.config; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.jwt.StpLogicJwtForSimple; +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpLogic; +import com.xmzs.common.satoken.core.dao.PlusSaTokenDao; +import com.xmzs.common.satoken.core.service.SaPermissionImpl; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * sa-token 配置 + * + * @author Lion Li + */ +@AutoConfiguration +public class SaTokenConfig implements WebMvcConfigurer { + + @Bean + public StpLogic getStpLogicJwt() { + // Sa-Token 整合 jwt (简单模式) + return new StpLogicJwtForSimple(); + } + + /** + * 权限接口实现(使用bean注入方便用户替换) + */ + @Bean + public StpInterface stpInterface() { + return new SaPermissionImpl(); + } + + /** + * 自定义dao层存储 + */ + @Bean + public SaTokenDao saTokenDao() { + return new PlusSaTokenDao(); + } + +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/dao/PlusSaTokenDao.java new file mode 100644 index 00000000..6defa1a9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/dao/PlusSaTokenDao.java @@ -0,0 +1,176 @@ +package com.xmzs.common.satoken.core.dao; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.util.SaFoxUtil; +import com.xmzs.common.redis.utils.RedisUtils; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) + * + * @author Lion Li + */ +public class PlusSaTokenDao implements SaTokenDao { + + /** + * 获取Value,如无返空 + */ + @Override + public String get(String key) { + return RedisUtils.getCacheObject(key); + } + + /** + * 写入Value,并设定存活时间 (单位: 秒) + */ + @Override + public void set(String key, String value, long timeout) { + if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if (timeout == NEVER_EXPIRE) { + RedisUtils.setCacheObject(key, value); + } else { + RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); + } + } + + /** + * 修修改指定key-value键值对 (过期时间不变) + */ + @Override + public void update(String key, String value) { + long expire = getTimeout(key); + // -2 = 无此键 + if (expire == NOT_VALUE_EXPIRE) { + return; + } + this.set(key, value, expire); + } + + /** + * 删除Value + */ + @Override + public void delete(String key) { + RedisUtils.deleteObject(key); + } + + /** + * 获取Value的剩余存活时间 (单位: 秒) + */ + @Override + public long getTimeout(String key) { + long timeout = RedisUtils.getTimeToLive(key); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Value的剩余存活时间 (单位: 秒) + */ + @Override + public void updateTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == NEVER_EXPIRE) { + long expire = getTimeout(key); + if (expire == NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.set(key, this.get(key), timeout); + } + return; + } + RedisUtils.expire(key, Duration.ofSeconds(timeout)); + } + + + /** + * 获取Object,如无返空 + */ + @Override + public Object getObject(String key) { + return RedisUtils.getCacheObject(key); + } + + /** + * 写入Object,并设定存活时间 (单位: 秒) + */ + @Override + public void setObject(String key, Object object, long timeout) { + if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if (timeout == NEVER_EXPIRE) { + RedisUtils.setCacheObject(key, object); + } else { + RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); + } + } + + /** + * 更新Object (过期时间不变) + */ + @Override + public void updateObject(String key, Object object) { + long expire = getObjectTimeout(key); + // -2 = 无此键 + if (expire == NOT_VALUE_EXPIRE) { + return; + } + this.setObject(key, object, expire); + } + + /** + * 删除Object + */ + @Override + public void deleteObject(String key) { + RedisUtils.deleteObject(key); + } + + /** + * 获取Object的剩余存活时间 (单位: 秒) + */ + @Override + public long getObjectTimeout(String key) { + long timeout = RedisUtils.getTimeToLive(key); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Object的剩余存活时间 (单位: 秒) + */ + @Override + public void updateObjectTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == NEVER_EXPIRE) { + long expire = getObjectTimeout(key); + if (expire == NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.setObject(key, this.getObject(key), timeout); + } + return; + } + RedisUtils.expire(key, Duration.ofSeconds(timeout)); + } + + + /** + * 搜索数据 + */ + @Override + public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { + Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*"); + List list = new ArrayList<>(keys); + return SaFoxUtil.searchList(list, start, size, sortType); + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/service/SaPermissionImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/service/SaPermissionImpl.java new file mode 100644 index 00000000..a6770480 --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/core/service/SaPermissionImpl.java @@ -0,0 +1,47 @@ +package com.xmzs.common.satoken.core.service; + +import cn.dev33.satoken.stp.StpInterface; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.enums.UserType; +import com.xmzs.common.satoken.utils.LoginHelper; + +import java.util.ArrayList; +import java.util.List; + +/** + * sa-token 权限管理实现类 + * + * @author Lion Li + */ +public class SaPermissionImpl implements StpInterface { + + /** + * 获取菜单权限列表 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + UserType userType = UserType.getUserType(loginUser.getUserType()); + if (userType == UserType.SYS_USER) { + return new ArrayList<>(loginUser.getMenuPermission()); + } else if (userType == UserType.APP_USER) { + // 其他端 自行根据业务编写 + } + return new ArrayList<>(); + } + + /** + * 获取角色权限列表 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + UserType userType = UserType.getUserType(loginUser.getUserType()); + if (userType == UserType.SYS_USER) { + return new ArrayList<>(loginUser.getRolePermission()); + } else if (userType == UserType.APP_USER) { + // 其他端 自行根据业务编写 + } + return new ArrayList<>(); + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/listener/UserActionListener.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/listener/UserActionListener.java new file mode 100644 index 00000000..3ab2097c --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/listener/UserActionListener.java @@ -0,0 +1,139 @@ +package com.xmzs.common.satoken.listener; + +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.listener.SaTokenListener; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.xmzs.common.core.constant.CacheConstants; +import com.xmzs.common.core.domain.dto.UserOnlineDTO; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.enums.UserType; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.core.utils.ip.AddressUtils; +import com.xmzs.common.core.utils.ServletUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * 用户行为 侦听器的实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Component +@Slf4j +public class UserActionListener implements SaTokenListener { + + private final SaTokenConfig tokenConfig; + + /** + * 每次登录时触发 + */ + @Override + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { + UserType userType = UserType.getUserType(loginId.toString()); + if (userType == UserType.SYS_USER) { + UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = ServletUtils.getClientIP(); + LoginUser user = LoginHelper.getLoginUser(); + UserOnlineDTO dto = new UserOnlineDTO(); + dto.setIpaddr(ip); + // dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + dto.setBrowser(userAgent.getBrowser().getName()); + dto.setOs(userAgent.getOs().getName()); + dto.setLoginTime(System.currentTimeMillis()); + dto.setTokenId(tokenValue); + dto.setUserName(user.getUsername()); + dto.setDeptName(user.getDeptName()); + if(tokenConfig.getTimeout() == -1) { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); + } else { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); + } + log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); + } else if (userType == UserType.APP_USER) { + // app端 自行根据业务编写 + } + } + + /** + * 每次注销时触发 + */ + @Override + public void doLogout(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被踢下线时触发 + */ + @Override + public void doKickout(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被顶下线时触发 + */ + @Override + public void doReplaced(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被封禁时触发 + */ + @Override + public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { + } + + /** + * 每次被解封时触发 + */ + @Override + public void doUntieDisable(String loginType, Object loginId, String service) { + } + + /** + * 每次打开二级认证时触发 + */ + @Override + public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCloseSafe(String loginType, String tokenValue, String service) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCreateSession(String id) { + } + + /** + * 每次注销Session时触发 + */ + @Override + public void doLogoutSession(String id) { + } + + /** + * 每次Token续期时触发 + */ + @Override + public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { + } +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/utils/LoginHelper.java new file mode 100644 index 00000000..af170c9e --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/xmzs/common/satoken/utils/LoginHelper.java @@ -0,0 +1,172 @@ +package com.xmzs.common.satoken.utils; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.enums.DeviceType; +import com.xmzs.common.core.enums.UserType; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * 登录鉴权助手 + *

+ * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app + * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios + * 可以组成 用户类型与设备类型多对多的 权限灵活控制 + *

+ * 多用户体系 针对 多种用户类型 但权限控制不一致 + * 可以组成 多用户类型表与多设备类型 分别控制权限 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class LoginHelper { + + public static final String LOGIN_USER_KEY = "loginUser"; + public static final String TENANT_KEY = "tenantId"; + public static final String USER_KEY = "userId"; + + /** + * 登录系统 + * + * @param loginUser 登录用户信息 + */ + public static void login(LoginUser loginUser) { + loginByDevice(loginUser, null); + } + + /** + * 登录系统 基于 设备类型 + * 针对相同用户体系不同设备 + * + * @param loginUser 登录用户信息 + */ + public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) { + SaStorage storage = SaHolder.getStorage(); + storage.set(LOGIN_USER_KEY, loginUser); + storage.set(TENANT_KEY, loginUser.getTenantId()); + storage.set(USER_KEY, loginUser.getUserId()); + SaLoginModel model = new SaLoginModel(); + if (ObjectUtil.isNotNull(deviceType)) { + model.setDevice(deviceType.getDevice()); + } + StpUtil.login(loginUser.getLoginId(), + model.setExtra(TENANT_KEY, loginUser.getTenantId()) + .setExtra(USER_KEY, loginUser.getUserId())); + StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser); + } + + /** + * 获取用户(多级缓存) + */ + public static LoginUser getLoginUser() { + LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY); + if (loginUser != null) { + return loginUser; + } + loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY); + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); + return loginUser; + } + + /** + * 获取用户基于token + */ + public static LoginUser getLoginUser(String token) { + return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY); + } + + /** + * 获取用户id + */ + public static Long getUserId() { + Long userId; + try { + userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY)); + if (ObjectUtil.isNull(userId)) { + userId = Convert.toLong(StpUtil.getExtra(USER_KEY)); + SaHolder.getStorage().set(USER_KEY, userId); + } + } catch (Exception e) { + return null; + } + return userId; + } + + /** + * 获取租户ID + */ + public static String getTenantId() { + String tenantId; + try { + tenantId = (String) SaHolder.getStorage().get(TENANT_KEY); + if (ObjectUtil.isNull(tenantId)) { + tenantId = (String) StpUtil.getExtra(TENANT_KEY); + SaHolder.getStorage().set(TENANT_KEY, tenantId); + } + } catch (Exception e) { + return null; + } + return tenantId; + } + + /** + * 获取部门ID + */ + public static Long getDeptId() { + return getLoginUser().getDeptId(); + } + + /** + * 获取用户账户 + */ + public static String getUsername() { + return getLoginUser().getUsername(); + } + + /** + * 获取用户类型 + */ + public static UserType getUserType() { + String loginId = StpUtil.getLoginIdAsString(); + return UserType.getUserType(loginId); + } + + /** + * 是否为超级管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isSuperAdmin(Long userId) { + return UserConstants.SUPER_ADMIN_ID.equals(userId); + } + + public static boolean isSuperAdmin() { + return isSuperAdmin(getUserId()); + } + + /** + * 是否为超级管理员 + * + * @param rolePermission 角色权限标识组 + * @return 结果 + */ + public static boolean isTenantAdmin(Set rolePermission) { + return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY); + } + + public static boolean isTenantAdmin() { + return isTenantAdmin(getLoginUser().getRolePermission()); + } + +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..3744e52e --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.satoken.config.SaTokenConfig diff --git a/ruoyi-common/ruoyi-common-security/pom.xml b/ruoyi-common/ruoyi-common-security/pom.xml new file mode 100644 index 00000000..00957938 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/pom.xml @@ -0,0 +1,27 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-security + + + ruoyi-common-security 安全模块 + + + + + com.xmzs + ruoyi-common-satoken + + + + + diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/SecurityConfig.java new file mode 100644 index 00000000..e18cc4a2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/SecurityConfig.java @@ -0,0 +1,59 @@ +package com.xmzs.common.security.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.security.config.properties.SecurityProperties; +import com.xmzs.common.security.handler.AllUrlHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 权限安全配置 + * + * @author Lion Li + */ + +@Slf4j +@AutoConfiguration +@EnableConfigurationProperties(SecurityProperties.class) +@RequiredArgsConstructor +public class SecurityConfig implements WebMvcConfigurer { + + private final SecurityProperties securityProperties; + + /** + * 注册sa-token的拦截器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册路由拦截器,自定义验证规则 + registry.addInterceptor(new SaInterceptor(handler -> { + AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); + // 登录验证 -- 排除多个路径 + SaRouter + // 获取所有的 + .match(allUrlHandler.getUrls()) + // 对未排除的路径进行检查 + .check(() -> { + // 检查是否登录 是否有token + StpUtil.checkLogin(); + + // 有效率影响 用于临时测试 + // if (log.isDebugEnabled()) { + // log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout()); + // log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); + // } + + }); + })).addPathPatterns("/**") + // 排除不需要拦截的路径 + .excludePathPatterns(securityProperties.getExcludes()); + } + +} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/properties/SecurityProperties.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/properties/SecurityProperties.java new file mode 100644 index 00000000..f4cf99fe --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/config/properties/SecurityProperties.java @@ -0,0 +1,21 @@ +package com.xmzs.common.security.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Security 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "security") +public class SecurityProperties { + + /** + * 排除路径 + */ + private String[] excludes; + + +} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/AllUrlHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/AllUrlHandler.java new file mode 100644 index 00000000..2826f2f5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/AllUrlHandler.java @@ -0,0 +1,39 @@ +package com.xmzs.common.security.handler; + +import cn.hutool.core.util.ReUtil; +import com.xmzs.common.core.utils.SpringUtils; +import lombok.Data; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.*; +import java.util.regex.Pattern; + +/** + * 获取所有Url配置 + * + * @author Lion Li + */ +@Data +public class AllUrlHandler implements InitializingBean { + + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private List urls = new ArrayList<>(); + + @Override + public void afterPropertiesSet() { + Set set = new HashSet<>(); + RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + map.keySet().forEach(info -> { + // 获取注解上边的 path 替代 path variable 为 * + Objects.requireNonNull(info.getPathPatternsCondition().getPatterns()) + .forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*"))); + }); + urls.addAll(set); + } + +} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/GlobalExceptionHandler.java new file mode 100644 index 00000000..6788ea51 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/xmzs/common/security/handler/GlobalExceptionHandler.java @@ -0,0 +1,141 @@ +package com.xmzs.common.security.handler; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.exception.DemoModeException; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.StreamUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; + +/** + * 全局异常处理器 + * + * @author Lion Li + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 权限码异常 + */ + @ExceptionHandler(NotPermissionException.class) + public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 角色权限异常 + */ + @ExceptionHandler(NotRoleException.class) + public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 认证失败 + */ + @ExceptionHandler(NotLoginException.class) + public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return R.fail(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public R handleServiceException(ServiceException e, HttpServletRequest request) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage()); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return R.fail(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public R handleException(Exception e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return R.fail(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public R handleBindException(BindException e) { + log.error(e.getMessage(), e); + String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", "); + return R.fail(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(ConstraintViolationException.class) + public R constraintViolationException(ConstraintViolationException e) { + log.error(e.getMessage(), e); + String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", "); + return R.fail(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return R.fail(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public R handleDemoModeException(DemoModeException e) { + return R.fail("演示模式,不允许操作"); + } +} diff --git a/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..8df4c8bb --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.xmzs.common.security.handler.GlobalExceptionHandler +com.xmzs.common.security.handler.AllUrlHandler +com.xmzs.common.security.config.SecurityConfig diff --git a/ruoyi-common/ruoyi-common-sensitive/pom.xml b/ruoyi-common/ruoyi-common-sensitive/pom.xml new file mode 100644 index 00000000..d0a2af7d --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/pom.xml @@ -0,0 +1,26 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-sensitive + + + ruoyi-common-sensitive 脱敏模块 + + + + + com.xmzs + ruoyi-common-json + + + + diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/annotation/Sensitive.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/annotation/Sensitive.java new file mode 100644 index 00000000..a508f486 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/annotation/Sensitive.java @@ -0,0 +1,24 @@ +package com.xmzs.common.sensitive.annotation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.xmzs.common.sensitive.core.SensitiveStrategy; +import com.xmzs.common.sensitive.handler.SensitiveHandler; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据脱敏注解 + * + * @author zhujie + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveHandler.class) +public @interface Sensitive { + SensitiveStrategy strategy(); +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveService.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveService.java new file mode 100644 index 00000000..0f935699 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveService.java @@ -0,0 +1,18 @@ +package com.xmzs.common.sensitive.core; + +/** + * 脱敏服务 + * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + */ +public interface SensitiveService { + + /** + * 是否脱敏 + */ + boolean isSensitive(); + +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveStrategy.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveStrategy.java new file mode 100644 index 00000000..14e32a6f --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/core/SensitiveStrategy.java @@ -0,0 +1,49 @@ +package com.xmzs.common.sensitive.core; + +import cn.hutool.core.util.DesensitizedUtil; +import lombok.AllArgsConstructor; + +import java.util.function.Function; + +/** + * 脱敏策略 + * + * @author Yjoioooo + * @version 3.6.0 + */ +@AllArgsConstructor +public enum SensitiveStrategy { + + /** + * 身份证脱敏 + */ + ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)), + + /** + * 手机号脱敏 + */ + PHONE(DesensitizedUtil::mobilePhone), + + /** + * 地址脱敏 + */ + ADDRESS(s -> DesensitizedUtil.address(s, 8)), + + /** + * 邮箱脱敏 + */ + EMAIL(DesensitizedUtil::email), + + /** + * 银行卡 + */ + BANK_CARD(DesensitizedUtil::bankCard); + + //可自行添加其他脱敏策略 + + private final Function desensitizer; + + public Function desensitizer() { + return desensitizer; + } +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/handler/SensitiveHandler.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/handler/SensitiveHandler.java new file mode 100644 index 00000000..10899dd9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/xmzs/common/sensitive/handler/SensitiveHandler.java @@ -0,0 +1,54 @@ +package com.xmzs.common.sensitive.handler; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.sensitive.annotation.Sensitive; +import com.xmzs.common.sensitive.core.SensitiveService; +import com.xmzs.common.sensitive.core.SensitiveStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; + +import java.io.IOException; +import java.util.Objects; + +/** + * 数据脱敏json序列化工具 + * + * @author Yjoioooo + */ +@Slf4j +public class SensitiveHandler extends JsonSerializer implements ContextualSerializer { + + private SensitiveStrategy strategy; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + try { + SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class); + if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive()) { + gen.writeString(strategy.desensitizer().apply(value)); + } else { + gen.writeString(value); + } + } catch (BeansException e) { + log.error("脱敏实现不存在, 采用默认处理 => {}", e.getMessage()); + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) { + this.strategy = annotation.strategy(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } +} diff --git a/ruoyi-common/ruoyi-common-sms/pom.xml b/ruoyi-common/ruoyi-common-sms/pom.xml new file mode 100644 index 00000000..d77feae3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/pom.xml @@ -0,0 +1,38 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-sms + + + ruoyi-common-sms 短信模块 + + + + + com.xmzs + ruoyi-common-json + + + + com.aliyun + dysmsapi20170525 + true + + + + com.tencentcloudapi + tencentcloud-sdk-java-sms + true + + + + diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/SmsConfig.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/SmsConfig.java new file mode 100644 index 00000000..4d33b446 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/SmsConfig.java @@ -0,0 +1,48 @@ +package com.xmzs.common.sms.config; + +import com.xmzs.common.sms.config.properties.SmsProperties; +import com.xmzs.common.sms.core.AliyunSmsTemplate; +import com.xmzs.common.sms.core.SmsTemplate; +import com.xmzs.common.sms.core.TencentSmsTemplate; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 短信配置类 + * + * @author Lion Li + * @version 4.2.0 + */ +@AutoConfiguration +@EnableConfigurationProperties(SmsProperties.class) +public class SmsConfig { + + @Configuration + @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") + @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class) + static class AliyunSmsConfig { + + @Bean + public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) { + return new AliyunSmsTemplate(smsProperties); + } + + } + + @Configuration + @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") + @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class) + static class TencentSmsConfig { + + @Bean + public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) { + return new TencentSmsTemplate(smsProperties); + } + + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/properties/SmsProperties.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/properties/SmsProperties.java new file mode 100644 index 00000000..52e3413d --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/config/properties/SmsProperties.java @@ -0,0 +1,45 @@ +package com.xmzs.common.sms.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * SMS短信 配置属性 + * + * @author Lion Li + * @version 4.2.0 + */ +@Data +@ConfigurationProperties(prefix = "sms") +public class SmsProperties { + + private Boolean enabled; + + /** + * 配置节点 + * 阿里云 dysmsapi.aliyuncs.com + * 腾讯云 sms.tencentcloudapi.com + */ + private String endpoint; + + /** + * key + */ + private String accessKeyId; + + /** + * 密匙 + */ + private String accessKeySecret; + + /* + * 短信签名 + */ + private String signName; + + /** + * 短信应用ID (腾讯专属) + */ + private String sdkAppId; + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/AliyunSmsTemplate.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/AliyunSmsTemplate.java new file mode 100644 index 00000000..f224ebca --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/AliyunSmsTemplate.java @@ -0,0 +1,66 @@ +package com.xmzs.common.sms.core; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.teaopenapi.models.Config; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.sms.config.properties.SmsProperties; +import com.xmzs.common.sms.entity.SmsResult; +import com.xmzs.common.sms.exception.SmsException; +import lombok.SneakyThrows; + +import java.util.Map; + +/** + * Aliyun 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class AliyunSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private Client client; + + @SneakyThrows(Exception.class) + public AliyunSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Config config = new Config() + // 您的AccessKey ID + .setAccessKeyId(smsProperties.getAccessKeyId()) + // 您的AccessKey Secret + .setAccessKeySecret(smsProperties.getAccessKeySecret()) + // 访问的域名 + .setEndpoint(smsProperties.getEndpoint()); + this.client = new Client(config); + } + + @Override + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest() + .setPhoneNumbers(phones) + .setSignName(properties.getSignName()) + .setTemplateCode(templateId) + .setTemplateParam(JsonUtils.toJsonString(param)); + try { + SendSmsResponse resp = client.sendSms(req); + return SmsResult.builder() + .isSuccess("OK".equals(resp.getBody().getCode())) + .message(resp.getBody().getMessage()) + .response(JsonUtils.toJsonString(resp)) + .build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/SmsTemplate.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/SmsTemplate.java new file mode 100644 index 00000000..f214ea82 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/SmsTemplate.java @@ -0,0 +1,26 @@ +package com.xmzs.common.sms.core; + +import com.xmzs.common.sms.entity.SmsResult; + +import java.util.Map; + +/** + * 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public interface SmsTemplate { + + /** + * 发送短信 + * + * @param phones 电话号(多个逗号分割) + * @param templateId 模板id + * @param param 模板对应参数 + * 阿里 需使用 模板变量名称对应内容 例如: code=1234 + * 腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数 + */ + SmsResult send(String phones, String templateId, Map param); + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/TencentSmsTemplate.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/TencentSmsTemplate.java new file mode 100644 index 00000000..fb64d668 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/core/TencentSmsTemplate.java @@ -0,0 +1,82 @@ +package com.xmzs.common.sms.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.sms.config.properties.SmsProperties; +import com.xmzs.common.sms.entity.SmsResult; +import com.xmzs.common.sms.exception.SmsException; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import com.tencentcloudapi.sms.v20190711.models.SendStatus; +import lombok.SneakyThrows; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Tencent 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class TencentSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private SmsClient client; + + @SneakyThrows(Exception.class) + public TencentSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret()); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint(smsProperties.getEndpoint()); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + this.client = new SmsClient(credential, "", clientProfile); + } + + @Override + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest(); + Set set = Arrays.stream(phones.split(StringUtils.SEPARATOR)).map(p -> "+86" + p).collect(Collectors.toSet()); + req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class)); + if (CollUtil.isNotEmpty(param)) { + req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class)); + } + req.setTemplateID(templateId); + req.setSign(properties.getSignName()); + req.setSmsSdkAppid(properties.getSdkAppId()); + try { + SendSmsResponse resp = client.SendSms(req); + SmsResult.SmsResultBuilder builder = SmsResult.builder() + .isSuccess(true) + .message("send success") + .response(JsonUtils.toJsonString(resp)); + for (SendStatus sendStatus : resp.getSendStatusSet()) { + if (!"Ok".equals(sendStatus.getCode())) { + builder.isSuccess(false).message(sendStatus.getMessage()); + break; + } + } + return builder.build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/entity/SmsResult.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/entity/SmsResult.java new file mode 100644 index 00000000..264ae766 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/entity/SmsResult.java @@ -0,0 +1,31 @@ +package com.xmzs.common.sms.entity; + +import lombok.Builder; +import lombok.Data; + +/** + * 上传返回体 + * + * @author Lion Li + */ +@Data +@Builder +public class SmsResult { + + /** + * 是否成功 + */ + private boolean isSuccess; + + /** + * 响应消息 + */ + private String message; + + /** + * 实际响应体 + *

+ * 可自行转换为 SDK 对应的 SendSmsResponse + */ + private String response; +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/exception/SmsException.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/exception/SmsException.java new file mode 100644 index 00000000..0ecadfbd --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/xmzs/common/sms/exception/SmsException.java @@ -0,0 +1,19 @@ +package com.xmzs.common.sms.exception; + +import java.io.Serial; + +/** + * Sms异常类 + * + * @author Lion Li + */ +public class SmsException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + public SmsException(String msg) { + super(msg); + } + +} diff --git a/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..8486f346 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.sms.config.SmsConfig diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml new file mode 100644 index 00000000..663236d6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/pom.xml @@ -0,0 +1,37 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-tenant + + + ruoyi-common-tenant 租户模块 + + + + + com.xmzs + ruoyi-common-mybatis + + + + com.xmzs + ruoyi-common-redis + + + + com.alibaba + transmittable-thread-local + + + + + diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/config/TenantConfig.java new file mode 100644 index 00000000..9ab360b8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/config/TenantConfig.java @@ -0,0 +1,100 @@ +package com.xmzs.common.tenant.config; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import com.xmzs.common.mybatis.config.MybatisPlusConfig; +import com.xmzs.common.redis.config.RedisConfig; +import com.xmzs.common.redis.config.properties.RedissonProperties; +import com.xmzs.common.tenant.core.TenantSaTokenDao; +import com.xmzs.common.tenant.handle.PlusTenantLineHandler; +import com.xmzs.common.tenant.handle.TenantKeyPrefixHandler; +import com.xmzs.common.tenant.manager.TenantSpringCacheManager; +import com.xmzs.common.tenant.properties.TenantProperties; +import org.redisson.config.ClusterServersConfig; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import java.util.ArrayList; +import java.util.List; + +/** + * 租户配置类 + * + * @author Lion Li + */ +@EnableConfigurationProperties(TenantProperties.class) +@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class}) +@ConditionalOnProperty(value = "tenant.enable", havingValue = "true") +public class TenantConfig { + + /** + * 初始化租户配置 + */ + @Bean + public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor, + TenantProperties tenantProperties) { + List interceptors = new ArrayList<>(); + // 多租户插件 必须放到第一位 + interceptors.add(tenantLineInnerInterceptor(tenantProperties)); + interceptors.addAll(mybatisPlusInterceptor.getInterceptors()); + mybatisPlusInterceptor.setInterceptors(interceptors); + return true; + } + + /** + * 多租户插件 + */ + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) { + return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties)); + } + + @Bean + public RedissonAutoConfigurationCustomizer tenantRedissonCustomizer(RedissonProperties redissonProperties) { + return config -> { + TenantKeyPrefixHandler nameMapper = new TenantKeyPrefixHandler(redissonProperties.getKeyPrefix()); + SingleServerConfig singleServerConfig = ReflectUtils.invokeGetter(config, "singleServerConfig"); + if (ObjectUtil.isNotNull(singleServerConfig)) { + // 使用单机模式 + // 设置多租户 redis key前缀 + singleServerConfig.setNameMapper(nameMapper); + ReflectUtils.invokeSetter(config, "singleServerConfig", singleServerConfig); + } + ClusterServersConfig clusterServersConfig = ReflectUtils.invokeGetter(config, "clusterServersConfig"); + // 集群配置方式 参考下方注释 + if (ObjectUtil.isNotNull(clusterServersConfig)) { + // 设置多租户 redis key前缀 + clusterServersConfig.setNameMapper(nameMapper); + ReflectUtils.invokeSetter(config, "clusterServersConfig", clusterServersConfig); + } + }; + } + + /** + * 多租户缓存管理器 + */ + @Primary + @Bean + public CacheManager tenantCacheManager() { + return new TenantSpringCacheManager(); + } + + /** + * 多租户鉴权dao实现 + */ + @Primary + @Bean + public SaTokenDao tenantSaTokenDao() { + return new TenantSaTokenDao(); + } + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantEntity.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantEntity.java new file mode 100644 index 00000000..efe2bf6b --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantEntity.java @@ -0,0 +1,21 @@ +package com.xmzs.common.tenant.core; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 租户基类 + * + * @author Michelle.Chung + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TenantEntity extends BaseEntity { + + /** + * 租户编号 + */ + private String tenantId; + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantSaTokenDao.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantSaTokenDao.java new file mode 100644 index 00000000..53e267b3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/core/TenantSaTokenDao.java @@ -0,0 +1,148 @@ +package com.xmzs.common.tenant.core; + +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.satoken.core.dao.PlusSaTokenDao; + +import java.time.Duration; +import java.util.List; + +/** + * SaToken 认证数据持久层 适配多租户 + * + * @author Lion Li + */ +public class TenantSaTokenDao extends PlusSaTokenDao { + + @Override + public String get(String key) { + return super.get(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + @Override + public void set(String key, String value, long timeout) { + super.set(GlobalConstants.GLOBAL_REDIS_KEY + key, value, timeout); + } + + /** + * 修修改指定key-value键值对 (过期时间不变) + */ + @Override + public void update(String key, String value) { + long expire = getTimeout(key); + // -2 = 无此键 + if (expire == NOT_VALUE_EXPIRE) { + return; + } + this.set(key, value, expire); + } + + /** + * 删除Value + */ + @Override + public void delete(String key) { + super.delete(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 获取Value的剩余存活时间 (单位: 秒) + */ + @Override + public long getTimeout(String key) { + return super.getTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 修改Value的剩余存活时间 (单位: 秒) + */ + @Override + public void updateTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == NEVER_EXPIRE) { + long expire = getTimeout(key); + if (expire == NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.set(key, this.get(key), timeout); + } + return; + } + RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout)); + } + + + /** + * 获取Object,如无返空 + */ + @Override + public Object getObject(String key) { + return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 写入Object,并设定存活时间 (单位: 秒) + */ + @Override + public void setObject(String key, Object object, long timeout) { + super.setObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object, timeout); + } + + /** + * 更新Object (过期时间不变) + */ + @Override + public void updateObject(String key, Object object) { + long expire = getObjectTimeout(key); + // -2 = 无此键 + if (expire == NOT_VALUE_EXPIRE) { + return; + } + this.setObject(key, object, expire); + } + + /** + * 删除Object + */ + @Override + public void deleteObject(String key) { + super.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 获取Object的剩余存活时间 (单位: 秒) + */ + @Override + public long getObjectTimeout(String key) { + return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 修改Object的剩余存活时间 (单位: 秒) + */ + @Override + public void updateObjectTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == NEVER_EXPIRE) { + long expire = getObjectTimeout(key); + if (expire == NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.setObject(key, this.getObject(key), timeout); + } + return; + } + RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout)); + } + + + /** + * 搜索数据 + */ + @Override + public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { + return super.searchData(GlobalConstants.GLOBAL_REDIS_KEY + prefix, keyword, start, size, sortType); + } +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/exception/TenantException.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/exception/TenantException.java new file mode 100644 index 00000000..23e549de --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/exception/TenantException.java @@ -0,0 +1,20 @@ +package com.xmzs.common.tenant.exception; + +import com.xmzs.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 租户异常类 + * + * @author Lion Li + */ +public class TenantException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public TenantException(String code, Object... args) { + super("tenant", code, args, null); + } +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/PlusTenantLineHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/PlusTenantLineHandler.java new file mode 100644 index 00000000..6d8106ca --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/PlusTenantLineHandler.java @@ -0,0 +1,59 @@ +package com.xmzs.common.tenant.handle; + +import cn.hutool.core.collection.ListUtil; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.common.tenant.properties.TenantProperties; +import lombok.AllArgsConstructor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.NullValue; +import net.sf.jsqlparser.expression.StringValue; + +import java.util.List; + +/** + * 自定义租户处理器 + * + * @author Lion Li + */ +@AllArgsConstructor +public class PlusTenantLineHandler implements TenantLineHandler { + + private final TenantProperties tenantProperties; + + @Override + public Expression getTenantId() { + String tenantId = LoginHelper.getTenantId(); + if (StringUtils.isBlank(tenantId)) { + return new NullValue(); + } + String dynamicTenantId = TenantHelper.getDynamic(); + if (StringUtils.isNotBlank(dynamicTenantId)) { + // 返回动态租户 + return new StringValue(dynamicTenantId); + } + // 返回固定租户 + return new StringValue(tenantId); + } + + @Override + public boolean ignoreTable(String tableName) { + String tenantId = LoginHelper.getTenantId(); + // 判断是否有租户 + if (StringUtils.isNotBlank(tenantId)) { + // 不需要过滤租户的表 + List excludes = tenantProperties.getExcludes(); + // 非业务表 + List tables = ListUtil.toList( + "gen_table", + "gen_table_column" + ); + tables.addAll(excludes); + return tables.contains(tableName); + } + return true; + } + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/TenantKeyPrefixHandler.java new file mode 100644 index 00000000..18a49a29 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/handle/TenantKeyPrefixHandler.java @@ -0,0 +1,58 @@ +package com.xmzs.common.tenant.handle; + +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.redis.handler.KeyPrefixHandler; +import com.xmzs.common.tenant.helper.TenantHelper; + +/** + * 多租户redis缓存key前缀处理 + * + * @author Lion Li + */ +public class TenantKeyPrefixHandler extends KeyPrefixHandler { + + public TenantKeyPrefixHandler(String keyPrefix) { + super(keyPrefix); + } + + /** + * 增加前缀 + */ + @Override + public String map(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { + return super.map(name); + } + String tenantId = TenantHelper.getTenantId(); + if (StringUtils.startsWith(name, tenantId)) { + // 如果存在则直接返回 + return super.map(name); + } + return super.map(tenantId + ":" + name); + } + + /** + * 去除前缀 + */ + @Override + public String unmap(String name) { + String unmap = super.unmap(name); + if (StringUtils.isBlank(unmap)) { + return null; + } + if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { + return super.unmap(name); + } + String tenantId = TenantHelper.getTenantId(); + if (StringUtils.startsWith(unmap, tenantId)) { + // 如果存在则删除 + return unmap.substring((tenantId + ":").length()); + } + return unmap; + } + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/helper/TenantHelper.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/helper/TenantHelper.java new file mode 100644 index 00000000..ba8ca152 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/helper/TenantHelper.java @@ -0,0 +1,140 @@ +package com.xmzs.common.tenant.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.spring.SpringMVCUtil; +import cn.hutool.core.convert.Convert; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.satoken.utils.LoginHelper; + +import java.util.function.Supplier; + +/** + * 租户助手 + * + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TenantHelper { + + private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant"; + + private static final ThreadLocal TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>(); + + /** + * 租户功能是否启用 + */ + public static boolean isEnable() { + return Convert.toBool(SpringUtils.getProperty("tenant.enable"), false); + } + + /** + * 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭) + */ + public static void enableIgnore() { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()); + } + + /** + * 关闭忽略租户 + */ + public static void disableIgnore() { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } + + /** + * 在忽略租户中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略租户中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + + /** + * 设置动态租户(一直有效 需要手动清理) + *

+ * 如果为非web环境 那么只在当前线程内生效 + */ + public static void setDynamic(String tenantId) { + if (!SpringMVCUtil.isWeb()) { + TEMP_DYNAMIC_TENANT.set(tenantId); + return; + } + String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId(); + RedisUtils.setCacheObject(cacheKey, tenantId); + SaHolder.getStorage().set(cacheKey, tenantId); + } + + /** + * 获取动态租户(一直有效 需要手动清理) + *

+ * 如果为非web环境 那么只在当前线程内生效 + */ + public static String getDynamic() { + if (!SpringMVCUtil.isWeb()) { + return TEMP_DYNAMIC_TENANT.get(); + } + String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId(); + String tenantId = (String) SaHolder.getStorage().get(cacheKey); + if (StringUtils.isNotBlank(tenantId)) { + return tenantId; + } + tenantId = RedisUtils.getCacheObject(cacheKey); + SaHolder.getStorage().set(cacheKey, tenantId); + return tenantId; + } + + /** + * 清除动态租户 + */ + public static void clearDynamic() { + if (!SpringMVCUtil.isWeb()) { + TEMP_DYNAMIC_TENANT.remove(); + return; + } + String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId(); + RedisUtils.deleteObject(cacheKey); + SaHolder.getStorage().delete(cacheKey); + } + + /** + * 获取当前租户id(动态租户优先) + */ + public static String getTenantId() { + String tenantId = TenantHelper.getDynamic(); + if (StringUtils.isBlank(tenantId)) { + tenantId = LoginHelper.getTenantId(); + } + return tenantId; + } + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/manager/TenantSpringCacheManager.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/manager/TenantSpringCacheManager.java new file mode 100644 index 00000000..966f2c5d --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/manager/TenantSpringCacheManager.java @@ -0,0 +1,32 @@ +package com.xmzs.common.tenant.manager; + +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.redis.manager.PlusSpringCacheManager; +import com.xmzs.common.tenant.helper.TenantHelper; +import org.springframework.cache.Cache; + +/** + * 重写 cacheName 处理方法 支持多租户 + * + * @author Lion Li + */ +public class TenantSpringCacheManager extends PlusSpringCacheManager { + + public TenantSpringCacheManager() { + } + + @Override + public Cache getCache(String name) { + if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { + return super.getCache(name); + } + String tenantId = TenantHelper.getTenantId(); + if (StringUtils.startsWith(name, tenantId)) { + // 如果存在则直接返回 + return super.getCache(name); + } + return super.getCache(tenantId + ":" + name); + } + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/properties/TenantProperties.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/properties/TenantProperties.java new file mode 100644 index 00000000..00ad9e8e --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/xmzs/common/tenant/properties/TenantProperties.java @@ -0,0 +1,27 @@ +package com.xmzs.common.tenant.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/** + * 租户 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "tenant") +public class TenantProperties { + + /** + * 是否启用 + */ + private Boolean enable; + + /** + * 排除表 + */ + private List excludes; + +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..ba20df88 --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xmzs.common.tenant.config.TenantConfig diff --git a/ruoyi-common/ruoyi-common-translation/pom.xml b/ruoyi-common/ruoyi-common-translation/pom.xml new file mode 100644 index 00000000..5e49cdbf --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/pom.xml @@ -0,0 +1,28 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-translation + + + ruoyi-common-translation 通用翻译功能 + + + + + + com.xmzs + ruoyi-common-json + + + + + diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/Translation.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/Translation.java new file mode 100644 index 00000000..256b0388 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/Translation.java @@ -0,0 +1,39 @@ +package com.xmzs.common.translation.annotation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.xmzs.common.translation.core.handler.TranslationHandler; + +import java.lang.annotation.*; + +/** + * 通用翻译注解 + * + * @author Lion Li + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Documented +@JacksonAnnotationsInside +@JsonSerialize(using = TranslationHandler.class) +public @interface Translation { + + /** + * 类型 (需与实现类上的 {@link TranslationType} 注解type对应) + *

+ * 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值 + */ + String type(); + + /** + * 映射字段 (如果不为空则取此字段的值) + */ + String mapper() default ""; + + /** + * 其他条件 例如: 字典type(sys_user_sex) + */ + String other() default ""; + +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/TranslationType.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/TranslationType.java new file mode 100644 index 00000000..62222356 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/annotation/TranslationType.java @@ -0,0 +1,23 @@ +package com.xmzs.common.translation.annotation; + +import com.xmzs.common.translation.core.TranslationInterface; + +import java.lang.annotation.*; + +/** + * 翻译类型注解 (标注到{@link TranslationInterface} 的实现类) + * + * @author Lion Li + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +public @interface TranslationType { + + /** + * 类型 + */ + String type(); + +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/config/TranslationConfig.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/config/TranslationConfig.java new file mode 100644 index 00000000..164ee21c --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/config/TranslationConfig.java @@ -0,0 +1,50 @@ +package com.xmzs.common.translation.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.translation.annotation.TranslationType; +import com.xmzs.common.translation.core.TranslationInterface; +import com.xmzs.common.translation.core.handler.TranslationBeanSerializerModifier; +import com.xmzs.common.translation.core.handler.TranslationHandler; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 翻译模块配置类 + * + * @author Lion Li + */ +@Slf4j +@AutoConfiguration +public class TranslationConfig { + + @Autowired + private List> list; + + @Autowired + private ObjectMapper objectMapper; + + @PostConstruct + public void init() { + Map> map = new HashMap<>(list.size()); + for (TranslationInterface trans : list) { + if (trans.getClass().isAnnotationPresent(TranslationType.class)) { + TranslationType annotation = trans.getClass().getAnnotation(TranslationType.class); + map.put(annotation.type(), trans); + } else { + log.warn(trans.getClass().getName() + " 翻译实现类未标注 TranslationType 注解!"); + } + } + TranslationHandler.TRANSLATION_MAPPER.putAll(map); + // 设置 Bean 序列化修改器 + objectMapper.setSerializerFactory( + objectMapper.getSerializerFactory() + .withSerializerModifier(new TranslationBeanSerializerModifier())); + } + +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/constant/TransConstant.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/constant/TransConstant.java new file mode 100644 index 00000000..2afa6d81 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/constant/TransConstant.java @@ -0,0 +1,30 @@ +package com.xmzs.common.translation.constant; + +/** + * 翻译常量 + * + * @author Lion Li + */ +public interface TransConstant { + + /** + * 用户id转账号 + */ + String USER_ID_TO_NAME = "user_id_to_name"; + + /** + * 部门id转名称 + */ + String DEPT_ID_TO_NAME = "dept_id_to_name"; + + /** + * 字典type转label + */ + String DICT_TYPE_TO_LABEL = "dict_type_to_label"; + + /** + * ossId转url + */ + String OSS_ID_TO_URL = "oss_id_to_url"; + +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/TranslationInterface.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/TranslationInterface.java new file mode 100644 index 00000000..b3e1dc7c --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/TranslationInterface.java @@ -0,0 +1,20 @@ +package com.xmzs.common.translation.core; + +import com.xmzs.common.translation.annotation.TranslationType; + +/** + * 翻译接口 (实现类需标注 {@link TranslationType} 注解标明翻译类型) + * + * @author Lion Li + */ +public interface TranslationInterface { + + /** + * 翻译 + * + * @param key 需要被翻译的键(不为空) + * @param other 其他参数 + * @return 返回键对应的值 + */ + T translation(Object key, String other); +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationBeanSerializerModifier.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationBeanSerializerModifier.java new file mode 100644 index 00000000..c38097d8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationBeanSerializerModifier.java @@ -0,0 +1,29 @@ +package com.xmzs.common.translation.core.handler; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; + +import java.util.List; + +/** + * Bean 序列化修改器 解决 Null 被单独处理问题 + * + * @author Lion Li + */ +public class TranslationBeanSerializerModifier extends BeanSerializerModifier { + + @Override + public List changeProperties(SerializationConfig config, BeanDescription beanDesc, + List beanProperties) { + for (BeanPropertyWriter writer : beanProperties) { + // 如果序列化器为 TranslationHandler 的话 将 Null 值也交给他处理 + if (writer.getSerializer() instanceof TranslationHandler serializer) { + writer.assignNullSerializer(serializer); + } + } + return beanProperties; + } + +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationHandler.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationHandler.java new file mode 100644 index 00000000..4d4bcaba --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/handler/TranslationHandler.java @@ -0,0 +1,65 @@ +package com.xmzs.common.translation.core.handler; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.reflect.ReflectUtils; +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.common.translation.core.TranslationInterface; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 翻译处理器 + * + * @author Lion Li + */ +@Slf4j +public class TranslationHandler extends JsonSerializer implements ContextualSerializer { + + /** + * 全局翻译实现类映射器 + */ + public static final Map> TRANSLATION_MAPPER = new ConcurrentHashMap<>(); + + private Translation translation; + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + TranslationInterface trans = TRANSLATION_MAPPER.get(translation.type()); + if (ObjectUtil.isNotNull(trans)) { + // 如果映射字段不为空 则取映射字段的值 + if (StringUtils.isNotBlank(translation.mapper())) { + value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper()); + } + // 如果为 null 直接写出 + if (ObjectUtil.isNull(value)) { + gen.writeNull(); + return; + } + Object result = trans.translation(value, translation.other()); + gen.writeObject(result); + } else { + gen.writeObject(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { + Translation translation = property.getAnnotation(Translation.class); + if (Objects.nonNull(translation)) { + this.translation = translation; + return this; + } + return prov.findValueSerializer(property.getType(), property); + } +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DeptNameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DeptNameTranslationImpl.java new file mode 100644 index 00000000..7c92cb06 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DeptNameTranslationImpl.java @@ -0,0 +1,29 @@ +package com.xmzs.common.translation.core.impl; + +import com.xmzs.common.core.service.DeptService; +import com.xmzs.common.translation.annotation.TranslationType; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.common.translation.core.TranslationInterface; +import lombok.AllArgsConstructor; + +/** + * 部门翻译实现 + * + * @author Lion Li + */ +@AllArgsConstructor +@TranslationType(type = TransConstant.DEPT_ID_TO_NAME) +public class DeptNameTranslationImpl implements TranslationInterface { + + private final DeptService deptService; + + @Override + public String translation(Object key, String other) { + if (key instanceof String ids) { + return deptService.selectDeptNameByIds(ids); + } else if (key instanceof Long id) { + return deptService.selectDeptNameByIds(id.toString()); + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DictTypeTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DictTypeTranslationImpl.java new file mode 100644 index 00000000..4fb6557a --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/DictTypeTranslationImpl.java @@ -0,0 +1,28 @@ +package com.xmzs.common.translation.core.impl; + +import com.xmzs.common.core.service.DictService; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.translation.annotation.TranslationType; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.common.translation.core.TranslationInterface; +import lombok.AllArgsConstructor; + +/** + * 字典翻译实现 + * + * @author Lion Li + */ +@AllArgsConstructor +@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL) +public class DictTypeTranslationImpl implements TranslationInterface { + + private final DictService dictService; + + @Override + public String translation(Object key, String other) { + if (key instanceof String dictValue && StringUtils.isNotBlank(other)) { + return dictService.getDictLabel(other, dictValue); + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/OssUrlTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/OssUrlTranslationImpl.java new file mode 100644 index 00000000..5337acfc --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/OssUrlTranslationImpl.java @@ -0,0 +1,29 @@ +package com.xmzs.common.translation.core.impl; + +import com.xmzs.common.core.service.OssService; +import com.xmzs.common.translation.annotation.TranslationType; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.common.translation.core.TranslationInterface; +import lombok.AllArgsConstructor; + +/** + * OSS翻译实现 + * + * @author Lion Li + */ +@AllArgsConstructor +@TranslationType(type = TransConstant.OSS_ID_TO_URL) +public class OssUrlTranslationImpl implements TranslationInterface { + + private final OssService ossService; + + @Override + public String translation(Object key, String other) { + if (key instanceof String ids) { + return ossService.selectUrlByIds(ids); + } else if (key instanceof Long id) { + return ossService.selectUrlByIds(id.toString()); + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/UserNameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/UserNameTranslationImpl.java new file mode 100644 index 00000000..5ae02c72 --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/com/xmzs/common/translation/core/impl/UserNameTranslationImpl.java @@ -0,0 +1,27 @@ +package com.xmzs.common.translation.core.impl; + +import com.xmzs.common.core.service.UserService; +import com.xmzs.common.translation.annotation.TranslationType; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.common.translation.core.TranslationInterface; +import lombok.AllArgsConstructor; + +/** + * 用户名翻译实现 + * + * @author Lion Li + */ +@AllArgsConstructor +@TranslationType(type = TransConstant.USER_ID_TO_NAME) +public class UserNameTranslationImpl implements TranslationInterface { + + private final UserService userService; + + @Override + public String translation(Object key, String other) { + if (key instanceof Long id) { + return userService.selectUserNameById(id); + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..378032bd --- /dev/null +++ b/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.xmzs.common.translation.config.TranslationConfig +com.xmzs.common.translation.core.impl.DeptNameTranslationImpl +com.xmzs.common.translation.core.impl.DictTypeTranslationImpl +com.xmzs.common.translation.core.impl.OssUrlTranslationImpl +com.xmzs.common.translation.core.impl.UserNameTranslationImpl diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml new file mode 100644 index 00000000..7bac726b --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -0,0 +1,68 @@ + + + + com.xmzs + ruoyi-common + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-common-web + + + ruoyi-common-web web服务 + + + + + com.xmzs + ruoyi-common-json + + + + com.xmzs + ruoyi-common-redis + + + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + org.springframework.boot + spring-boot-starter-actuator + + + + cn.hutool + hutool-captcha + + + + cn.hutool + hutool-crypto + + + + com.alibaba + transmittable-thread-local + + + + diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/CaptchaConfig.java new file mode 100644 index 00000000..84f6170a --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/CaptchaConfig.java @@ -0,0 +1,65 @@ +package com.xmzs.common.web.config; + +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.CircleCaptcha; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.captcha.ShearCaptcha; +import com.xmzs.common.web.config.properties.CaptchaProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; + +import java.awt.*; + +/** + * 验证码配置 + * + * @author Lion Li + */ +@AutoConfiguration +@EnableConfigurationProperties(CaptchaProperties.class) +public class CaptchaConfig { + + private static final int WIDTH = 160; + private static final int HEIGHT = 60; + private static final Color BACKGROUND = Color.PINK; + private static final Font FONT = new Font("Arial", Font.BOLD, 48); + + /** + * 圆圈干扰验证码 + */ + @Lazy + @Bean + public CircleCaptcha circleCaptcha() { + CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + + /** + * 线段干扰的验证码 + */ + @Lazy + @Bean + public LineCaptcha lineCaptcha() { + LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + + /** + * 扭曲干扰验证码 + */ + @Lazy + @Bean + public ShearCaptcha shearCaptcha() { + ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/FilterConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/FilterConfig.java new file mode 100644 index 00000000..20962f3e --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/FilterConfig.java @@ -0,0 +1,53 @@ +package com.xmzs.common.web.config; + +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.web.config.properties.XssProperties; +import com.xmzs.common.web.filter.RepeatableFilter; +import com.xmzs.common.web.filter.XssFilter; +import jakarta.servlet.DispatcherType; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; + +import java.util.HashMap; +import java.util.Map; + +/** + * Filter配置 + * + * @author Lion Li + */ +@AutoConfiguration +@EnableConfigurationProperties(XssProperties.class) +public class FilterConfig { + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration(XssProperties xssProperties) { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR)); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap<>(); + initParameters.put("excludes", xssProperties.getExcludes()); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean someFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/I18nConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/I18nConfig.java new file mode 100644 index 00000000..e0e5b845 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/I18nConfig.java @@ -0,0 +1,22 @@ +package com.xmzs.common.web.config; + +import com.xmzs.common.web.core.I18nLocaleResolver; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.web.servlet.LocaleResolver; + +/** + * 国际化配置 + * + * @author Lion Li + */ +@AutoConfiguration(before = WebMvcAutoConfiguration.class) +public class I18nConfig { + + @Bean + public LocaleResolver localeResolver() { + return new I18nLocaleResolver(); + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/ResourcesConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/ResourcesConfig.java new file mode 100644 index 00000000..df747c53 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/ResourcesConfig.java @@ -0,0 +1,52 @@ +package com.xmzs.common.web.config; + +import com.xmzs.common.web.interceptor.PlusWebInvokeTimeInterceptor; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 通用配置 + * + * @author Lion Li + */ +@AutoConfiguration +public class ResourcesConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 全局访问性能拦截 + registry.addInterceptor(new PlusWebInvokeTimeInterceptor()); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/UndertowConfig.java new file mode 100644 index 00000000..1d3a4d31 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/UndertowConfig.java @@ -0,0 +1,30 @@ +package com.xmzs.common.web.config; + +import io.undertow.server.DefaultByteBufferPool; +import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; + +/** + * Undertow 自定义配置 + * + * @author Lion Li + */ +@AutoConfiguration +public class UndertowConfig implements WebServerFactoryCustomizer { + + /** + * 设置 Undertow 的 websocket 缓冲池 + */ + @Override + public void customize(UndertowServletWebServerFactory factory) { + // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配 + factory.addDeploymentInfoCustomizers(deploymentInfo -> { + WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); + webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512)); + deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); + }); + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/CaptchaProperties.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/CaptchaProperties.java new file mode 100644 index 00000000..8a83e261 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/CaptchaProperties.java @@ -0,0 +1,38 @@ +package com.xmzs.common.web.config.properties; + +import com.xmzs.common.web.enums.CaptchaCategory; +import com.xmzs.common.web.enums.CaptchaType; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 验证码 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties { + + private Boolean enable; + + /** + * 验证码类型 + */ + private CaptchaType type; + + /** + * 验证码类别 + */ + private CaptchaCategory category; + + /** + * 数字验证码位数 + */ + private Integer numberLength; + + /** + * 字符验证码长度 + */ + private Integer charLength; +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/XssProperties.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/XssProperties.java new file mode 100644 index 00000000..8d381f33 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/config/properties/XssProperties.java @@ -0,0 +1,30 @@ +package com.xmzs.common.web.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * xss过滤 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "xss") +public class XssProperties { + + /** + * 过滤开关 + */ + private String enabled; + + /** + * 排除链接(多个用逗号分隔) + */ + private String excludes; + + /** + * 匹配链接 + */ + private String urlPatterns; + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/BaseController.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/BaseController.java new file mode 100644 index 00000000..b5c8732d --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/BaseController.java @@ -0,0 +1,40 @@ +package com.xmzs.common.web.core; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.StringUtils; + +/** + * web层通用数据处理 + * + * @author Lion Li + */ +public class BaseController { + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected R toAjax(int rows) { + return rows > 0 ? R.ok() : R.fail(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected R toAjax(boolean result) { + return result ? R.ok() : R.fail(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) { + return StringUtils.format("redirect:{}", url); + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/I18nLocaleResolver.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/I18nLocaleResolver.java new file mode 100644 index 00000000..f84e21ad --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/core/I18nLocaleResolver.java @@ -0,0 +1,31 @@ +package com.xmzs.common.web.core; + +import org.springframework.web.servlet.LocaleResolver; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Locale; + +/** + * 获取请求头国际化信息 + * + * @author Lion Li + */ +public class I18nLocaleResolver implements LocaleResolver { + + @Override + public Locale resolveLocale(HttpServletRequest httpServletRequest) { + String language = httpServletRequest.getHeader("content-language"); + Locale locale = Locale.getDefault(); + if (language != null && language.length() > 0) { + String[] split = language.split("_"); + locale = new Locale(split[0], split[1]); + } + return locale; + } + + @Override + public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { + + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaCategory.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaCategory.java new file mode 100644 index 00000000..8f1ce6b3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaCategory.java @@ -0,0 +1,35 @@ +package com.xmzs.common.web.enums; + +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.CircleCaptcha; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.captcha.ShearCaptcha; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 验证码类别 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum CaptchaCategory { + + /** + * 线段干扰 + */ + LINE(LineCaptcha.class), + + /** + * 圆圈干扰 + */ + CIRCLE(CircleCaptcha.class), + + /** + * 扭曲干扰 + */ + SHEAR(ShearCaptcha.class); + + private final Class clazz; +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaType.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaType.java new file mode 100644 index 00000000..929d972f --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/enums/CaptchaType.java @@ -0,0 +1,29 @@ +package com.xmzs.common.web.enums; + +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.captcha.generator.RandomGenerator; +import com.xmzs.common.web.utils.UnsignedMathGenerator; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 验证码类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum CaptchaType { + + /** + * 数字 + */ + MATH(UnsignedMathGenerator.class), + + /** + * 字符 + */ + CHAR(RandomGenerator.class); + + private final Class clazz; +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatableFilter.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatableFilter.java new file mode 100644 index 00000000..25758645 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatableFilter.java @@ -0,0 +1,40 @@ +package com.xmzs.common.web.filter; + +import com.xmzs.common.core.utils.StringUtils; +import org.springframework.http.MediaType; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) { + chain.doFilter(request, response); + } else { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() { + + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 00000000..3a73d340 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,68 @@ +package com.xmzs.common.web.filter; + +import cn.hutool.core.io.IoUtil; +import com.xmzs.common.core.constant.Constants; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = IoUtil.readBytes(request.getInputStream(), false); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public int available() throws IOException { + return body.length; + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssFilter.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssFilter.java new file mode 100644 index 00000000..84e459f5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssFilter.java @@ -0,0 +1,62 @@ +package com.xmzs.common.web.filter; + +import com.xmzs.common.core.utils.StringUtils; +import org.springframework.http.HttpMethod; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter { + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) { + String[] url = tempExcludes.split(StringUtils.SEPARATOR); + for (int i = 0; url != null && i < url.length; i++) { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() { + + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..44d8cf81 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,97 @@ +package com.xmzs.common.web.filter; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import com.xmzs.common.core.utils.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values != null) { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) { + // 防xss攻击和过滤前后空格 + escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + // 非json类型,直接返回 + if (!isJsonRequest()) { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8); + if (StringUtils.isEmpty(json)) { + return super.getInputStream(); + } + + // xss过滤 + json = HtmlUtil.cleanHtmlTag(json).trim(); + byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); + final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public int available() throws IOException { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + */ + public boolean isJsonRequest() { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/interceptor/PlusWebInvokeTimeInterceptor.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/interceptor/PlusWebInvokeTimeInterceptor.java new file mode 100644 index 00000000..2bd93314 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/interceptor/PlusWebInvokeTimeInterceptor.java @@ -0,0 +1,94 @@ +package com.xmzs.common.web.interceptor; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.web.filter.RepeatedlyRequestWrapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import java.io.BufferedReader; +import java.util.Map; + +/** + * web的调用时间统计拦截器 + * dev环境有效 + * + * @author Lion Li + * @since 3.3.0 + */ +@Slf4j +public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { + + private final String prodProfile = "prod"; + + private final TransmittableThreadLocal invokeTimeTL = new TransmittableThreadLocal<>(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!prodProfile.equals(SpringUtils.getActiveProfile())) { + String url = request.getMethod() + " " + request.getRequestURI(); + + // 打印请求参数 + if (isJsonRequest(request)) { + String jsonParam = ""; + if (request instanceof RepeatedlyRequestWrapper) { + BufferedReader reader = request.getReader(); + jsonParam = IoUtil.read(reader); + } + log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); + } else { + Map parameterMap = request.getParameterMap(); + if (MapUtil.isNotEmpty(parameterMap)) { + String parameters = JsonUtils.toJsonString(parameterMap); + log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); + } else { + log.debug("[PLUS]开始请求 => URL[{}],无参数", url); + } + } + + StopWatch stopWatch = new StopWatch(); + invokeTimeTL.set(stopWatch); + stopWatch.start(); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + if (!prodProfile.equals(SpringUtils.getActiveProfile())) { + StopWatch stopWatch = invokeTimeTL.get(); + stopWatch.stop(); + log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); + invokeTimeTL.remove(); + } + } + + /** + * 判断本次请求的数据类型是否为json + * + * @param request request + * @return boolean + */ + private boolean isJsonRequest(HttpServletRequest request) { + String contentType = request.getContentType(); + if (contentType != null) { + return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE); + } + return false; + } + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/utils/UnsignedMathGenerator.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/utils/UnsignedMathGenerator.java new file mode 100644 index 00000000..aec75e5c --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/xmzs/common/web/utils/UnsignedMathGenerator.java @@ -0,0 +1,88 @@ +package com.xmzs.common.web.utils; + +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.math.Calculator; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.RandomUtil; +import com.xmzs.common.core.utils.StringUtils; + +import java.io.Serial; + +/** + * 无符号计算生成器 + * + * @author Lion Li + */ +public class UnsignedMathGenerator implements CodeGenerator { + + @Serial + private static final long serialVersionUID = -5514819971774091076L; + + private static final String OPERATORS = "+-*"; + + /** + * 参与计算数字最大长度 + */ + private final int numberLength; + + /** + * 构造 + */ + public UnsignedMathGenerator() { + this(2); + } + + /** + * 构造 + * + * @param numberLength 参与计算最大数字位数 + */ + public UnsignedMathGenerator(int numberLength) { + this.numberLength = numberLength; + } + + @Override + public String generate() { + final int limit = getLimit(); + int a = RandomUtil.randomInt(limit); + int b = RandomUtil.randomInt(limit); + String max = Integer.toString(Math.max(a,b)); + String min = Integer.toString(Math.min(a,b)); + max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE); + min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE); + + return max + RandomUtil.randomChar(OPERATORS) + min + '='; + } + + @Override + public boolean verify(String code, String userInputCode) { + int result; + try { + result = Integer.parseInt(userInputCode); + } catch (NumberFormatException e) { + // 用户输入非数字 + return false; + } + + final int calculateResult = (int) Calculator.conversion(code); + return result == calculateResult; + } + + /** + * 获取验证码长度 + * + * @return 验证码长度 + */ + public int getLength() { + return this.numberLength * 2 + 2; + } + + /** + * 根据长度获取参与计算数字最大值 + * + * @return 最大值 + */ + private int getLimit() { + return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength)); + } +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..ae11f013 --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.xmzs.common.web.config.CaptchaConfig +com.xmzs.common.web.config.FilterConfig +com.xmzs.common.web.config.I18nConfig +com.xmzs.common.web.config.ResourcesConfig +com.xmzs.common.web.config.UndertowConfig diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml new file mode 100644 index 00000000..bd7f3881 --- /dev/null +++ b/ruoyi-modules/pom.xml @@ -0,0 +1,27 @@ + + + + ruoyi-ai + com.xmzs + ${revision} + ../pom.xml + + 4.0.0 + + + ruoyi-demo + ruoyi-generator + ruoyi-job + ruoyi-system + + + ruoyi-modules + pom + + + ruoyi-modules 业务模块 + + + diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml new file mode 100644 index 00000000..877f8641 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/pom.xml @@ -0,0 +1,115 @@ + + + + com.xmzs + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-demo + + + demo模块 + + + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-doc + + + + com.xmzs + ruoyi-common-sms + + + + com.xmzs + ruoyi-common-mail + + + + com.xmzs + ruoyi-common-redis + + + + com.xmzs + ruoyi-common-idempotent + + + + com.xmzs + ruoyi-common-mybatis + + + + com.xmzs + ruoyi-common-log + + + + com.xmzs + ruoyi-common-excel + + + + com.xmzs + ruoyi-common-web + + + + com.xmzs + ruoyi-common-ratelimiter + + + + com.xmzs + ruoyi-common-translation + + + + com.xmzs + ruoyi-common-sensitive + + + + com.xmzs + ruoyi-common-encrypt + + + + com.xmzs + ruoyi-common-tenant + + + + com.xmzs + ruoyi-common-pay + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/MailController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/MailController.java new file mode 100644 index 00000000..c6d5fcc4 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/MailController.java @@ -0,0 +1,52 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.mail.utils.MailUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; + + +/** + * 邮件发送案例 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/mail") +public class MailController { + + /** + * 发送邮件 + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + */ + @GetMapping("/sendSimpleMessage") + public R sendSimpleMessage(String to, String subject, String text) { + MailUtils.sendText(to, subject, text); + return R.ok(); + } + + /** + * 发送邮件(带附件) + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + * @param filePath 附件路径 + */ + @GetMapping("/sendMessageWithAttachment") + public R sendMessageWithAttachment(String to, String subject, String text, String filePath) { + MailUtils.sendText(to, subject, text, new File(filePath)); + return R.ok(); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisCacheController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisCacheController.java new file mode 100644 index 00000000..ba5caf45 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisCacheController.java @@ -0,0 +1,95 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.redis.utils.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; + +/** + * spring-cache 演示案例 + * + * @author Lion Li + */ +// 类级别 缓存统一配置 +//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE) +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/cache") +public class RedisCacheController { + + /** + * 测试 @Cacheable + *

+ * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来 + * 下一次调用该方法前,会去检查是否缓存中已经有值 + * 如果有就直接返回,不调用方法 + * 如果没有,就调用方法,然后把结果缓存起来 + * 这个注解「一般用在查询方法上」 + *

+ * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用 + * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null") + @GetMapping("/test1") + public R test1(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CachePut + *

+ * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用 + * 它「通常用在新增或者实时更新方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test2") + public R test2(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CacheEvict + *

+ * 使用了CacheEvict注解的方法,会清空指定缓存 + * 「一般用在删除的方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test3") + public R test3(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试设置过期时间 + * 手动设置过期时间10秒 + * 11秒后获取 判断是否相等 + */ + @GetMapping("/test6") + public R test6(String key, String value) { + RedisUtils.setCacheObject(key, value); + boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10)); + System.out.println("***********" + flag); + try { + Thread.sleep(11 * 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Object obj = RedisUtils.getCacheObject(key); + return R.ok(value.equals(obj)); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisLockController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisLockController.java new file mode 100644 index 00000000..2876aa01 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisLockController.java @@ -0,0 +1,71 @@ +package com.xmzs.demo.controller; + +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.baomidou.lock.annotation.Lock4j; +import com.baomidou.lock.executor.RedissonLockExecutor; +import com.xmzs.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalTime; + + +/** + * 测试分布式锁的样例 + * + * @author shenxinquan + */ +@Slf4j +@RestController +@RequestMapping("/demo/redisLock") +public class RedisLockController { + + @Autowired + private LockTemplate lockTemplate; + + /** + * 测试lock4j 注解 + */ + @Lock4j(keys = {"#key"}) + @GetMapping("/testLock4j") + public R testLock4j(String key, String value) { + System.out.println("start:" + key + ",time:" + LocalTime.now().toString()); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("end :" + key + ",time:" + LocalTime.now().toString()); + return R.ok("操作成功", value); + } + + /** + * 测试lock4j 工具 + */ + @GetMapping("/testLock4jLockTemplate") + public R testLock4jLockTemplate(String key, String value) { + final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class); + if (null == lockInfo) { + throw new RuntimeException("业务处理中,请稍后再试"); + } + // 获取锁成功,处理业务 + try { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + // + } + System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName()); + } finally { + //释放锁 + lockTemplate.releaseLock(lockInfo); + } + //结束 + return R.ok("操作成功", value); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisPubSubController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisPubSubController.java new file mode 100644 index 00000000..29e8801d --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisPubSubController.java @@ -0,0 +1,47 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.redis.utils.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Redis 发布订阅 演示案例 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/redis/pubsub") +public class RedisPubSubController { + + /** + * 发布消息 + * + * @param key 通道Key + * @param value 发送内容 + */ + @GetMapping("/pub") + public R pub(String key, String value) { + RedisUtils.publish(key, value, consumer -> { + System.out.println("发布通道 => " + key + ", 发送值 => " + value); + }); + return R.ok("操作成功"); + } + + /** + * 订阅消息 + * + * @param key 通道Key + */ + @GetMapping("/sub") + public R sub(String key) { + RedisUtils.subscribe(key, String.class, msg -> { + System.out.println("订阅通道 => " + key + ", 接收值 => " + msg); + }); + return R.ok("操作成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisRateLimiterController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisRateLimiterController.java new file mode 100644 index 00000000..d5dc3ecf --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/RedisRateLimiterController.java @@ -0,0 +1,64 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.ratelimiter.annotation.RateLimiter; +import com.xmzs.common.ratelimiter.enums.LimitType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 测试分布式限流样例 + * + * @author Lion Li + */ +@Slf4j +@RestController +@RequestMapping("/demo/rateLimiter") +public class RedisRateLimiterController { + + /** + * 测试全局限流 + * 全局影响 + */ + @RateLimiter(count = 2, time = 10) + @GetMapping("/test") + public R test(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试请求IP限流 + * 同一IP请求受影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP) + @GetMapping("/testip") + public R testip(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试集群实例限流 + * 启动两个后端服务互不影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER) + @GetMapping("/testcluster") + public R testcluster(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试请求IP限流(key基于参数获取) + * 同一IP请求受影响 + * + * 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0} + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value") + @GetMapping("/testObj") + public R testObj(String value) { + return R.ok("操作成功", value); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/SmsController.java new file mode 100644 index 00000000..311cb797 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/SmsController.java @@ -0,0 +1,76 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.sms.config.properties.SmsProperties; +import com.xmzs.common.sms.core.SmsTemplate; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * 短信演示案例 + * 请先阅读文档 否则无法使用 + * + * @author Lion Li + * @version 4.2.0 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/sms") +public class SmsController { + + private final SmsProperties smsProperties; +// private final SmsTemplate smsTemplate; // 可以使用spring注入 +// private final AliyunSmsTemplate smsTemplate; // 也可以注入某个厂家的模板工具 + + /** + * 发送短信Aliyun + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendAliyun") + public R sendAliyun(String phones, String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("aliyunSmsTemplate")) { + return R.fail("阿里云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); + map.put("code", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + + /** + * 发送短信Tencent + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendTencent") + public R sendTencent(String phones, String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("tencentSmsTemplate")) { + return R.fail("腾讯云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); +// map.put("2", "测试测试"); + map.put("1", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/Swagger3DemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/Swagger3DemoController.java new file mode 100644 index 00000000..560674b3 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/Swagger3DemoController.java @@ -0,0 +1,31 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * swagger3 用法示例 + * + * @author Lion Li + */ +@RestController +@RequestMapping("/swagger/demo") +public class Swagger3DemoController { + + /** + * 上传请求 + * 必须使用 @RequestPart 注解标注为文件 + * + * @param file 文件 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file) { + return R.ok("操作成功", file.getOriginalFilename()); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestBatchController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestBatchController.java new file mode 100644 index 00000000..bdf3733e --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestBatchController.java @@ -0,0 +1,90 @@ +package com.xmzs.demo.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.demo.domain.TestDemo; +import com.xmzs.demo.mapper.TestDemoMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 测试批量方法 + * + * @author Lion Li + * @date 2021-05-30 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/batch") +public class TestBatchController extends BaseController { + + /** + * 为了便于测试 直接引入mapper + */ + private final TestDemoMapper testDemoMapper; + + /** + * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大) + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/add") +// @DS("slave") + public R add() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + return toAjax(testDemoMapper.insertBatch(list)); + } + + /** + * 新增或更新 可完美替代 saveOrUpdateBatch 高性能 + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/addOrUpdate") +// @DS("slave") + public R addOrUpdate() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + testDemoMapper.insertBatch(list); + for (int i = 0; i < list.size(); i++) { + TestDemo testDemo = list.get(i); + testDemo.setTestKey("批量新增或修改"); + testDemo.setValue("批量新增或修改"); + if (i % 2 == 0) { + testDemo.setId(null); + } + } + return toAjax(testDemoMapper.insertOrUpdateBatch(list)); + } + + /** + * 删除批量方法 + */ + @DeleteMapping() +// @DS("slave") + public R remove() { + return toAjax(testDemoMapper.delete(new LambdaQueryWrapper() + .eq(TestDemo::getOrderNum, -1L))); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestDemoController.java new file mode 100644 index 00000000..50532dbd --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestDemoController.java @@ -0,0 +1,147 @@ +package com.xmzs.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.ValidatorUtils; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.core.validate.QueryGroup; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.excel.core.ExcelResult; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.demo.domain.TestDemo; +import com.xmzs.demo.domain.bo.TestDemoBo; +import com.xmzs.demo.domain.bo.TestDemoImportVo; +import com.xmzs.demo.domain.vo.TestDemoVo; +import com.xmzs.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 测试单表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/demo") +public class TestDemoController extends BaseController { + + private final ITestDemoService testDemoService; + + /** + * 查询测试单表列表 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return testDemoService.queryPageList(bo, pageQuery); + } + + /** + * 自定义分页查询 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/page") + public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return testDemoService.customPageList(bo, pageQuery); + } + + /** + * 导入数据 + * + * @param file 导入文件 + */ + @Log(title = "测试单表", businessType = BusinessType.IMPORT) + @SaCheckPermission("demo:demo:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file) throws Exception { + ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true); + List list = MapstructUtils.convert(excelResult.getList(), TestDemo.class); + testDemoService.saveBatch(list); + return R.ok(excelResult.getAnalysis()); + } + + /** + * 导出测试单表列表 + */ + @SaCheckPermission("demo:demo:export") + @Log(title = "测试单表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(@Validated TestDemoBo bo, HttpServletResponse response) { + List list = testDemoService.queryList(bo); + // 测试雪花id导出 +// for (TestDemoVo vo : list) { +// vo.setId(1234567891234567893L); +// } + ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response); + } + + /** + * 获取测试单表详细信息 + * + * @param id 测试ID + */ + @SaCheckPermission("demo:demo:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(testDemoService.queryById(id)); + } + + /** + * 新增测试单表 + */ + @SaCheckPermission("demo:demo:add") + @Log(title = "测试单表", businessType = BusinessType.INSERT) + @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}") + @PostMapping() + public R add(@RequestBody TestDemoBo bo) { + // 使用校验工具对标 @Validated(AddGroup.class) 注解 + // 用于在非 Controller 的地方校验对象 + ValidatorUtils.validate(bo, AddGroup.class); + return toAjax(testDemoService.insertByBo(bo)); + } + + /** + * 修改测试单表 + */ + @SaCheckPermission("demo:demo:edit") + @Log(title = "测试单表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) { + return toAjax(testDemoService.updateByBo(bo)); + } + + /** + * 删除测试单表 + * + * @param ids 测试ID串 + */ + @SaCheckPermission("demo:demo:remove") + @Log(title = "测试单表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestEncryptController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestEncryptController.java new file mode 100644 index 00000000..4f7fd868 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestEncryptController.java @@ -0,0 +1,55 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.demo.domain.TestDemoEncrypt; +import com.xmzs.demo.mapper.TestDemoEncryptMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + + +/** + * 测试数据库加解密功能 + * + * @author Lion Li + */ +@Validated +@RestController +@RequestMapping("/demo/encrypt") +public class TestEncryptController { + + @Autowired + private TestDemoEncryptMapper mapper; + @Value("${mybatis-encryptor.enable}") + private Boolean encryptEnable; + + /** + * 测试数据库加解密 + * + * @param key 测试key + * @param value 测试value + */ + @GetMapping() + public R> test(String key, String value) { + if (!encryptEnable) { + throw new RuntimeException("加密功能未开启!"); + } + Map map = new HashMap<>(2); + TestDemoEncrypt demo = new TestDemoEncrypt(); + demo.setTestKey(key); + demo.setValue(value); + mapper.insert(demo); + map.put("加密", demo); + TestDemoEncrypt testDemo = mapper.selectById(demo.getId()); + map.put("解密", testDemo); + return R.ok(map); + } + + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestExcelController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestExcelController.java new file mode 100644 index 00000000..fa18cf0b --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestExcelController.java @@ -0,0 +1,97 @@ +package com.xmzs.demo.controller; + +import cn.hutool.core.collection.CollUtil; +import com.xmzs.common.excel.utils.ExcelUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 测试Excel功能 + * + * @author Lion Li + */ +@RestController +@RequestMapping("/demo/excel") +public class TestExcelController { + + /** + * 单列表多数据 + */ + @GetMapping("/exportTemplateOne") + public void exportTemplateOne(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title", "单列表多数据"); + map.put("test1", "数据测试1"); + map.put("test2", "数据测试2"); + map.put("test3", "数据测试3"); + map.put("test4", "数据测试4"); + map.put("testTest", "666"); + List list = new ArrayList<>(); + list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4")); + list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8")); + list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12")); + ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response); + } + + /** + * 多列表多数据 + */ + @GetMapping("/exportTemplateMuliti") + public void exportTemplateMuliti(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title1", "标题1"); + map.put("title2", "标题2"); + map.put("title3", "标题3"); + map.put("title4", "标题4"); + map.put("author", "Lion Li"); + List list1 = new ArrayList<>(); + list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3")); + list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6")); + list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9")); + List list2 = new ArrayList<>(); + list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3")); + list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6")); + List list3 = new ArrayList<>(); + list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3")); + List list4 = new ArrayList<>(); + list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3")); + list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6")); + list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9")); + list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12")); + Map multiListMap = new HashMap<>(); + multiListMap.put("map", map); + multiListMap.put("data1", list1); + multiListMap.put("data2", list2); + multiListMap.put("data3", list3); + multiListMap.put("data4", list4); + ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); + } + + @Data + @AllArgsConstructor + static class TestObj1 { + private String test1; + private String test2; + private String test3; + } + + @Data + @AllArgsConstructor + static class TestObj { + private String name; + private String list1; + private String list2; + private String list3; + private String list4; + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestI18nController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestI18nController.java new file mode 100644 index 00000000..3df8c1f5 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestI18nController.java @@ -0,0 +1,71 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.MessageUtils; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + + +/** + * 测试国际化 + * + * @author Lion Li + */ +@Validated +@RestController +@RequestMapping("/demo/i18n") +public class TestI18nController { + + /** + * 通过code获取国际化内容 + * code为 messages.properties 中的 key + *

+ * 测试使用 user.register.success + * + * @param code 国际化code + */ + @GetMapping() + public R get(String code) { + return R.ok(MessageUtils.message(code)); + } + + /** + * Validator 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test1") + public R test1(@NotBlank(message = "{not.null}") String str) { + return R.ok(str); + } + + /** + * Bean 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test2") + public R test2(@Validated TestI18nBo bo) { + return R.ok(bo); + } + + @Data + public static class TestI18nBo { + + @NotBlank(message = "{not.null}") + private String name; + + @NotNull(message = "{not.null}") + @Range(min = 0, max = 100, message = "{length.not.valid}") + private Integer age; + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestSensitiveController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestSensitiveController.java new file mode 100644 index 00000000..5385030c --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestSensitiveController.java @@ -0,0 +1,76 @@ +package com.xmzs.demo.controller; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.sensitive.annotation.Sensitive; +import com.xmzs.common.sensitive.core.SensitiveStrategy; +import lombok.Data; +import com.xmzs.common.sensitive.core.SensitiveService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 测试数据脱敏控制器 + *

+ * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + * @see SensitiveService + */ +@RestController +@RequestMapping("/demo/sensitive") +public class TestSensitiveController extends BaseController { + + /** + * 测试数据脱敏 + */ + @GetMapping("/test") + public R test() { + TestSensitive testSensitive = new TestSensitive(); + testSensitive.setIdCard("210397198608215431"); + testSensitive.setPhone("17640125371"); + testSensitive.setAddress("北京市朝阳区某某四合院1203室"); + testSensitive.setEmail("17640125371@163.com"); + testSensitive.setBankCard("6226456952351452853"); + return R.ok(testSensitive); + } + + @Data + static class TestSensitive { + + /** + * 身份证 + */ + @Sensitive(strategy = SensitiveStrategy.ID_CARD) + private String idCard; + + /** + * 电话 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phone; + + /** + * 地址 + */ + @Sensitive(strategy = SensitiveStrategy.ADDRESS) + private String address; + + /** + * 邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + private String email; + + /** + * 银行卡 + */ + @Sensitive(strategy = SensitiveStrategy.BANK_CARD) + private String bankCard; + + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestTreeController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestTreeController.java new file mode 100644 index 00000000..8eaa420e --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/TestTreeController.java @@ -0,0 +1,107 @@ +package com.xmzs.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.core.validate.QueryGroup; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.demo.domain.bo.TestTreeBo; +import com.xmzs.demo.domain.vo.TestTreeVo; +import com.xmzs.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +/** + * 测试树表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/tree") +public class TestTreeController extends BaseController { + + private final ITestTreeService testTreeService; + + /** + * 查询测试树表列表 + */ + @SaCheckPermission("demo:tree:list") + @GetMapping("/list") + public R> list(@Validated(QueryGroup.class) TestTreeBo bo) { + List list = testTreeService.queryList(bo); + return R.ok(list); + } + + /** + * 导出测试树表列表 + */ + @SaCheckPermission("demo:tree:export") + @Log(title = "测试树表", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(@Validated TestTreeBo bo, HttpServletResponse response) { + List list = testTreeService.queryList(bo); + ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response); + } + + /** + * 获取测试树表详细信息 + * + * @param id 测试树ID + */ + @SaCheckPermission("demo:tree:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(testTreeService.queryById(id)); + } + + /** + * 新增测试树表 + */ + @SaCheckPermission("demo:tree:add") + @Log(title = "测试树表", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(testTreeService.insertByBo(bo)); + } + + /** + * 修改测试树表 + */ + @SaCheckPermission("demo:tree:edit") + @Log(title = "测试树表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(testTreeService.updateByBo(bo)); + } + + /** + * 删除测试树表 + * + * @param ids 测试树ID串 + */ + @SaCheckPermission("demo:tree:remove") + @Log(title = "测试树表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/BoundedQueueController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/BoundedQueueController.java new file mode 100644 index 00000000..204e61e3 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/BoundedQueueController.java @@ -0,0 +1,90 @@ +package com.xmzs.demo.controller.queue; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.redis.utils.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 有界队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/bounded") +public class BoundedQueueController { + + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param capacity 容量 + */ + @GetMapping("/add") + public R add(String queueName, int capacity) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + // 初始化设置一次即可 + if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) { + log.info("通道: {} , 设置容量: {}", queueName, capacity); + } else { + log.info("通道: {} , 设置容量失败", queueName); + return R.fail("操作失败"); + } + for (int i = 0; i < 11; i++) { + String data = "data-" + i; + boolean flag = QueueUtils.addBoundedQueueObject(queueName, data); + if (flag == false) { + log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/remove") + public R remove(String queueName) { + String data = "data-" + 5; + if (QueueUtils.removeQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + String data; + do { + data = QueueUtils.getQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/DelayedQueueController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/DelayedQueueController.java new file mode 100644 index 00000000..cdfc9c6a --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/DelayedQueueController.java @@ -0,0 +1,90 @@ +package com.xmzs.demo.controller.queue; + +import com.xmzs.common.core.domain.R; +import com.xmzs.common.redis.utils.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.TimeUnit; + +/** + * 延迟队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + * 例如: 创建订单30分钟后过期处理 + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 两台集群分别开启订阅 在其中一台发送数据 观察接收消息的规律 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/delayed") +public class DelayedQueueController { + + /** + * 订阅队列 + * + * @param queueName 队列名 + */ + @GetMapping("/subscribe") + public R subscribe(String queueName) { + log.info("通道: {} 监听中......", queueName); + // 项目初始化设置一次即可 + QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { + // 观察接收时间 + log.info("通道: {}, 收到数据: {}", queueName, orderNum); + }); + return R.ok("操作成功"); + } + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + * @param time 延迟时间(秒) + */ + @GetMapping("/add") + public R add(String queueName, String orderNum, Long time) { + QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS); + // 观察发送时间 + log.info("通道: {} , 发送数据: {}", queueName, orderNum); + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + */ + @GetMapping("/remove") + public R remove(String queueName, String orderNum) { + if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) { + log.info("通道: {} , 删除数据: {}", queueName, orderNum); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 销毁队列 + * + * @param queueName 队列名 + */ + @GetMapping("/destroy") + public R destroy(String queueName) { + // 用完了一定要销毁 否则会一直存在 + QueueUtils.destroyDelayedQueue(queueName); + return R.ok("操作成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityDemo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityDemo.java new file mode 100644 index 00000000..7ba2125b --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityDemo.java @@ -0,0 +1,22 @@ +package com.xmzs.demo.controller.queue; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 实体类 注意不允许使用内部类 否则会找不到类 + * + * @author Lion Li + * @version 3.6.0 + */ +@Data +@NoArgsConstructor +public class PriorityDemo implements Comparable { + private String name; + private Integer orderNum; + + @Override + public int compareTo(PriorityDemo other) { + return Integer.compare(getOrderNum(), other.getOrderNum()); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityQueueController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityQueueController.java new file mode 100644 index 00000000..ba68f240 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/controller/queue/PriorityQueueController.java @@ -0,0 +1,89 @@ +package com.xmzs.demo.controller.queue; + +import cn.hutool.core.util.RandomUtil; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.redis.utils.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 优先队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个消息只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/priority") +public class PriorityQueueController { + + /** + * 添加队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/add") + public R add(String queueName) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + + for (int i = 0; i < 10; i++) { + int randomNum = RandomUtil.randomInt(10); + PriorityDemo data = new PriorityDemo(); + data.setName("data-" + i); + data.setOrderNum(randomNum); + if (QueueUtils.addPriorityQueueObject(queueName, data)) { + log.info("通道: {} , 发送数据: {}", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param name 对象名 + * @param orderNum 排序号 + */ + @GetMapping("/remove") + public R remove(String queueName, String name, Integer orderNum) { + PriorityDemo data = new PriorityDemo(); + data.setName(name); + data.setOrderNum(orderNum); + if (QueueUtils.removeQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + PriorityDemo data; + do { + data = QueueUtils.getQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemo.java new file mode 100644 index 00000000..2774b5e3 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemo.java @@ -0,0 +1,68 @@ +package com.xmzs.demo.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 测试单表对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_demo") +public class TestDemo extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 排序号 + */ + @OrderBy(asc = false, sort = 1) + private Integer orderNum; + + /** + * key键 + */ + private String testKey; + + /** + * 值 + */ + private String value; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemoEncrypt.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemoEncrypt.java new file mode 100644 index 00000000..b8cb213f --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestDemoEncrypt.java @@ -0,0 +1,29 @@ +package com.xmzs.demo.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.encrypt.annotation.EncryptField; +import com.xmzs.common.encrypt.enumd.AlgorithmType; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_demo") +public class TestDemoEncrypt extends TestDemo { + + /** + * key键 + */ + // @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==") + @EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB") + private String testKey; + + /** + * 值 + */ + // @EncryptField // 什么也不写走默认yml配置 + // @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5") + @EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5") + private String value; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestTree.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestTree.java new file mode 100644 index 00000000..d8bb0c09 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/TestTree.java @@ -0,0 +1,65 @@ +package com.xmzs.demo.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.Version; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 测试树表对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_tree") +public class TestTree extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 树节点名 + */ + private String treeName; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoBo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoBo.java new file mode 100644 index 00000000..89df0b18 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoBo.java @@ -0,0 +1,62 @@ +package com.xmzs.demo.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.demo.domain.TestDemo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestDemo.class, reverseConvertGenerate = false) +public class TestDemoBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Integer orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class}) + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class}) + private String value; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoImportVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoImportVo.java new file mode 100644 index 00000000..077af6fc --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestDemoImportVo.java @@ -0,0 +1,53 @@ +package com.xmzs.demo.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +public class TestDemoImportVo { + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空") + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空") + @ExcelProperty(value = "排序号") + private Long orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空") + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空") + @ExcelProperty(value = "值") + private String value; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestTreeBo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestTreeBo.java new file mode 100644 index 00000000..baf5d6d7 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/bo/TestTreeBo.java @@ -0,0 +1,54 @@ +package com.xmzs.demo.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.demo.domain.TestTree; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 测试树表业务对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestTree.class, reverseConvertGenerate = false) +public class TestTreeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 树节点名 + */ + @NotBlank(message = "树节点名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String treeName; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/package-info.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/package-info.java new file mode 100644 index 00000000..3ceb1f96 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/package-info.java @@ -0,0 +1 @@ +package com.xmzs.demo.domain; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestDemoVo.java new file mode 100644 index 00000000..ff340faa --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestDemoVo.java @@ -0,0 +1,104 @@ +package com.xmzs.demo.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.demo.domain.TestDemo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 测试单表视图对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestDemo.class) +public class TestDemoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Integer orderNum; + + /** + * key键 + */ + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @ExcelProperty(value = "值") + private String value; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 创建人 + */ + @ExcelProperty(value = "创建人") + private Long createBy; + + /** + * 创建人账号 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + @ExcelProperty(value = "创建人账号") + private String createByName; + + /** + * 更新时间 + */ + @ExcelProperty(value = "更新时间") + private Date updateTime; + + /** + * 更新人 + */ + @ExcelProperty(value = "更新人") + private Long updateBy; + + /** + * 更新人账号 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy") + @ExcelProperty(value = "更新人账号") + private String updateByName; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestTreeVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestTreeVo.java new file mode 100644 index 00000000..3f1cf8a9 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/domain/vo/TestTreeVo.java @@ -0,0 +1,64 @@ +package com.xmzs.demo.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.demo.domain.TestTree; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 测试树表视图对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestTree.class) +public class TestTreeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 父id + */ + @ExcelProperty(value = "父id") + private Long parentId; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 树节点名 + */ + @ExcelProperty(value = "树节点名") + private String treeName; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoEncryptMapper.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoEncryptMapper.java new file mode 100644 index 00000000..da018456 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoEncryptMapper.java @@ -0,0 +1,13 @@ +package com.xmzs.demo.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.demo.domain.TestDemoEncrypt; + +/** + * 测试加密功能 + * + * @author Lion Li + */ +public interface TestDemoEncryptMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoMapper.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoMapper.java new file mode 100644 index 00000000..50cd21b8 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestDemoMapper.java @@ -0,0 +1,58 @@ +package com.xmzs.demo.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.demo.domain.TestDemo; +import com.xmzs.demo.domain.vo.TestDemoVo; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface TestDemoMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + Page customPageList(@Param("page") Page page, @Param("ew") Wrapper wrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) +

> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int updateById(@Param(Constants.ENTITY) TestDemo entity); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int deleteBatchIds(@Param(Constants.COLL) Collection idList); +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestTreeMapper.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestTreeMapper.java new file mode 100644 index 00000000..3c5f7fb7 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/TestTreeMapper.java @@ -0,0 +1,21 @@ +package com.xmzs.demo.mapper; + +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.demo.domain.TestTree; +import com.xmzs.demo.domain.vo.TestTreeVo; + +/** + * 测试树表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +@DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") +}) +public interface TestTreeMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/package-info.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/package-info.java new file mode 100644 index 00000000..0f77326f --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/mapper/package-info.java @@ -0,0 +1 @@ +package com.xmzs.demo.mapper; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestDemoService.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestDemoService.java new file mode 100644 index 00000000..21fcf50d --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestDemoService.java @@ -0,0 +1,71 @@ +package com.xmzs.demo.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.demo.domain.TestDemo; +import com.xmzs.demo.domain.bo.TestDemoBo; +import com.xmzs.demo.domain.vo.TestDemoVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestDemoService { + + /** + * 查询单个 + * + * @return + */ + TestDemoVo queryById(Long id); + + /** + * 查询列表 + */ + TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 自定义分页查询 + */ + TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 查询列表 + */ + List queryList(TestDemoBo bo); + + /** + * 根据新增业务对象插入测试单表 + * + * @param bo 测试单表新增业务对象 + * @return + */ + Boolean insertByBo(TestDemoBo bo); + + /** + * 根据编辑业务对象修改测试单表 + * + * @param bo 测试单表编辑业务对象 + * @return + */ + Boolean updateByBo(TestDemoBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 批量保存 + */ + Boolean saveBatch(List list); +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestTreeService.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestTreeService.java new file mode 100644 index 00000000..fe51e7f2 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/ITestTreeService.java @@ -0,0 +1,52 @@ +package com.xmzs.demo.service; + +import com.xmzs.demo.domain.bo.TestTreeBo; +import com.xmzs.demo.domain.vo.TestTreeVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试树表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestTreeService { + /** + * 查询单个 + * + * @return + */ + TestTreeVo queryById(Long id); + + /** + * 查询列表 + */ + List queryList(TestTreeBo bo); + + /** + * 根据新增业务对象插入测试树表 + * + * @param bo 测试树表新增业务对象 + * @return + */ + Boolean insertByBo(TestTreeBo bo); + + /** + * 根据编辑业务对象修改测试树表 + * + * @param bo 测试树表编辑业务对象 + * @return + */ + Boolean updateByBo(TestTreeBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestDemoServiceImpl.java new file mode 100644 index 00000000..3782d4cc --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestDemoServiceImpl.java @@ -0,0 +1,110 @@ +package com.xmzs.demo.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.demo.domain.TestDemo; +import com.xmzs.demo.domain.bo.TestDemoBo; +import com.xmzs.demo.domain.vo.TestDemoVo; +import com.xmzs.demo.mapper.TestDemoMapper; +import com.xmzs.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试单表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +@RequiredArgsConstructor +@Service +public class TestDemoServiceImpl implements ITestDemoService { + + private final TestDemoMapper baseMapper; + + @Override + public TestDemoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + public TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 自定义分页查询 + */ + @Override + public TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.customPageList(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(TestDemoBo bo) { + return baseMapper.selectVoList(buildQueryWrapper(bo)); + } + + private LambdaQueryWrapper buildQueryWrapper(TestDemoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey()); + lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + @Override + public Boolean insertByBo(TestDemoBo bo) { + TestDemo add = MapstructUtils.convert(bo, TestDemo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestDemoBo bo) { + TestDemo update = MapstructUtils.convert(bo, TestDemo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestDemo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + public Boolean saveBatch(List list) { + return baseMapper.insertBatch(list); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestTreeServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestTreeServiceImpl.java new file mode 100644 index 00000000..ecca17fd --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/TestTreeServiceImpl.java @@ -0,0 +1,87 @@ +package com.xmzs.demo.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.demo.domain.TestTree; +import com.xmzs.demo.domain.bo.TestTreeBo; +import com.xmzs.demo.domain.vo.TestTreeVo; +import com.xmzs.demo.mapper.TestTreeMapper; +import com.xmzs.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试树表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +// @DS("slave") // 切换从库查询 +@RequiredArgsConstructor +@Service +public class TestTreeServiceImpl implements ITestTreeService { + + private final TestTreeMapper baseMapper; + + @Override + public TestTreeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + // @DS("slave") // 切换从库查询 + @Override + public List queryList(TestTreeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(TestTreeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + @Override + public Boolean insertByBo(TestTreeBo bo) { + TestTree add = MapstructUtils.convert(bo, TestTree.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestTreeBo bo) { + TestTree update = MapstructUtils.convert(bo, TestTree.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestTree entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/package-info.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/package-info.java new file mode 100644 index 00000000..f73f3f3e --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/impl/package-info.java @@ -0,0 +1 @@ +package com.xmzs.demo.service.impl; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/package-info.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/package-info.java new file mode 100644 index 00000000..badcad43 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/xmzs/demo/service/package-info.java @@ -0,0 +1 @@ +package com.xmzs.demo.service; diff --git a/ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx b/ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx new file mode 100644 index 00000000..0f7347d6 Binary files /dev/null and b/ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx differ diff --git a/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx b/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx new file mode 100644 index 00000000..c7d11dcc Binary files /dev/null and b/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx differ diff --git a/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml new file mode 100644 index 00000000..8200a7f7 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml new file mode 100644 index 00000000..2768fcf5 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md new file mode 100644 index 00000000..c938b1e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-generator/pom.xml b/ruoyi-modules/ruoyi-generator/pom.xml new file mode 100644 index 00000000..5d7f0410 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/pom.xml @@ -0,0 +1,53 @@ + + + + com.xmzs + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-generator + + + generator 代码生成 + + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-doc + + + + com.xmzs + ruoyi-common-mybatis + + + + com.xmzs + ruoyi-common-web + + + + com.xmzs + ruoyi-common-log + + + + + org.apache.velocity + velocity-engine-core + + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/config/GenConfig.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/config/GenConfig.java new file mode 100644 index 00000000..0f9a98b0 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.xmzs.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8") +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; + } + + @Value("${author}") + public void setAuthor(String author) { + GenConfig.author = author; + } + + public static String getPackageName() { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/constant/GenConstants.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/constant/GenConstants.java new file mode 100644 index 00000000..3be7f780 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/constant/GenConstants.java @@ -0,0 +1,186 @@ +package com.xmzs.generator.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public interface GenConstants { + /** + * 单表(增删改查) + */ + String TPL_CRUD = "crud"; + + /** + * 树表(增删改查) + */ + String TPL_TREE = "tree"; + + /** + * 树编码字段 + */ + String TREE_CODE = "treeCode"; + + /** + * 树父编码字段 + */ + String TREE_PARENT_CODE = "treeParentCode"; + + /** + * 树名称字段 + */ + String TREE_NAME = "treeName"; + + /** + * 上级菜单ID字段 + */ + String PARENT_MENU_ID = "parentMenuId"; + + /** + * 上级菜单名称字段 + */ + String PARENT_MENU_NAME = "parentMenuName"; + + /** + * 数据库字符串类型 + */ + String[] COLUMNTYPE_STR = {"char", "varchar", "enum", "set", "nchar", "nvarchar", "varchar2", "nvarchar2"}; + + /** + * 数据库文本类型 + */ + String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext", "binary", "varbinary", "blob", + "ntext", "image", "bytea"}; + + /** + * 数据库时间类型 + */ + String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp", "year", "interval", + "smalldatetime", "datetime2", "datetimeoffset"}; + + /** + * 数据库数字类型 + */ + String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal", "numeric", "real", "double precision", + "smallserial", "serial", "bigserial", "money", "smallmoney"}; + + /** + * BO对象 不需要添加字段 + */ + String[] COLUMNNAME_NOT_ADD = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * BO对象 不需要编辑字段 + */ + String[] COLUMNNAME_NOT_EDIT = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * VO对象 不需要返回字段 + */ + String[] COLUMNNAME_NOT_LIST = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * BO对象 不需要查询字段 + */ + String[] COLUMNNAME_NOT_QUERY = {"id", "create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark", "version", "tenant_id"}; + + /** + * Entity基类字段 + */ + String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime", "tenantId"}; + + /** + * 文本框 + */ + String HTML_INPUT = "input"; + + /** + * 文本域 + */ + String HTML_TEXTAREA = "textarea"; + + /** + * 下拉框 + */ + String HTML_SELECT = "select"; + + /** + * 单选框 + */ + String HTML_RADIO = "radio"; + + /** + * 复选框 + */ + String HTML_CHECKBOX = "checkbox"; + + /** + * 日期控件 + */ + String HTML_DATETIME = "datetime"; + + /** + * 图片上传控件 + */ + String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** + * 文件上传控件 + */ + String HTML_FILE_UPLOAD = "fileUpload"; + + /** + * 富文本控件 + */ + String HTML_EDITOR = "editor"; + + /** + * 字符串类型 + */ + String TYPE_STRING = "String"; + + /** + * 整型 + */ + String TYPE_INTEGER = "Integer"; + + /** + * 长整型 + */ + String TYPE_LONG = "Long"; + + /** + * 浮点型 + */ + String TYPE_DOUBLE = "Double"; + + /** + * 高精度计算类型 + */ + String TYPE_BIGDECIMAL = "BigDecimal"; + + /** + * 时间类型 + */ + String TYPE_DATE = "Date"; + + /** + * 模糊查询 + */ + String QUERY_LIKE = "LIKE"; + + /** + * 相等查询 + */ + String QUERY_EQ = "EQ"; + + /** + * 需要 + */ + String REQUIRE = "1"; +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/controller/GenController.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/controller/GenController.java new file mode 100644 index 00000000..0a017f73 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/controller/GenController.java @@ -0,0 +1,207 @@ +package com.xmzs.generator.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.generator.domain.GenTable; +import com.xmzs.generator.domain.GenTableColumn; +import com.xmzs.generator.service.IGenTableService; +import lombok.RequiredArgsConstructor; +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 Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController { + + private final IGenTableService genTableService; + + /** + * 查询代码生成列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageGenTableList(genTable, pageQuery); + } + + /** + * 修改代码生成业务 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:query") + @GetMapping(value = "/{tableId}") + public R> getInfo(@PathVariable Long tableId) { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return R.ok(map); + } + + /** + * 查询数据库列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageDbTableList(genTable, pageQuery); + } + + /** + * 查询数据表字段列表 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:list") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) { + TableDataInfo dataInfo = new TableDataInfo<>(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + * + * @param tables 表名串 + */ + @SaCheckPermission("tool:gen:import") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public R importTableSave(String tables) { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return R.ok(); + } + + /** + * 修改保存代码生成业务 + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public R editSave(@Validated @RequestBody GenTable genTable) { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return R.ok(); + } + + /** + * 删除代码生成 + * + * @param tableIds 表ID串 + */ + @SaCheckPermission("tool:gen:remove") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public R remove(@PathVariable Long[] tableIds) { + genTableService.deleteGenTableByIds(tableIds); + return R.ok(); + } + + /** + * 预览代码 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:preview") + @GetMapping("/preview/{tableId}") + public R> preview(@PathVariable("tableId") Long tableId) throws IOException { + Map dataMap = genTableService.previewCode(tableId); + return R.ok(dataMap); + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名 + */ + @SaCheckPermission("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); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名 + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public R genCode(@PathVariable("tableName") String tableName) { + genTableService.generatorCode(tableName); + return R.ok(); + } + + /** + * 同步数据库 + * + * @param tableName 表名 + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public R synchDb(@PathVariable("tableName") String tableName) { + genTableService.synchDb(tableName); + return R.ok(); + } + + /** + * 批量生成代码 + * + * @param tables 表名串 + */ + @SaCheckPermission("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.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IoUtil.write(response.getOutputStream(), false, data); + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTable.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTable.java new file mode 100644 index 00000000..59ab3899 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTable.java @@ -0,0 +1,190 @@ +package com.xmzs.generator.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.generator.constant.GenConstants; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 业务表 gen_table + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table") +public class GenTable extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "table_id") + 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; + + /** + * 生成路径(不填默认项目路径) + */ + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) + private String genPath; + + /** + * 主键信息 + */ + @TableField(exist = false) + private GenTableColumn pkColumn; + + /** + * 表列信息 + */ + @Valid + @TableField(exist = false) + private List columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 备注 + */ + private String remark; + + /** + * 树编码字段 + */ + @TableField(exist = false) + private String treeCode; + + /** + * 树父编码字段 + */ + @TableField(exist = false) + private String treeParentCode; + + /** + * 树名称字段 + */ + @TableField(exist = false) + private String treeName; + + /* + * 菜单id列表 + */ + @TableField(exist = false) + private List menuIds; + + /** + * 上级菜单ID字段 + */ + @TableField(exist = false) + private String parentMenuId; + + /** + * 上级菜单名称字段 + */ + @TableField(exist = false) + private String parentMenuName; + + public boolean isTree() { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTableColumn.java new file mode 100644 index 00000000..d675af7e --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/domain/GenTableColumn.java @@ -0,0 +1,223 @@ +package com.xmzs.generator.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.ibatis.type.JdbcType; + +import jakarta.validation.constraints.NotBlank; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table_column") +public class GenTableColumn extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "column_id") + private Long columnId; + + /** + * 归属表编号 + */ + private Long tableId; + + /** + * 列名称 + */ + private String columnName; + + /** + * 列描述 + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String columnComment; + + /** + * 列类型 + */ + private String columnType; + + /** + * JAVA类型 + */ + private String javaType; + + /** + * JAVA字段名 + */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** + * 是否主键(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isPk; + + /** + * 是否自增(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isIncrement; + + /** + * 是否必填(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isRequired; + + /** + * 是否为插入字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isInsert; + + /** + * 是否编辑字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isEdit; + + /** + * 是否列表字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isList; + + /** + * 是否查询字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + 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 String getCapJavaField() { + return StringUtils.capitalize(javaField); + } + + public boolean isPk() { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) { + return isPk != null && StringUtils.equals("1", isPk); + } + + public boolean isIncrement() { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public boolean isRequired() { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public boolean isInsert() { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public boolean isEdit() { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public boolean isList() { + return isList(this.isList); + } + + public boolean isList(String isList) { + return isList != null && StringUtils.equals("1", isList); + } + + public boolean isQuery() { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public boolean isSuperColumn() { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", + // TreeEntity + "parentName", "parentId"); + } + + public boolean isUsableColumn() { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + 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(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableColumnMapper.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 00000000..286fc182 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,24 @@ +package com.xmzs.generator.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.generator.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface GenTableColumnMapper extends BaseMapperPlus { + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + List selectDbTableColumnsByName(String tableName); + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableMapper.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableMapper.java new file mode 100644 index 00000000..e43567c6 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/mapper/GenTableMapper.java @@ -0,0 +1,58 @@ +package com.xmzs.generator.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.generator.domain.GenTable; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface GenTableMapper extends BaseMapperPlus { + + /** + * 查询据库列表 + * + * @param genTable 查询条件 + * @return 数据库表集合 + */ + Page selectPageDbTableList(@Param("page") Page page, @Param("genTable") 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); + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/GenTableServiceImpl.java new file mode 100644 index 00000000..3436b7f1 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/GenTableServiceImpl.java @@ -0,0 +1,459 @@ +package com.xmzs.generator.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.generator.constant.GenConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.file.FileUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.generator.domain.GenTable; +import com.xmzs.generator.domain.GenTableColumn; +import com.xmzs.generator.mapper.GenTableColumnMapper; +import com.xmzs.generator.mapper.GenTableMapper; +import com.xmzs.generator.util.GenUtils; +import com.xmzs.generator.util.VelocityInitializer; +import com.xmzs.generator.util.VelocityUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +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.nio.charset.StandardCharsets; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author Lion Li + */ +@DS("#header.datasource") +@Slf4j +@RequiredArgsConstructor +@Service +public class GenTableServiceImpl implements IGenTableService { + + private final GenTableMapper baseMapper; + private final GenTableColumnMapper genTableColumnMapper; + private final IdentifierGenerator identifierGenerator; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) { + return genTableColumnMapper.selectList(new LambdaQueryWrapper() + .eq(GenTableColumn::getTableId, tableId) + .orderByAsc(GenTableColumn::getSort)); + } + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) { + GenTable genTable = baseMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + @Override + public TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery) { + Page page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); + return TableDataInfo.build(page); + } + + private QueryWrapper buildGenTableQueryWrapper(GenTable genTable) { + Map params = genTable.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName())) + .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment())) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "create_time", params.get("beginTime"), params.get("endTime")); + return wrapper; + } + + + @Override + public TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery) { + Page page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable); + return TableDataInfo.build(page); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) { + return baseMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() { + return baseMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void updateGenTable(GenTable genTable) { + String options = JsonUtils.toJsonString(genTable.getParams()); + genTable.setOptions(options); + int row = baseMapper.updateById(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.updateById(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteGenTableByIds(Long[] tableIds) { + List ids = Arrays.asList(tableIds); + baseMapper.deleteBatchIds(ids); + genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void importGenTable(List tableList) { + String operName = LoginHelper.getUsername(); + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = baseMapper.insert(table); + if (row > 0) { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + List saveColumns = new ArrayList<>(); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + saveColumns.add(column); + } + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertBatch(saveColumns); + } + } + } + } catch (Exception e) { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主键列信息 + 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); + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + // 设置主键列信息 + 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.ts.vm", "types.ts.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.writeUtf8String(sw.toString(), path); + } catch (Exception e) { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void synchDb(String tableName) { + GenTable table = baseMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (CollUtil.isEmpty(dbTableColumns)) { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName); + + List saveColumns = new ArrayList<>(); + 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()); + } + } + saveColumns.add(column); + }); + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertOrUpdateBatch(saveColumns); + } + List delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName())); + if (CollUtil.isNotEmpty(delColumns)) { + List ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); + genTableColumnMapper.deleteBatchIds(ids); + } + } + + /** + * 批量生成代码(下载方式) + * + * @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); + } + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主键列信息 + 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))); + IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString()); + IoUtil.close(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 = JsonUtils.toJsonString(genTable.getParams()); + Dict paramsObj = JsonUtils.parseMap(options); + if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) { + throw new ServiceException("树编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) { + throw new ServiceException("树父编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) { + throw new ServiceException("树名称字段不能为空"); + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (ObjectUtil.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) { + Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); + if (ObjectUtil.isNotNull(paramsObj)) { + String treeCode = paramsObj.getStr(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @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); + } +} + diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/IGenTableService.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/IGenTableService.java new file mode 100644 index 00000000..0df97e72 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/service/IGenTableService.java @@ -0,0 +1,133 @@ +package com.xmzs.generator.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.generator.domain.GenTable; +import com.xmzs.generator.domain.GenTableColumn; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author Lion Li + */ +public interface IGenTableService { + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId(Long tableId); + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @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/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/GenUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/GenUtils.java new file mode 100644 index 00000000..78d8265c --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/GenUtils.java @@ -0,0 +1,233 @@ +package com.xmzs.generator.util; + +import com.xmzs.generator.constant.GenConstants; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.generator.config.GenConfig; +import com.xmzs.generator.domain.GenTable; +import com.xmzs.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.RegExUtils; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +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(LoginHelper.getUserId()); + } + + /** + * 初始化列属性字段 + */ + 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(), "(", ")"), StringUtils.SEPARATOR); + 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); + } + } + + // BO对象 默认插入勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) { + column.setIsInsert(GenConstants.REQUIRE); + } + // BO对象 默认编辑勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { + column.setIsEdit(GenConstants.REQUIRE); + } + // BO对象 默认是否必填勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { + column.setIsRequired(GenConstants.REQUIRE); + } + // VO对象 默认返回勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) { + column.setIsList(GenConstants.REQUIRE); + } + // BO对象 默认查询勾选 + 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 firstIndex = tableName.indexOf("_"); + int nameLength = tableName.length(); + String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength); + businessName = StringUtils.toCamelCase(businessName); + return businessName; + } + + /** + * 表名转换成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, StringUtils.SEPARATOR); + 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/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityInitializer.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityInitializer.java new file mode 100644 index 00000000..8485ff3f --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityInitializer.java @@ -0,0 +1,35 @@ +package com.xmzs.generator.util; + +import com.xmzs.common.core.constant.Constants; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.app.Velocity; + +import java.util.Properties; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +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/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityUtils.java new file mode 100644 index 00000000..3e34edea --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/xmzs/generator/util/VelocityUtils.java @@ -0,0 +1,338 @@ +package com.xmzs.generator.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import com.xmzs.generator.constant.GenConstants; +import com.xmzs.common.core.utils.DateUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.mybatis.helper.DataBaseHelper; +import com.xmzs.generator.domain.GenTable; +import com.xmzs.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.VelocityContext; + +import java.util.*; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +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); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(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.get(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); + } + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) { + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/bo.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"); + if (DataBaseHelper.isOracle()) { + templates.add("vm/sql/oracle/sql.vm"); + } else if (DataBaseHelper.isPostgerSql()) { + templates.add("vm/sql/postgres/sql.vm"); + } else if (DataBaseHelper.isSqlServer()) { + templates.add("vm/sql/sqlserver/sql.vm"); + } else { + templates.add("vm/sql/sql.vm"); + } + templates.add("vm/ts/api.ts.vm"); + templates.add("vm/ts/types.ts.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"); + } + 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("vo.java.vm")) { + fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); + } + if (template.contains("bo.java.vm")) { + fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); + } + 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.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/index.ts", vuePath, moduleName, businessName); + } else if (template.contains("types.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/types.ts", 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(); + HashSet importList = new HashSet(); + 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); + 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(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) { + return paramsObj.getStr(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(Map paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { + return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeName = paramsObj.getStr(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/ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml new file mode 100644 index 00000000..5dc7a190 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: Lion Li + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.xmzs.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 00000000..5749189f --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 00000000..6684c9e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md new file mode 100644 index 00000000..c938b1e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm new file mode 100644 index 00000000..a838fffd --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm @@ -0,0 +1,48 @@ +package ${packageName}.domain.bo; + +import ${packageName}.domain.${ClassName}; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +#foreach ($import in $importList) +import ${import}; +#end + +/** + * ${functionName}业务对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ${ClassName}.class, reverseConvertGenerate = false) +public class ${ClassName}Bo extends BaseEntity { + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.insert || $column.edit)) + /** + * $column.columnComment + */ +#if($column.insert && $column.edit) +#set($Group="AddGroup.class, EditGroup.class") +#elseif($column.insert) +#set($Group="AddGroup.class") +#elseif($column.edit) +#set($Group="EditGroup.class") +#end +#if($column.required) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空", groups = { $Group }) +#else + @NotNull(message = "$column.columnComment不能为空", groups = { $Group }) +#end +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 00000000..48eb6b80 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import annotation.idempotent.common.com.xmzs.RepeatSubmit; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.log.enums.BusinessType; +import utils.excel.common.com.xmzs.ExcelUtil; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.service.I${ClassName}Service; +#if($table.crud || $table.sub) +import com.xmzs.common.mybatis.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName} + * + * @author ${author} + * @date ${datetime} + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + + private final I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { + return ${className}Service.queryPageList(bo, pageQuery); + } +#elseif($table.tree) + public R> list(${ClassName}Bo bo) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + return R.ok(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:export") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(${ClassName}Bo bo, HttpServletResponse response) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); + } + + /** + * 获取${functionName}详细信息 + * + * @param ${pkColumn.javaField} 主键 + */ + @SaCheckPermission("${permissionPrefix}:query") + @GetMapping("/{${pkColumn.javaField}}") + public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空") + @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { + return R.ok(${className}Service.queryById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @SaCheckPermission("${permissionPrefix}:add") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.insertByBo(bo)); + } + + /** + * 修改${functionName} + */ + @SaCheckPermission("${permissionPrefix}:edit") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.updateByBo(bo)); + } + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField}s 主键串 + */ + @SaCheckPermission("${permissionPrefix}:remove") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true)); + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 00000000..f78f35c9 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,59 @@ +package ${packageName}.domain; + +#foreach ($column in $columns) +#if($column.javaField=='tenantId') +#set($IsTenant=1) +#end +#end +#if($IsTenant==1) +import core.tenant.common.com.xmzs.TenantEntity; +#else +#end +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +#foreach ($import in $importList) +import ${import}; +#end + +import java.io.Serial; + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($IsTenant==1) +#set($Entity="TenantEntity") +#else +#set($Entity="BaseEntity") +#end +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("${tableName}") +public class ${ClassName} extends ${Entity} { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** + * $column.columnComment + */ +#if($column.javaField=='delFlag') + @TableLogic +#end +#if($column.javaField=='version') + @Version +#end +#if($column.isPk==1) + @TableId(value = "$column.columnName") +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 00000000..d8095956 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,15 @@ +package ${packageName}.mapper; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}, ${ClassName}Vo> { + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 00000000..c53469a6 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,53 @@ +package ${packageName}.service; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +#if($table.crud || $table.sub) +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; +#end + +import java.util.Collection; +import java.util.List; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service { + + /** + * 查询${functionName} + */ + ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); + +#if($table.crud || $table.sub) + /** + * 查询${functionName}列表 + */ + TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery); +#end + + /** + * 查询${functionName}列表 + */ + List<${ClassName}Vo> queryList(${ClassName}Bo bo); + + /** + * 新增${functionName} + */ + Boolean insertByBo(${ClassName}Bo bo); + + /** + * 修改${functionName} + */ + Boolean updateByBo(${ClassName}Bo bo); + + /** + * 校验并批量删除${functionName}信息 + */ + Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 00000000..c9ca78ee --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,133 @@ +package ${packageName}.service.impl; + +import com.xmzs.common.core.utils.MapstructUtils; + #if($table.crud || $table.sub) +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +#end +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.${ClassName}; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.service.I${ClassName}Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@RequiredArgsConstructor +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service { + + private final ${ClassName}Mapper baseMapper; + + /** + * 查询${functionName} + */ + @Override + public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ + return baseMapper.selectVoById(${pkColumn.javaField}); + } + +#if($table.crud || $table.sub) + /** + * 查询${functionName}列表 + */ + @Override + public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } +#end + + /** + * 查询${functionName}列表 + */ + @Override + public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); +#foreach($column in $columns) +#if($column.query) +#set($queryType=$column.queryType) +#set($javaField=$column.javaField) +#set($javaType=$column.javaType) +#set($columnName=$column.columnName) +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#set($mpMethod=$column.queryType.toLowerCase()) +#if($queryType != 'BETWEEN') +#if($javaType == 'String') +#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') +#else +#set($condition='bo.get'+$AttrName+'() != null') +#end + lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); +#else + lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, + ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName")); +#end +#end +#end + return lqw; + } + + /** + * 新增${functionName} + */ + @Override + public Boolean insertByBo(${ClassName}Bo bo) { + ${ClassName} add = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; +#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) + if (flag) { + bo.set$pk(add.get$pk()); + } + return flag; + } + + /** + * 修改${functionName} + */ + @Override + public Boolean updateByBo(${ClassName}Bo bo) { + ${ClassName} update = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(${ClassName} entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除${functionName} + */ + @Override + public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm new file mode 100644 index 00000000..97800e1b --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -0,0 +1,59 @@ +package ${packageName}.domain.vo; + +#foreach ($import in $importList) +import ${import}; +#end +import ${packageName}.domain.${ClassName}; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import annotation.excel.common.com.xmzs.ExcelDictFormat; +import convert.excel.common.com.xmzs.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * ${functionName}视图对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = ${ClassName}.class) +public class ${ClassName}Vo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if(${column.dictType} && ${column.dictType} != '') + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "${column.dictType}") +#elseif($parentheseIndex != -1) + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") +#else + @ExcelProperty(value = "${comment}") +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 00000000..9295524a --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/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/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm new file mode 100644 index 00000000..f6638be5 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate, null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate, null, null, ''); diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm new file mode 100644 index 00000000..09233923 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm @@ -0,0 +1,20 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, now(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, now(), null, null, ''); + diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 00000000..01824c27 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate(), null, null, ''); diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm new file mode 100644 index 00000000..bdf166e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, getdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, getdate(), null, null, ''); diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm new file mode 100644 index 00000000..3aa4a5f6 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ${BusinessName}VO, ${BusinessName}Form, ${BusinessName}Query } from '@/api/${moduleName}/${businessName}/types'; + +/** + * 查询${functionName}列表 + * @param query + * @returns {*} + */ + +export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<${BusinessName}VO[]> => { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }); +}; + +/** + * 查询${functionName}详细 + * @param ${pkColumn.javaField} + */ +export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }); +}; + +/** + * 新增${functionName} + * @param data + */ +export const add${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }); +}; + +/** + * 修改${functionName} + * @param data + */ +export const update${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }); +}; + +/** + * 删除${functionName} + * @param ${pkColumn.javaField} + */ +export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array) => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }); +}; diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm new file mode 100644 index 00000000..99359e02 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm @@ -0,0 +1,44 @@ +export interface ${BusinessName}VO { +#foreach ($column in $columns) +#if($column.insert || $column.edit) + /** + * $column.columnComment + */ + $column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} + +export interface ${BusinessName}Form extends BaseEntity { +#foreach ($column in $columns) +#if($column.insert || $column.edit) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} + +export interface ${BusinessName}Query #if(!${treeCode})extends PageQuery #end{ +#foreach ($column in $columns) +#if($column.query) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 00000000..dda1b467 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,502 @@ + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 00000000..84432ac8 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,468 @@ + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 00000000..9fb48d99 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-job/pom.xml b/ruoyi-modules/ruoyi-job/pom.xml new file mode 100644 index 00000000..7dafc399 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/pom.xml @@ -0,0 +1,36 @@ + + + + com.xmzs + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + jar + ruoyi-job + + + 任务调度 + + + + + + + com.xmzs + ruoyi-common-core + + + + + com.xmzs + ruoyi-common-job + + + + + + diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/xmzs/job/service/SampleService.java b/ruoyi-modules/ruoyi-job/src/main/java/com/xmzs/job/service/SampleService.java new file mode 100644 index 00000000..857a209f --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/com/xmzs/job/service/SampleService.java @@ -0,0 +1,252 @@ +package com.xmzs.job.service; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; + +/** + * XxlJob开发示例(Bean模式) + *

+ * 开发步骤: + * 1、任务开发:在Spring Bean实例中,开发Job方法; + * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 + * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; + * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; + * + * @author xuxueli 2019-12-11 21:52:51 + */ +@Slf4j +@Service +public class SampleService { + + + /** + * 1、简单任务示例(Bean模式) + */ + @XxlJob("demoJobHandler") + public void demoJobHandler() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + + for (int i = 0; i < 5; i++) { + XxlJobHelper.log("beat at:" + i); + } + // default success + } + + + /** + * 2、分片广播任务 + */ + @XxlJob("shardingJobHandler") + public void shardingJobHandler() throws Exception { + + // 分片参数 + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + + // 业务逻辑 + for (int i = 0; i < shardTotal; i++) { + if (i == shardIndex) { + XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); + } else { + XxlJobHelper.log("第 {} 片, 忽略", i); + } + } + + } + + + /** + * 3、命令行任务 + */ + @XxlJob("commandJobHandler") + public void commandJobHandler() throws Exception { + String command = XxlJobHelper.getJobParam(); + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(command); + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + //Process process = Runtime.getRuntime().exec(command); + + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobHelper.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobHelper.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + // default success + } else { + XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed"); + } + + } + + + /** + * 4、跨平台Http任务 + * 参数示例: + * "url: http://www.baidu.com\n" + + * "method: get\n" + + * "data: content\n"; + */ + @XxlJob("httpJobHandler") + public void httpJobHandler() throws Exception { + + // param parse + String param = XxlJobHelper.getJobParam(); + if (param == null || param.trim().length() == 0) { + XxlJobHelper.log("param[" + param + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + + String[] httpParams = param.split("\n"); + String url = null; + String method = null; + String data = null; + for (String httpParam : httpParams) { + if (httpParam.startsWith("url:")) { + url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); + } + if (httpParam.startsWith("method:")) { + method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); + } + if (httpParam.startsWith("data:")) { + data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); + } + } + + // param valid + if (url == null || url.trim().length() == 0) { + XxlJobHelper.log("url[" + url + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + if (method == null || !Arrays.asList("GET", "POST").contains(method)) { + XxlJobHelper.log("method[" + method + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + boolean isPostMethod = method.equals("POST"); + + // request + HttpURLConnection connection = null; + BufferedReader bufferedReader = null; + try { + // connection + URL realUrl = new URL(url); + connection = (HttpURLConnection) realUrl.openConnection(); + + // connection setting + connection.setRequestMethod(method); + connection.setDoOutput(isPostMethod); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setReadTimeout(5 * 1000); + connection.setConnectTimeout(3 * 1000); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); + + // do connection + connection.connect(); + + // data + if (isPostMethod && data != null && data.trim().length() > 0) { + DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); + dataOutputStream.write(data.getBytes("UTF-8")); + dataOutputStream.flush(); + dataOutputStream.close(); + } + + // valid StatusCode + int statusCode = connection.getResponseCode(); + if (statusCode != 200) { + throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); + } + + // result + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + StringBuilder result = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String responseMsg = result.toString(); + + XxlJobHelper.log(responseMsg); + + return; + } catch (Exception e) { + XxlJobHelper.log(e); + + XxlJobHelper.handleFail(); + return; + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + if (connection != null) { + connection.disconnect(); + } + } catch (Exception e2) { + XxlJobHelper.log(e2); + } + } + + } + + /** + * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; + */ + @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") + public void demoJobHandler2() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + } + + public void init() { + log.info("init"); + } + + public void destroy() { + log.info("destory"); + } + + +} diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml new file mode 100644 index 00000000..c1fe78a4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/pom.xml @@ -0,0 +1,96 @@ + + + + com.xmzs + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + + ruoyi-system + + + system系统模块 + + + + + + com.xmzs + ruoyi-common-core + + + + com.xmzs + ruoyi-common-doc + + + + com.xmzs + ruoyi-common-mybatis + + + + com.xmzs + ruoyi-common-translation + + + + + com.xmzs + ruoyi-common-oss + + + + com.xmzs + ruoyi-common-log + + + + + com.xmzs + ruoyi-common-excel + + + + + com.xmzs + ruoyi-common-sms + + + + com.xmzs + ruoyi-common-tenant + + + + com.xmzs + ruoyi-common-security + + + + com.xmzs + ruoyi-common-web + + + + com.xmzs + ruoyi-common-idempotent + + + + com.xmzs + ruoyi-common-sensitive + + + + + com.xmzs + ruoyi-common-chat + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaConfiguration.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaConfiguration.java new file mode 100644 index 00000000..55515a74 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaConfiguration.java @@ -0,0 +1,132 @@ +package com.xmzs.system.cofing; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import cn.binarywang.wx.miniapp.message.WxMaMessageHandler; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.error.WxRuntimeException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Admin + */ +@Slf4j +@Configuration +@EnableConfigurationProperties(WxMaProperties.class) +public class WxMaConfiguration { + private final WxMaProperties properties; + + @Autowired + public WxMaConfiguration(WxMaProperties properties) { + this.properties = properties; + } + + @Bean + public WxMaService wxMaService() { + List configs = this.properties.getConfigs(); + if (configs == null) { + throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!"); + } + WxMaService maService = new WxMaServiceImpl(); + maService.setMultiConfigs( + configs.stream() + .map(a -> { + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); +// WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool()); + // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常 + config.setAppid(a.getAppid()); + config.setSecret(a.getSecret()); + config.setToken(a.getToken()); + config.setAesKey(a.getAesKey()); + config.setMsgDataFormat(a.getMsgDataFormat()); + return config; + }).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o))); + return maService; + } + + @Bean + public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) { + final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService); + router + .rule().handler(logHandler).next() + .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end() + .rule().async(false).content("文本").handler(textHandler).end() + .rule().async(false).content("图片").handler(picHandler).end() + .rule().async(false).content("二维码").handler(qrcodeHandler).end(); + return router; + } + + private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> { + service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder() + .templateId("此处更换为自己的模板id") + .data(Lists.newArrayList( + new WxMaSubscribeMessage.MsgData("keyword1", "339208499"))) + .toUser(wxMessage.getFromUser()) + .build()); + return null; + }; + + private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> { + log.info("收到消息:" + wxMessage.toString()); + service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson()) + .toUser(wxMessage.getFromUser()).build()); + return null; + }; + + private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> { + service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息") + .toUser(wxMessage.getFromUser()).build()); + return null; + }; + + private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> { + try { + WxMediaUploadResult uploadResult = service.getMediaService() + .uploadMedia("image", "png", + ClassLoader.getSystemResourceAsStream("tmp.png")); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .newImageBuilder() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + + return null; + }; + + private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> { + try { + final File file = service.getQrcodeService().createQrcode("123", 430); + WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .newImageBuilder() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + + return null; + }; + +} + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaProperties.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaProperties.java new file mode 100644 index 00000000..f6a0f4e4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/cofing/WxMaProperties.java @@ -0,0 +1,52 @@ +package com.xmzs.system.cofing; + +/** + * 微信小程序属性配置类 + * + * @author: wangle + * @date: 2023/5/18 + */ +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import lombok.Data; + +/** + * @author Binary Wang + */ +@Data +@ConfigurationProperties(prefix = "wx.miniapp") +public class WxMaProperties { + + private List configs; + + @Data + public static class Config { + /** + * 设置微信小程序的appid + */ + private String appid; + + /** + * 设置微信小程序的Secret + */ + private String secret; + + /** + * 设置微信小程序消息服务器配置的token + */ + private String token; + + /** + * 设置微信小程序消息服务器配置的EncodingAESKey + */ + private String aesKey; + + /** + * 消息格式,XML或者JSON + */ + private String msgDataFormat; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/CacheController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/CacheController.java new file mode 100644 index 00000000..3beaf46d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/CacheController.java @@ -0,0 +1,55 @@ +package com.xmzs.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.system.domain.vo.CacheListInfoVo; +import lombok.RequiredArgsConstructor; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; + +/** + * 缓存监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/cache") +public class CacheController { + + private final RedissonConnectionFactory connectionFactory; + + /** + * 获取缓存监控列表 + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping() + public R getInfo() throws Exception { + RedisConnection connection = connectionFactory.getConnection(); + Properties commandStats = connection.commands().info("commandstats"); + + List> pieList = new ArrayList<>(); + if (commandStats != null) { + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + } + + CacheListInfoVo infoVo = new CacheListInfoVo(); + infoVo.setInfo(connection.commands().info()); + infoVo.setDbSize(connection.commands().dbSize()); + infoVo.setCommandStats(pieList); + return R.ok(infoVo); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysLogininforController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysLogininforController.java new file mode 100644 index 00000000..2c54ca7c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysLogininforController.java @@ -0,0 +1,89 @@ +package com.xmzs.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysLogininforBo; +import com.xmzs.system.domain.vo.SysLogininforVo; +import com.xmzs.system.service.ISysLogininforService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 系统访问记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController { + + private final ISysLogininforService logininforService; + + /** + * 获取系统访问记录列表 + */ + @SaCheckPermission("monitor:logininfor:list") + @GetMapping("/list") + public TableDataInfo list(SysLogininforBo logininfor, PageQuery pageQuery) { + return logininforService.selectPageLogininforList(logininfor, pageQuery); + } + + /** + * 导出系统访问记录列表 + */ + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:logininfor:export") + @PostMapping("/export") + public void export(SysLogininforBo logininfor, HttpServletResponse response) { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil.exportExcel(list, "登录日志", SysLogininforVo.class, response); + } + + /** + * 批量删除登录日志 + * @param infoIds 日志ids + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public R remove(@PathVariable Long[] infoIds) { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + /** + * 清理系统访问记录 + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public R clean() { + logininforService.cleanLogininfor(); + return R.ok(); + } + + @SaCheckPermission("monitor:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public R unlock(@PathVariable("userName") String userName) { + String loginName = GlobalConstants.PWD_ERR_CNT_KEY + userName; + if (RedisUtils.hasKey(loginName)) { + RedisUtils.deleteObject(loginName); + } + return R.ok(); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysOperlogController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysOperlogController.java new file mode 100644 index 00000000..7917f6de --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysOperlogController.java @@ -0,0 +1,75 @@ +package com.xmzs.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.system.domain.bo.SysOperLogBo; +import com.xmzs.system.domain.vo.SysOperLogVo; +import com.xmzs.system.service.ISysOperLogService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController { + + private final ISysOperLogService operLogService; + + /** + * 获取操作日志记录列表 + */ + @SaCheckPermission("monitor:operlog:list") + @GetMapping("/list") + public TableDataInfo list(SysOperLogBo operLog, PageQuery pageQuery) { + return operLogService.selectPageOperLogList(operLog, pageQuery); + } + + /** + * 导出操作日志记录列表 + */ + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:operlog:export") + @PostMapping("/export") + public void export(SysOperLogBo operLog, HttpServletResponse response) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil.exportExcel(list, "操作日志", SysOperLogVo.class, response); + } + + /** + * 批量删除操作日志记录 + * @param operIds 日志ids + */ + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/{operIds}") + public R remove(@PathVariable Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + /** + * 清理操作日志记录 + */ + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/clean") + public R clean() { + operLogService.cleanOperLog(); + return R.ok(); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysUserOnlineController.java new file mode 100644 index 00000000..1756ec34 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,90 @@ +package com.xmzs.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import com.xmzs.common.core.constant.CacheConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.domain.dto.UserOnlineDTO; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.SysUserOnline; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 在线用户监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController { + + /** + * 获取在线用户监控列表 + * + * @param ipaddr IP地址 + * @param userName 用户名 + */ + @SaCheckPermission("monitor:online:list") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) { + // 获取所有未过期的 token + List keys = StpUtil.searchTokenValue("", 0, -1, false); + List userOnlineDTOList = new ArrayList<>(); + for (String key : keys) { + String token = StringUtils.substringAfterLast(key, ":"); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { + continue; + } + userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); + } + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) && + StringUtils.equals(userName, userOnline.getUserName()) + ); + } else if (StringUtils.isNotEmpty(ipaddr)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) + ); + } else if (StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(userName, userOnline.getUserName()) + ); + } + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退用户 + * + * @param tokenId token值 + */ + @SaCheckPermission("monitor:online:forceLogout") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public R forceLogout(@PathVariable String tokenId) { + try { + StpUtil.kickoutByTokenValue(tokenId); + } catch (NotLoginException ignored) { + } + return R.ok(); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysConfigController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysConfigController.java new file mode 100644 index 00000000..2e741461 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysConfigController.java @@ -0,0 +1,137 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysConfigBo; +import com.xmzs.system.domain.vo.SysConfigVo; +import com.xmzs.system.service.ISysConfigService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController { + + private final ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @SaCheckPermission("system:config:list") + @GetMapping("/list") + public TableDataInfo list(SysConfigBo config, PageQuery pageQuery) { + return configService.selectPageConfigList(config, pageQuery); + } + + /** + * 导出参数配置列表 + */ + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:config:export") + @PostMapping("/export") + public void export(SysConfigBo config, HttpServletResponse response) { + List list = configService.selectConfigList(config); + ExcelUtil.exportExcel(list, "参数数据", SysConfigVo.class, response); + } + + /** + * 根据参数编号获取详细信息 + * + * @param configId 参数ID + */ + @SaCheckPermission("system:config:query") + @GetMapping(value = "/{configId}") + public R getInfo(@PathVariable Long configId) { + return R.ok(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + * + * @param configKey 参数Key + */ + @GetMapping(value = "/configKey/{configKey}") + public R getConfigKey(@PathVariable String configKey) { + return R.ok(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @SaCheckPermission("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysConfigBo config) { + if (!configService.checkConfigKeyUnique(config)) { + return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.insertConfig(config); + return R.ok(); + } + + /** + * 修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysConfigBo config) { + if (!configService.checkConfigKeyUnique(config)) { + return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.updateConfig(config); + return R.ok(); + } + + /** + * 根据参数键名修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping("/updateByKey") + public R updateByKey(@RequestBody SysConfigBo config) { + configService.updateConfig(config); + return R.ok(); + } + + /** + * 删除参数配置 + * + * @param configIds 参数ID串 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public R remove(@PathVariable Long[] configIds) { + configService.deleteConfigByIds(configIds); + return R.ok(); + } + + /** + * 刷新参数缓存 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + configService.resetConfigCache(); + return R.ok(); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDeptController.java new file mode 100644 index 00000000..dce84e4d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDeptController.java @@ -0,0 +1,120 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.convert.Convert; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysDeptBo; +import com.xmzs.system.domain.vo.SysDeptVo; +import com.xmzs.system.service.ISysDeptService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController { + + private final ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list") + public R> list(SysDeptBo dept) { + List depts = deptService.selectDeptList(dept); + return R.ok(depts); + } + + /** + * 查询部门列表(排除节点) + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list/exclude/{deptId}") + public R> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDeptBo()); + depts.removeIf(d -> d.getDeptId().equals(deptId) + || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId))); + return R.ok(depts); + } + + /** + * 根据部门编号获取详细信息 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:query") + @GetMapping(value = "/{deptId}") + public R getInfo(@PathVariable Long deptId) { + deptService.checkDeptDataScope(deptId); + return R.ok(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @SaCheckPermission("system:dept:add") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDeptBo dept) { + if (!deptService.checkDeptNameUnique(dept)) { + return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @SaCheckPermission("system:dept:edit") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDeptBo dept) { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } else if (dept.getParentId().equals(deptId)) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) + && deptService.selectNormalChildrenDeptById(deptId) > 0) { + return R.fail("该部门包含未停用的子部门!"); + } + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:remove") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public R remove(@PathVariable Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return R.warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return R.warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictDataController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictDataController.java new file mode 100644 index 00000000..e1485742 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictDataController.java @@ -0,0 +1,117 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.system.domain.bo.SysDictDataBo; +import com.xmzs.system.domain.vo.SysDictDataVo; +import com.xmzs.system.service.ISysDictDataService; +import com.xmzs.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +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 Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController { + + private final ISysDictDataService dictDataService; + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典数据列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictDataBo dictData, PageQuery pageQuery) { + return dictDataService.selectPageDictDataList(dictData, pageQuery); + } + + /** + * 导出字典数据列表 + */ + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictDataBo dictData, HttpServletResponse response) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil.exportExcel(list, "字典数据", SysDictDataVo.class, response); + } + + /** + * 查询字典数据详细 + * + * @param dictCode 字典code + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictCode}") + public R getInfo(@PathVariable Long dictCode) { + return R.ok(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + * + * @param dictType 字典类型 + */ + @GetMapping(value = "/type/{dictType}") + public R> dictType(@PathVariable String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (ObjectUtil.isNull(data)) { + data = new ArrayList<>(); + } + return R.ok(data); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictDataBo dict) { + dictDataService.insertDictData(dict); + return R.ok(); + } + + /** + * 修改保存字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictDataBo dict) { + dictDataService.updateDictData(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictCodes 字典code串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public R remove(@PathVariable Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return R.ok(); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictTypeController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictTypeController.java new file mode 100644 index 00000000..c78907e3 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysDictTypeController.java @@ -0,0 +1,125 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysDictTypeBo; +import com.xmzs.system.domain.vo.SysDictTypeVo; +import com.xmzs.system.service.ISysDictTypeService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 数据字典信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController { + + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典类型列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictTypeBo dictType, PageQuery pageQuery) { + return dictTypeService.selectPageDictTypeList(dictType, pageQuery); + } + + /** + * 导出字典类型列表 + */ + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictTypeBo dictType, HttpServletResponse response) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil.exportExcel(list, "字典类型", SysDictTypeVo.class, response); + } + + /** + * 查询字典类型详细 + * + * @param dictId 字典ID + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictId}") + public R getInfo(@PathVariable Long dictId) { + return R.ok(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictTypeBo dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.insertDictType(dict); + return R.ok(); + } + + /** + * 修改字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictTypeBo dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.updateDictType(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictIds 字典ID串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public R remove(@PathVariable Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return R.ok(); + } + + /** + * 刷新字典缓存 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + dictTypeService.resetDictCache(); + return R.ok(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public R> optionselect() { + List dictTypes = dictTypeService.selectDictTypeAll(); + return R.ok(dictTypes); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysMenuController.java new file mode 100644 index 00000000..adc05edc --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysMenuController.java @@ -0,0 +1,182 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.annotation.SaMode; +import cn.hutool.core.lang.tree.Tree; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.SysMenu; +import com.xmzs.system.domain.bo.SysMenuBo; +import com.xmzs.system.domain.vo.MenuTreeSelectVo; +import com.xmzs.system.domain.vo.RouterVo; +import com.xmzs.system.domain.vo.SysMenuVo; +import com.xmzs.system.service.ISysMenuService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 菜单信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController { + + private final ISysMenuService menuService; + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("/getRouters") + public R> getRouters() { + List menus = menuService.selectMenuTreeByUserId(LoginHelper.getUserId()); + return R.ok(menuService.buildMenus(menus)); + } + + /** + * 获取菜单列表 + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:list") + @GetMapping("/list") + public R> list(SysMenuBo menu) { + List menus = menuService.selectMenuList(menu, LoginHelper.getUserId()); + return R.ok(menus); + } + + /** + * 根据菜单编号获取详细信息 + * + * @param menuId 菜单ID + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/{menuId}") + public R getInfo(@PathVariable Long menuId) { + return R.ok(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:query") + @GetMapping("/treeselect") + public R>> treeselect(SysMenuBo menu) { + List menus = menuService.selectMenuList(menu, LoginHelper.getUserId()); + return R.ok(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + * + * @param roleId 角色ID + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public R roleMenuTreeselect(@PathVariable("roleId") Long roleId) { + List menus = menuService.selectMenuList(LoginHelper.getUserId()); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); + selectVo.setCheckedKeys(menuService.selectMenuListByRoleId(roleId)); + selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + return R.ok(selectVo); + } + + /** + * 加载对应租户套餐菜单列表树 + * + * @param packageId 租户套餐ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}") + public R tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) { + List menus = menuService.selectMenuList(LoginHelper.getUserId()); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); + selectVo.setCheckedKeys(menuService.selectMenuListByPackageId(packageId)); + selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + return R.ok(selectVo); + } + + /** + * 新增菜单 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:add") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysMenuBo menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:edit") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysMenuBo menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + * + * @param menuId 菜单ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public R remove(@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return R.warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return R.warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysNoticeController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysNoticeController.java new file mode 100644 index 00000000..1ac4dabc --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysNoticeController.java @@ -0,0 +1,81 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.system.domain.bo.SysNoticeBo; +import com.xmzs.system.domain.vo.SysNoticeVo; +import com.xmzs.system.service.ISysNoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 公告 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController { + + private final ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @SaCheckPermission("system:notice:list") + @GetMapping("/list") + public TableDataInfo list(SysNoticeBo notice, PageQuery pageQuery) { + return noticeService.selectPageNoticeList(notice, pageQuery); + } + + /** + * 根据通知公告编号获取详细信息 + * + * @param noticeId 公告ID + */ + @SaCheckPermission("system:notice:query") + @GetMapping(value = "/{noticeId}") + public R getInfo(@PathVariable Long noticeId) { + return R.ok(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @SaCheckPermission("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysNoticeBo notice) { + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @SaCheckPermission("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysNoticeBo notice) { + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + * + * @param noticeIds 公告ID串 + */ + @SaCheckPermission("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public R remove(@PathVariable Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssConfigController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssConfigController.java new file mode 100644 index 00000000..60ddea45 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssConfigController.java @@ -0,0 +1,105 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.core.validate.QueryGroup; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysOssConfigBo; +import com.xmzs.system.domain.vo.SysOssConfigVo; +import com.xmzs.system.service.ISysOssConfigService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 对象存储配置 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resource/oss/config") +public class SysOssConfigController extends BaseController { + + private final ISysOssConfigService ossConfigService; + + /** + * 查询对象存储配置列表 + */ + @SaCheckPermission("system:oss:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) { + return ossConfigService.queryPageList(bo, pageQuery); + } + + /** + * 获取对象存储配置详细信息 + * + * @param ossConfigId OSS配置ID + */ + @SaCheckPermission("system:oss:query") + @GetMapping("/{ossConfigId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long ossConfigId) { + return R.ok(ossConfigService.queryById(ossConfigId)); + } + + /** + * 新增对象存储配置 + */ + @SaCheckPermission("system:oss:add") + @Log(title = "对象存储配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.insertByBo(bo)); + } + + /** + * 修改对象存储配置 + */ + @SaCheckPermission("system:oss:edit") + @Log(title = "对象存储配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.updateByBo(bo)); + } + + /** + * 删除对象存储配置 + * + * @param ossConfigIds OSS配置ID串 + */ + @SaCheckPermission("system:oss:remove") + @Log(title = "对象存储配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ossConfigIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossConfigIds) { + return toAjax(ossConfigService.deleteWithValidByIds(List.of(ossConfigIds), true)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:oss:edit") + @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.updateOssConfigStatus(bo)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssController.java new file mode 100644 index 00000000..431ce896 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysOssController.java @@ -0,0 +1,108 @@ +package com.xmzs.system.controller.system; + + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.QueryGroup; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysOssBo; +import com.xmzs.system.domain.vo.SysOssUploadVo; +import com.xmzs.system.domain.vo.SysOssVo; +import com.xmzs.system.service.ISysOssService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * 文件上传 控制层 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resource/oss") +public class SysOssController extends BaseController { + + private final ISysOssService ossService; + + /** + * 查询OSS对象存储列表 + */ + @SaCheckPermission("system:oss:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) { + return ossService.queryPageList(bo, pageQuery); + } + + /** + * 查询OSS对象基于id串 + * + * @param ossIds OSS对象ID串 + */ + @SaCheckPermission("system:oss:list") + @GetMapping("/listByIds/{ossIds}") + public R> listByIds(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossIds) { + List list = ossService.listByIds(Arrays.asList(ossIds)); + return R.ok(list); + } + + /** + * 上传OSS对象存储 + * + * @param file 文件 + */ + @SaCheckPermission("system:oss:upload") + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file) { + if (ObjectUtil.isNull(file)) { + return R.fail("上传文件不能为空"); + } + SysOssVo oss = ossService.upload(file); + SysOssUploadVo uploadVo = new SysOssUploadVo(); + uploadVo.setUrl(oss.getUrl()); + uploadVo.setFileName(oss.getOriginalName()); + uploadVo.setOssId(oss.getOssId().toString()); + return R.ok(uploadVo); + } + + /** + * 下载OSS对象 + * + * @param ossId OSS对象ID + */ + @SaCheckPermission("system:oss:download") + @GetMapping("/download/{ossId}") + public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { + ossService.download(ossId, response); + } + + /** + * 删除OSS对象存储 + * + * @param ossIds OSS对象ID串 + */ + @SaCheckPermission("system:oss:remove") + @Log(title = "OSS对象存储", businessType = BusinessType.DELETE) + @DeleteMapping("/{ossIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossIds) { + return toAjax(ossService.deleteWithValidByIds(List.of(ossIds), true)); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysPostController.java new file mode 100644 index 00000000..f98d1357 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysPostController.java @@ -0,0 +1,115 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysPostBo; +import com.xmzs.system.domain.vo.SysPostVo; +import com.xmzs.system.service.ISysPostService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController { + + private final ISysPostService postService; + + /** + * 获取岗位列表 + */ + @SaCheckPermission("system:post:list") + @GetMapping("/list") + public TableDataInfo list(SysPostBo post, PageQuery pageQuery) { + return postService.selectPagePostList(post, pageQuery); + } + + /** + * 导出岗位列表 + */ + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:post:export") + @PostMapping("/export") + public void export(SysPostBo post, HttpServletResponse response) { + List list = postService.selectPostList(post); + ExcelUtil.exportExcel(list, "岗位数据", SysPostVo.class, response); + } + + /** + * 根据岗位编号获取详细信息 + * + * @param postId 岗位ID + */ + @SaCheckPermission("system:post:query") + @GetMapping(value = "/{postId}") + public R getInfo(@PathVariable Long postId) { + return R.ok(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @SaCheckPermission("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysPostBo post) { + if (!postService.checkPostNameUnique(post)) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @SaCheckPermission("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysPostBo post) { + if (!postService.checkPostNameUnique(post)) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + * + * @param postIds 岗位ID串 + */ + @SaCheckPermission("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public R remove(@PathVariable Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public R> optionselect() { + List posts = postService.selectPostAll(); + return R.ok(posts); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysProfileController.java new file mode 100644 index 00000000..719cf2a9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysProfileController.java @@ -0,0 +1,123 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.FileUtil; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.file.MimeTypeUtils; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.bo.SysUserProfileBo; +import com.xmzs.system.domain.vo.AvatarVo; +import com.xmzs.system.domain.vo.ProfileVo; +import com.xmzs.system.domain.vo.SysOssVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.service.ISysOssService; +import com.xmzs.system.service.ISysUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; + +/** + * 个人信息 业务处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController { + + private final ISysUserService userService; + private final ISysOssService ossService; + + /** + * 个人信息 + */ + @GetMapping + public R profile() { + SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); + ProfileVo profileVo = new ProfileVo(); + profileVo.setUser(user); + profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserName())); + profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserName())); + return R.ok(profileVo); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public R updateProfile(@RequestBody SysUserProfileBo profile) { + SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUserId(LoginHelper.getUserId()); + if (userService.updateUserProfile(user) > 0) { + return R.ok(); + } + return R.fail("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + * + * @param newPassword 旧密码 + * @param oldPassword 新密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public R updatePwd(String oldPassword, String newPassword) { + SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); + String password = user.getPassword(); + if (!BCrypt.checkpw(oldPassword, password)) { + return R.fail("修改密码失败,旧密码错误"); + } + if (BCrypt.checkpw(newPassword, password)) { + return R.fail("新密码不能与旧密码相同"); + } + + if (userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(newPassword)) > 0) { + return R.ok(); + } + return R.fail("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + * + * @param avatarfile 用户头像 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { + if (!avatarfile.isEmpty()) { + String extension = FileUtil.extName(avatarfile.getOriginalFilename()); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + SysOssVo oss = ossService.upload(avatarfile); + String avatar = oss.getUrl(); + if (userService.updateUserAvatar(LoginHelper.getUserId(), oss.getUrl())) { + AvatarVo avatarVo = new AvatarVo(); + avatarVo.setImgUrl(avatar); + return R.ok(avatarVo); + } + } + return R.fail("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysRoleController.java new file mode 100644 index 00000000..ac118392 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysRoleController.java @@ -0,0 +1,226 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.SysUserRole; +import com.xmzs.system.domain.bo.SysDeptBo; +import com.xmzs.system.domain.bo.SysRoleBo; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.DeptTreeSelectVo; +import com.xmzs.system.domain.vo.SysRoleVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.service.ISysDeptService; +import com.xmzs.system.service.ISysRoleService; +import com.xmzs.system.service.ISysUserService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 角色信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController { + + private final ISysRoleService roleService; + private final ISysUserService userService; + private final ISysDeptService deptService; + + /** + * 获取角色信息列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/list") + public TableDataInfo list(SysRoleBo role, PageQuery pageQuery) { + return roleService.selectPageRoleList(role, pageQuery); + } + + /** + * 导出角色信息列表 + */ + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:role:export") + @PostMapping("/export") + public void export(SysRoleBo role, HttpServletResponse response) { + List list = roleService.selectRoleList(role); + ExcelUtil.exportExcel(list, "角色数据", SysRoleVo.class, response); + } + + /** + * 根据角色编号获取详细信息 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:query") + @GetMapping(value = "/{roleId}") + public R getInfo(@PathVariable Long roleId) { + roleService.checkRoleDataScope(roleId); + return R.ok(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @SaCheckPermission("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysRoleBo role) { + if (!roleService.checkRoleNameUnique(role)) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role.getRoleId()); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + + if (roleService.updateRole(role) > 0) { + roleService.cleanOnlineUserByRole(role.getRoleId()); + return R.ok(); + } + return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public R dataScope(@RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role.getRoleId()); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role.getRoleId()); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.updateRoleStatus(role.getRoleId(), role.getStatus())); + } + + /** + * 删除角色 + * + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public R remove(@PathVariable Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @SaCheckPermission("system:role:query") + @GetMapping("/optionselect") + public R> optionselect() { + return R.ok(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUserBo user, PageQuery pageQuery) { + return userService.selectAllocatedList(user, pageQuery); + } + + /** + * 查询未分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUserBo user, PageQuery pageQuery) { + return userService.selectUnallocatedList(user, pageQuery); + } + + /** + * 取消授权用户 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public R cancelAuthUser(@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public R cancelAuthUserAll(Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public R selectAuthUserAll(Long roleId, Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:list") + @GetMapping(value = "/deptTree/{roleId}") + public R roleDeptTreeselect(@PathVariable("roleId") Long roleId) { + DeptTreeSelectVo selectVo = new DeptTreeSelectVo(); + selectVo.setCheckedKeys(deptService.selectDeptListByRoleId(roleId)); + selectVo.setDepts(deptService.selectDeptTreeList(new SysDeptBo())); + return R.ok(selectVo); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantController.java new file mode 100644 index 00000000..b9654dbe --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantController.java @@ -0,0 +1,181 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import com.baomidou.lock.annotation.Lock4j; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysTenantBo; +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.system.service.ISysTenantService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 租户管理 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/tenant") +public class SysTenantController extends BaseController { + + private final ISysTenantService tenantService; + + /** + * 查询租户列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:list") + @GetMapping("/list") + public TableDataInfo list(SysTenantBo bo, PageQuery pageQuery) { + return tenantService.queryPageList(bo, pageQuery); + } + + /** + * 导出租户列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:export") + @Log(title = "租户", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysTenantBo bo, HttpServletResponse response) { + List list = tenantService.queryList(bo); + ExcelUtil.exportExcel(list, "租户", SysTenantVo.class, response); + } + + /** + * 获取租户详细信息 + * + * @param id 主键 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(tenantService.queryById(id)); + } + + /** + * 新增租户 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:add") + @Log(title = "租户", businessType = BusinessType.INSERT) + @Lock4j + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysTenantBo bo) { + if (!tenantService.checkCompanyNameUnique(bo)) { + return R.fail("新增租户'" + bo.getCompanyName() + "'失败,企业名称已存在"); + } + return toAjax(TenantHelper.ignore(() -> tenantService.insertByBo(bo))); + } + + /** + * 修改租户 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysTenantBo bo) { + tenantService.checkTenantAllowed(bo.getTenantId()); + if (!tenantService.checkCompanyNameUnique(bo)) { + return R.fail("修改租户'" + bo.getCompanyName() + "'失败,公司名称已存在"); + } + return toAjax(tenantService.updateByBo(bo)); + } + + /** + * 状态修改 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysTenantBo bo) { + tenantService.checkTenantAllowed(bo.getTenantId()); + return toAjax(tenantService.updateTenantStatus(bo)); + } + + /** + * 删除租户 + * + * @param ids 主键串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:remove") + @Log(title = "租户", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(tenantService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 动态切换租户 + * + * @param tenantId 租户ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @GetMapping("/dynamic/{tenantId}") + public R dynamicTenant(@NotBlank(message = "租户ID不能为空") @PathVariable String tenantId) { + TenantHelper.setDynamic(tenantId); + return R.ok(); + } + + /** + * 清除动态租户 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @GetMapping("/dynamic/clear") + public R dynamicClear() { + TenantHelper.clearDynamic(); + return R.ok(); + } + + + /** + * 同步租户套餐 + * + * @param tenantId 租户id + * @param packageId 套餐id + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户", businessType = BusinessType.UPDATE) + @GetMapping("/syncTenantPackage") + public R syncTenantPackage(@NotBlank(message = "租户ID不能为空") String tenantId, @NotBlank(message = "套餐ID不能为空") String packageId) { + return toAjax(TenantHelper.ignore(() -> tenantService.syncTenantPackage(tenantId, packageId))); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantPackageController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantPackageController.java new file mode 100644 index 00000000..9ade45f1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysTenantPackageController.java @@ -0,0 +1,134 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.idempotent.annotation.RepeatSubmit; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysTenantPackageBo; +import com.xmzs.system.domain.vo.SysTenantPackageVo; +import com.xmzs.system.service.ISysTenantPackageService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 租户套餐管理 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/tenant/package") +public class SysTenantPackageController extends BaseController { + + private final ISysTenantPackageService tenantPackageService; + + /** + * 查询租户套餐列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:list") + @GetMapping("/list") + public TableDataInfo list(SysTenantPackageBo bo, PageQuery pageQuery) { + return tenantPackageService.queryPageList(bo, pageQuery); + } + + /** + * 查询租户套餐下拉选列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:list") + @GetMapping("/selectList") + public R> selectList() { + return R.ok(tenantPackageService.selectList()); + } + + /** + * 导出租户套餐列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:export") + @Log(title = "租户套餐", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysTenantPackageBo bo, HttpServletResponse response) { + List list = tenantPackageService.queryList(bo); + ExcelUtil.exportExcel(list, "租户套餐", SysTenantPackageVo.class, response); + } + + /** + * 获取租户套餐详细信息 + * + * @param packageId 主键 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:query") + @GetMapping("/{packageId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long packageId) { + return R.ok(tenantPackageService.queryById(packageId)); + } + + /** + * 新增租户套餐 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:add") + @Log(title = "租户套餐", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysTenantPackageBo bo) { + return toAjax(tenantPackageService.insertByBo(bo)); + } + + /** + * 修改租户套餐 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:edit") + @Log(title = "租户套餐", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) { + return toAjax(tenantPackageService.updateByBo(bo)); + } + + /** + * 状态修改 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:edit") + @Log(title = "租户套餐", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysTenantPackageBo bo) { + return toAjax(tenantPackageService.updatePackageStatus(bo)); + } + + /** + * 删除租户套餐 + * + * @param packageIds 主键串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:remove") + @Log(title = "租户套餐", businessType = BusinessType.DELETE) + @DeleteMapping("/{packageIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] packageIds) { + return toAjax(tenantPackageService.deleteWithValidByIds(List.of(packageIds), true)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysUserController.java new file mode 100644 index 00000000..d5f6e6b1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/controller/system/SysUserController.java @@ -0,0 +1,299 @@ +package com.xmzs.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.secure.BCrypt; + +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.xmzs.system.domain.request.UserRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; +import com.xmzs.common.core.domain.R; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.excel.core.ExcelResult; +import com.xmzs.common.excel.utils.ExcelUtil; +import com.xmzs.common.log.annotation.Log; +import com.xmzs.common.log.enums.BusinessType; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.common.web.core.BaseController; +import com.xmzs.system.domain.bo.SysDeptBo; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.*; +import com.xmzs.system.listener.SysUserImportListener; +import com.xmzs.system.service.*; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用户信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + + private final ISysUserService userService; + private final ISysRoleService roleService; + private final ISysPostService postService; + private final ISysDeptService deptService; + private final ISysTenantService tenantService; + private final ISysOssService ossService; + /** + * 获取用户列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/list") + public TableDataInfo list(SysUserBo user, PageQuery pageQuery) { + return userService.selectPageUserList(user, pageQuery); + } + + /** + * 导出用户列表 + */ + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:user:export") + @PostMapping("/export") + public void export(SysUserBo user, HttpServletResponse response) { + List list = userService.selectUserList(user); + List listVo = MapstructUtils.convert(list, SysUserExportVo.class); + ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); + } + + /** + * 导入数据 + * + * @param file 导入文件 + * @param updateSupport 是否更新已存在数据 + */ + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:user:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { + ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); + return R.ok(result.getAnalysis()); + } + + /** + * 获取导入模板 + */ + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("/getInfo") + public R getInfo() { + UserInfoVo userInfoVo = new UserInfoVo(); + LoginUser loginUser = LoginHelper.getLoginUser(); + if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { + // 超级管理员 如果重新加载用户信息需清除动态租户 + TenantHelper.clearDynamic(); + } + SysUserVo user = userService.selectUserById(loginUser.getUserId()); + userInfoVo.setUser(user); + userInfoVo.setPermissions(loginUser.getMenuPermission()); + userInfoVo.setRoles(loginUser.getRolePermission()); + return R.ok(userInfoVo); + } + + /** + * 根据用户编号获取详细信息 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping(value = {"/", "/{userId}"}) + public R getInfo(@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + SysUserInfoVo userInfoVo = new SysUserInfoVo(); + List roles = roleService.selectRoleAll(); + userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); + userInfoVo.setPosts(postService.selectPostAll()); + if (ObjectUtil.isNotNull(userId)) { + SysUserVo sysUser = userService.selectUserById(userId); + userInfoVo.setUser(sysUser); + userInfoVo.setRoleIds(StreamUtils.toList(sysUser.getRoles(), SysRoleVo::getRoleId)); + userInfoVo.setPostIds(postService.selectPostListByUserId(userId)); + } + return R.ok(userInfoVo); + } + + /** + * 新增用户 + */ + @SaCheckPermission("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysUserBo user) { + if (!userService.checkUserNameUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (TenantHelper.isEnable()) { + if (!tenantService.checkAccountBalance(TenantHelper.getTenantId())) { + return R.fail("当前租户下用户名额不足,请联系管理员"); + } + } + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + return toAjax(userService.updateUser(user)); + } + + /** + * 修改用户名称 + */ + @Log(title = "修改用户名称", businessType = BusinessType.UPDATE) + @PostMapping("/editName") + public R editName(@RequestBody @Validated UserRequest userRequest) { + LoginUser loginUser = LoginHelper.getLoginUser(); + userService.updateUserName(loginUser.getUserId(), userRequest.getNickName()); + return R.ok("操作成功!"); + } + + /** + * 修改用户头像 + */ + @Log(title = "修改用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/edit/avatar") + public R editAvatar(@RequestPart("file") MultipartFile file) { + if (ObjectUtil.isNull(file)) { + return R.fail("上传文件不能为空"); + } + LoginUser loginUser = LoginHelper.getLoginUser(); + // 获取当前登录用户 + SysOssVo oss = ossService.upload(file); + userService.updateUserAvatar(loginUser.getUserId(), oss.getUrl()); + return R.ok(oss.getUrl()); + } + + /** + * 小程序-修改用户 + */ + @PostMapping("/edit/xcxUser") + public R editXcxUser(@RequestBody SysUserBo user) { + return R.ok(userService.updateXcxUser(user)); + } + + /** + * 删除用户 + * + * @param userIds 角色ID串 + */ + @SaCheckPermission("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public R remove(@PathVariable Long[] userIds) { + if (ArrayUtil.contains(userIds, LoginHelper.getUserId())) { + return R.fail("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @SaCheckPermission("system:user:resetPwd") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public R resetPwd(@RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.resetUserPwd(user.getUserId(), user.getPassword())); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + return toAjax(userService.updateUserStatus(user.getUserId(), user.getStatus())); + } + + /** + * 根据用户编号获取授权角色 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/authRole/{userId}") + public R authRole(@PathVariable Long userId) { + SysUserVo user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + SysUserInfoVo userInfoVo = new SysUserInfoVo(); + userInfoVo.setUser(user); + userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); + return R.ok(userInfoVo); + } + + /** + * 用户授权角色 + * + * @param userId 用户Id + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public R insertAuthRole(Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return R.ok(); + } + + /** + * 获取部门树列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/deptTree") + public R>> deptTree(SysDeptBo dept) { + return R.ok(deptService.selectDeptTreeList(dept)); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatMessage.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatMessage.java new file mode 100644 index 00000000..1318bea7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatMessage.java @@ -0,0 +1,70 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 聊天消息对象 chat_message + * + * @author Lion Li + * @date 2023-11-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("chat_message") +public class ChatMessage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 用户ID + */ + @NotBlank(message = "用户ID", groups = { AddGroup.class, EditGroup.class }) + private Long UserId; + + /** + * 消息内容 + */ + @NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String content; + + + /** + * 扣除费用 + */ + private Double deductCost; + + /** + * 累计 Tokens + */ + @NotNull(message = "累计 Tokens不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer totalTokens; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class }) + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatToken.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatToken.java new file mode 100644 index 00000000..83631d6e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/ChatToken.java @@ -0,0 +1,49 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户token chat_token + * + * @author Lion Li + * @date 2023-11-26 + */ +@Data +@TableName("chat_token") +public class ChatToken implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 用户ID + */ + @NotBlank(message = "用户ID", groups = { AddGroup.class, EditGroup.class }) + private Long UserId; + + /** + * 待结算token + */ + private Integer token; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/PaymentOrders.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/PaymentOrders.java new file mode 100644 index 00000000..f592fe38 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/PaymentOrders.java @@ -0,0 +1,66 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.math.BigDecimal; + +import java.io.Serial; + +/** + * 支付订单对象 payment_orders + * + * @author Lion Li + * @date 2023-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("payment_orders") +public class PaymentOrders extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 订单编号 + */ + private String orderNo; + + /** + * 订单名称 + */ + private String orderName; + + /** + * 金额 + */ + private BigDecimal amount; + + /** + * 支付状态 + */ + private String paymentStatus; + + /** + * 支付方式 + */ + private String paymentMethod; + + /** + * 用户ID + */ + private Long userId; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysCache.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysCache.java new file mode 100644 index 00000000..d3ed8614 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysCache.java @@ -0,0 +1,47 @@ +package com.xmzs.system.domain; + +import com.xmzs.common.core.utils.StringUtils; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 缓存信息 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class SysCache { + + /** + * 缓存名称 + */ + private String cacheName = ""; + + /** + * 缓存键名 + */ + private String cacheKey = ""; + + /** + * 缓存内容 + */ + private String cacheValue = ""; + + /** + * 备注 + */ + private String remark = ""; + + public SysCache(String cacheName, String remark) { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysConfig.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysConfig.java new file mode 100644 index 00000000..5e49cf0b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysConfig.java @@ -0,0 +1,51 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 参数配置表 sys_config + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_config") +public class SysConfig extends TenantEntity { + + /** + * 参数主键 + */ + @TableId(value = "config_id") + private Long configId; + + /** + * 参数名称 + */ + private String configName; + + /** + * 参数键名 + */ + private String configKey; + + /** + * 参数键值 + */ + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + private String configType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDept.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDept.java new file mode 100644 index 00000000..93f05c02 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDept.java @@ -0,0 +1,78 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 部门表 sys_dept + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dept") +public class SysDept extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + @TableId(value = "dept_id") + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 负责人 + */ + private String leader; + + /** + * 联系电话 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 部门状态:0正常,1停用 + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 祖级列表 + */ + private String ancestors; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictData.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictData.java new file mode 100644 index 00000000..523d4d4d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictData.java @@ -0,0 +1,76 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典数据表 sys_dict_data + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_data") +public class SysDictData extends TenantEntity { + + /** + * 字典编码 + */ + @TableId(value = "dict_code") + private Long dictCode; + + /** + * 字典排序 + */ + private Integer dictSort; + + /** + * 字典标签 + */ + private String dictLabel; + + /** + * 字典键值 + */ + private String dictValue; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + private String cssClass; + + /** + * 表格字典样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + public boolean getDefault() { + return UserConstants.YES.equals(this.isDefault); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictType.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictType.java new file mode 100644 index 00000000..1788177f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysDictType.java @@ -0,0 +1,46 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典类型表 sys_dict_type + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_type") +public class SysDictType extends TenantEntity { + + /** + * 字典主键 + */ + @TableId(value = "dict_id") + private Long dictId; + + /** + * 字典名称 + */ + private String dictName; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysLogininfor.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysLogininfor.java new file mode 100644 index 00000000..1ce9fc19 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysLogininfor.java @@ -0,0 +1,75 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 系统访问记录表 sys_logininfor + * + * @author Lion Li + */ + +@Data +@TableName("sys_logininfor") +public class SysLogininfor implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "info_id") + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysMenu.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysMenu.java new file mode 100644 index 00000000..79cebd77 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysMenu.java @@ -0,0 +1,191 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_menu") +public class SysMenu extends BaseEntity { + + /** + * 菜单ID + */ + @TableId(value = "menu_id") + private Long menuId; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(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; + + /** + * 备注 + */ + private String remark; + + /** + * 父菜单名称 + */ + @TableField(exist = false) + private String parentName; + + /** + * 子菜单 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + + /** + * 获取路由名称 + */ + public String getRouteName() { + String routerName = StringUtils.capitalize(path); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame()) { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + */ + public String getRouterPath() { + String routerPath = this.path; + // 内链打开外网方式 + if (getParentId() != 0L && isInnerLink()) { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0L == getParentId() && UserConstants.TYPE_DIR.equals(getMenuType()) + && UserConstants.NO_FRAME.equals(getIsFrame())) { + routerPath = "/" + this.path; + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame()) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + */ + public String getComponentInfo() { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) { + component = this.component; + } else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) { + component = UserConstants.INNER_LINK; + } else if (StringUtils.isEmpty(this.component) && isParentView()) { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + */ + public boolean isMenuFrame() { + return getParentId() == 0L && UserConstants.TYPE_MENU.equals(menuType) && isFrame.equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + */ + public boolean isInnerLink() { + return isFrame.equals(UserConstants.NO_FRAME) && StringUtils.ishttp(path); + } + + /** + * 是否为parent_view组件 + */ + public boolean isParentView() { + return getParentId() != 0L && UserConstants.TYPE_DIR.equals(menuType); + } + + /** + * 内链域名特殊字符替换 + */ + public static String innerLinkReplaceEach(String path) { + return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."}, + new String[]{"", "", "", "/"}); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysNotice.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysNotice.java new file mode 100644 index 00000000..5e20e4d1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysNotice.java @@ -0,0 +1,51 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + + +/** + * 通知公告表 sys_notice + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_notice") +public class SysNotice extends TenantEntity { + + /** + * 公告ID + */ + @TableId(value = "notice_id") + private Long noticeId; + + /** + * 公告标题 + */ + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOperLog.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOperLog.java new file mode 100644 index 00000000..250e7df6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOperLog.java @@ -0,0 +1,115 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志记录表 oper_log + * + * @author Lion Li + */ + +@Data +@TableName("sys_oper_log") +public class SysOperLog implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @TableId(value = "oper_id") + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOss.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOss.java new file mode 100644 index 00000000..414c47c1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOss.java @@ -0,0 +1,50 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss") +public class SysOss extends TenantEntity { + + /** + * 对象存储主键 + */ + @TableId(value = "oss_id") + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 服务商 + */ + private String service; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOssConfig.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOssConfig.java new file mode 100644 index 00000000..46580f6e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysOssConfig.java @@ -0,0 +1,89 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 对象存储配置对象 sys_oss_config + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss_config") +public class SysOssConfig extends TenantEntity { + + /** + * 主建 + */ + @TableId(value = "oss_config_id") + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(0否 1是) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysPost.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysPost.java new file mode 100644 index 00000000..b8b39652 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysPost.java @@ -0,0 +1,51 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 岗位表 sys_post + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_post") +public class SysPost extends TenantEntity { + + /** + * 岗位序号 + */ + @TableId(value = "post_id") + private Long postId; + + /** + * 岗位编码 + */ + private String postCode; + + /** + * 岗位名称 + */ + private String postName; + + /** + * 岗位排序 + */ + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRole.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRole.java new file mode 100644 index 00000000..52f833e9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRole.java @@ -0,0 +1,79 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 角色表 sys_role + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_role") +public class SysRole extends TenantEntity { + + /** + * 角色ID + */ + @TableId(value = "role_id") + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 角色排序 + */ + private Integer roleSort; + + /** + * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) + */ + private String dataScope; + + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) + */ + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 备注 + */ + private String remark; + + public SysRole(Long roleId) { + this.roleId = roleId; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleDept.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleDept.java new file mode 100644 index 00000000..4cf396c3 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleDept.java @@ -0,0 +1,29 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和部门关联 sys_role_dept + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_dept") +public class SysRoleDept { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleMenu.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleMenu.java new file mode 100644 index 00000000..7f6bc587 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysRoleMenu.java @@ -0,0 +1,29 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_menu") +public class SysRoleMenu { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenant.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenant.java new file mode 100644 index 00000000..a4dde1c6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenant.java @@ -0,0 +1,103 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 租户对象 sys_tenant + * + * @author Michelle.Chung + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_tenant") +public class SysTenant extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "id") + private Long id; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 联系人 + */ + private String contactUserName; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 统一社会信用代码 + */ + private String licenseNumber; + + /** + * 地址 + */ + private String address; + + /** + * 域名 + */ + private String domain; + + /** + * 企业简介 + */ + private String intro; + + /** + * 备注 + */ + private String remark; + + /** + * 租户套餐编号 + */ + private Long packageId; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenantPackage.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenantPackage.java new file mode 100644 index 00000000..8da465cd --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysTenantPackage.java @@ -0,0 +1,54 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.io.Serial; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; + +/** + * 租户套餐对象 sys_tenant_package + * + * @author Michelle.Chung + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_tenant_package") +public class SysTenantPackage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户套餐id + */ + @TableId(value = "package_id") + private Long packageId; + /** + * 套餐名称 + */ + private String packageName; + /** + * 关联菜单id + */ + private String menuIds; + /** + * 备注 + */ + private String remark; + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private Boolean menuCheckStrictly; + /** + * 状态(0正常 1停用) + */ + private String status; + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUser.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUser.java new file mode 100644 index 00000000..e5cb1918 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUser.java @@ -0,0 +1,124 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 用户对象 sys_user + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_user") +public class SysUser extends TenantEntity { + + /** + * 用户ID + */ + @TableId(value = "user_id") + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 用户头像 + */ + private String avatar; + + /** + * 密码 + */ + @TableField( + insertStrategy = FieldStrategy.NOT_EMPTY, + updateStrategy = FieldStrategy.NOT_EMPTY, + whereStrategy = FieldStrategy.NOT_EMPTY + ) + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + /** 普通用户的标识,对当前开发者帐号唯一。一个openid对应一个公众号或小程序 */ + private String openId; + + /** 用户余额 */ + private Double userBalance; + + /** 用户余额 */ + private String userGrade; + + public SysUser(Long userId) { + this.userId = userId; + } + + public boolean isSuperAdmin() { + return UserConstants.SUPER_ADMIN_ID.equals(this.userId); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserOnline.java new file mode 100644 index 00000000..0ef4fd90 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserOnline.java @@ -0,0 +1,54 @@ +package com.xmzs.system.domain; + +import lombok.Data; + +/** + * 当前在线会话 + * + * @author Lion Li + */ + +@Data +public class SysUserOnline { + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserPost.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserPost.java new file mode 100644 index 00000000..ceca287a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserPost.java @@ -0,0 +1,29 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和岗位关联 sys_user_post + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_post") +public class SysUserPost { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserRole.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserRole.java new file mode 100644 index 00000000..688c66c1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/SysUserRole.java @@ -0,0 +1,29 @@ +package com.xmzs.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和角色关联 sys_user_role + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_role") +public class SysUserRole { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/ChatMessageBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/ChatMessageBo.java new file mode 100644 index 00000000..f3525d06 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/ChatMessageBo.java @@ -0,0 +1,65 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.ChatMessage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 聊天消息业务对象 chat_message + * + * @author Lion Li + * @date 2023-11-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ChatMessage.class, reverseConvertGenerate = false) +public class ChatMessageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 用户ID + */ + @NotBlank(message = "用户ID", groups = { AddGroup.class, EditGroup.class }) + private Long UserId; + + /** + * 消息内容 + */ + @NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String content; + + + /** + * 扣除费用 + */ + private Double deductCost; + + /** + * 累计 Tokens + */ + @NotNull(message = "累计 Tokens不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer totalTokens; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class }) + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/PaymentOrdersBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/PaymentOrdersBo.java new file mode 100644 index 00000000..be0bb1da --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/PaymentOrdersBo.java @@ -0,0 +1,73 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.system.domain.PaymentOrders; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.math.BigDecimal; + +/** + * 支付订单业务对象 payment_orders + * + * @author Lion Li + * @date 2023-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PaymentOrders.class, reverseConvertGenerate = false) +public class PaymentOrdersBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 订单编号 + */ + @NotBlank(message = "订单编号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String orderNo; + + /** + * 订单名称 + */ + @NotBlank(message = "订单名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String orderName; + + /** + * 金额 + */ + @NotNull(message = "金额不能为空", groups = { AddGroup.class, EditGroup.class }) + private BigDecimal amount; + + /** + * 支付状态 + */ + @NotBlank(message = "支付状态不能为空", groups = { AddGroup.class, EditGroup.class }) + private String paymentStatus; + + /** + * 支付方式 + */ + @NotBlank(message = "支付方式不能为空", groups = { AddGroup.class, EditGroup.class }) + private String paymentMethod; + + /** + * 用户ID + */ + @NotNull(message = "用户ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long userId; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class }) + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysConfigBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysConfigBo.java new file mode 100644 index 00000000..f3acb89b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysConfigBo.java @@ -0,0 +1,62 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.system.domain.SysConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; + +/** + * 参数配置业务对象 sys_config + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysConfig.class, reverseConvertGenerate = false) +public class SysConfigBo extends BaseEntity { + + /** + * 参数主键 + */ + @NotNull(message = "参数主键不能为空", groups = { EditGroup.class }) + private Long configId; + + /** + * 参数名称 + */ + @NotBlank(message = "参数名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "参数名称不能超过{max}个字符") + private String configName; + + /** + * 参数键名 + */ + @NotBlank(message = "参数键名不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "参数键名长度不能超过{max}个字符") + private String configKey; + + /** + * 参数键值 + */ + @NotBlank(message = "参数键值不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 500, message = "参数键值长度不能超过{max}个字符") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + private String configType; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDeptBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDeptBo.java new file mode 100644 index 00000000..df6a1aad --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDeptBo.java @@ -0,0 +1,73 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysDept; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 部门业务对象 sys_dept + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDept.class, reverseConvertGenerate = false) +public class SysDeptBo extends BaseEntity { + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = { EditGroup.class }) + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + @NotBlank(message = "部门名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符") + private String deptName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer orderNum; + + /** + * 负责人 + */ + private String leader; + + /** + * 联系电话 + */ + @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符") + private String phone; + + /** + * 邮箱 + */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 部门状态(0正常 1停用) + */ + private String status; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictDataBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictDataBo.java new file mode 100644 index 00000000..2174234b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictDataBo.java @@ -0,0 +1,88 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysDictData; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典数据业务对象 sys_dict_data + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDictData.class, reverseConvertGenerate = false) +public class SysDictDataBo extends BaseEntity { + + /** + * 字典编码 + */ + @NotNull(message = "字典编码不能为空", groups = { EditGroup.class }) + private Long dictCode; + + /** + * 字典排序 + */ + private Integer dictSort; + + /** + * 字典标签 + */ + @NotBlank(message = "字典标签不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符") + private String dictLabel; + + /** + * 字典键值 + */ + @NotBlank(message = "字典键值不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符") + private String dictValue; + + /** + * 字典类型 + */ + @NotBlank(message = "字典类型不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符") + private String cssClass; + + /** + * 表格回显样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 创建部门 + */ + private Long createDept; + + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictTypeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictTypeBo.java new file mode 100644 index 00000000..9e0bcc13 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysDictTypeBo.java @@ -0,0 +1,58 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysDictType; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典类型业务对象 sys_dict_type + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDictType.class, reverseConvertGenerate = false) +public class SysDictTypeBo extends BaseEntity { + + /** + * 字典主键 + */ + @NotNull(message = "字典主键不能为空", groups = { EditGroup.class }) + private Long dictId; + + /** + * 字典名称 + */ + @NotBlank(message = "字典名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符") + private String dictName; + + /** + * 字典类型 + */ + @NotBlank(message = "字典类型不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + private String dictType; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysLogininforBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysLogininforBo.java new file mode 100644 index 00000000..227c22eb --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysLogininforBo.java @@ -0,0 +1,77 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.system.domain.SysLogininfor; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 系统访问记录业务对象 sys_logininfor + * + * @author Michelle.Chung + */ + +@Data +@AutoMapper(target = SysLogininfor.class, reverseConvertGenerate = false) +public class SysLogininforBo { + + /** + * 访问ID + */ + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态(0成功 1失败) + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + + /** + * 请求参数 + */ + private Map params = new HashMap<>(); + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysMenuBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysMenuBo.java new file mode 100644 index 00000000..81c19c80 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysMenuBo.java @@ -0,0 +1,111 @@ +package com.xmzs.system.domain.bo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysMenu; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 菜单权限业务对象 sys_menu + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysMenu.class, reverseConvertGenerate = false) +public class SysMenuBo extends BaseEntity { + + /** + * 菜单ID + */ + @NotNull(message = "菜单ID不能为空", groups = { EditGroup.class }) + private Long menuId; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 菜单名称 + */ + @NotBlank(message = "菜单名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符") + private String menuName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer orderNum; + + /** + * 路由地址 + */ + @Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符") + private String path; + + /** + * 组件路径 + */ + @Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符") + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 菜单类型(M目录 C菜单 F按钮) + */ + @NotBlank(message = "菜单类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限标识 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysNoticeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysNoticeBo.java new file mode 100644 index 00000000..7c3591e8 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysNoticeBo.java @@ -0,0 +1,65 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.core.xss.Xss; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysNotice; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 通知公告业务对象 sys_notice + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysNotice.class, reverseConvertGenerate = false) +public class SysNoticeBo extends BaseEntity { + + /** + * 公告ID + */ + @NotNull(message = "公告ID不能为空", groups = { EditGroup.class }) + private Long noticeId; + + /** + * 公告标题 + */ + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 50, message = "公告标题不能超过{max}个字符") + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 创建人名称 + */ + private String createByName; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOperLogBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOperLogBo.java new file mode 100644 index 00000000..a4034a96 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOperLogBo.java @@ -0,0 +1,127 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.log.event.OperLogEvent; +import com.xmzs.system.domain.SysOperLog; +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMappers; +import lombok.Data; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 操作日志记录业务对象 sys_oper_log + * + * @author Michelle.Chung + * @date 2023-02-07 + */ + +@Data +@AutoMappers({ + @AutoMapper(target = SysOperLog.class, reverseConvertGenerate = false), + @AutoMapper(target = OperLogEvent.class) +}) +public class SysOperLogBo { + + /** + * 日志主键 + */ + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 模块标题 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 方法名称 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求URL + */ + private String operUrl; + + /** + * 主机地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; + + /** + * 请求参数 + */ + private Map params = new HashMap<>(); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssBo.java new file mode 100644 index 00000000..40ee5c73 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssBo.java @@ -0,0 +1,49 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysOss; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储分页查询对象 sys_oss + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysOss.class, reverseConvertGenerate = false) +public class SysOssBo extends BaseEntity { + + /** + * ossId + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 服务商 + */ + private String service; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssConfigBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssConfigBo.java new file mode 100644 index 00000000..a7417614 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysOssConfigBo.java @@ -0,0 +1,109 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysOssConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 对象存储配置业务对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysOssConfig.class, reverseConvertGenerate = false) +public class SysOssConfigBo extends BaseEntity { + + /** + * 主建 + */ + @NotNull(message = "主建不能为空", groups = {EditGroup.class}) + private Long ossConfigId; + + /** + * 配置key + */ + @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间") + private String configKey; + + /** + * accessKey + */ + @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间") + private String accessKey; + + /** + * 秘钥 + */ + @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间") + private String secretKey; + + /** + * 桶名称 + */ + @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间") + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间") + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 域 + */ + private String region; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String accessPolicy; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysPostBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysPostBo.java new file mode 100644 index 00000000..5f8ed62d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysPostBo.java @@ -0,0 +1,62 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysPost; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 岗位信息业务对象 sys_post + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysPost.class, reverseConvertGenerate = false) +public class SysPostBo extends BaseEntity { + + /** + * 岗位ID + */ + @NotNull(message = "岗位ID不能为空", groups = { EditGroup.class }) + private Long postId; + + /** + * 岗位编码 + */ + @NotBlank(message = "岗位编码不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 64, message = "岗位编码长度不能超过{max}个字符") + private String postCode; + + /** + * 岗位名称 + */ + @NotBlank(message = "岗位名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 50, message = "岗位名称长度不能超过{max}个字符") + private String postName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysRoleBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysRoleBo.java new file mode 100644 index 00000000..b6dfa3f1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysRoleBo.java @@ -0,0 +1,97 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.system.domain.SysRole; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 角色信息业务对象 sys_role + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysRole.class, reverseConvertGenerate = false) +public class SysRoleBo extends BaseEntity { + + /** + * 角色ID + */ + @NotNull(message = "角色ID不能为空", groups = { EditGroup.class }) + private Long roleId; + + /** + * 角色名称 + */ + @NotBlank(message = "角色名称不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符") + private String roleName; + + /** + * 角色权限字符串 + */ + @NotBlank(message = "角色权限字符串不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符") + private String roleKey; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer roleSort; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) + */ + private String dataScope; + + /** + * 菜单树选择项是否关联显示 + */ + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示 + */ + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 菜单组 + */ + private Long[] menuIds; + + /** + * 部门组(数据权限) + */ + private Long[] deptIds; + + public SysRoleBo(Long roleId) { + this.roleId = roleId; + } + + public boolean isSuperAdmin() { + return UserConstants.SUPER_ADMIN_ID.equals(this.roleId); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantBo.java new file mode 100644 index 00000000..80ed2427 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantBo.java @@ -0,0 +1,114 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.system.domain.SysTenant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; + +/** + * 租户业务对象 sys_tenant + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysTenant.class, reverseConvertGenerate = false) +public class SysTenantBo extends BaseEntity { + + /** + * id + */ + @NotNull(message = "id不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 联系人 + */ + @NotBlank(message = "联系人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String contactUserName; + + /** + * 联系电话 + */ + @NotBlank(message = "联系电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String contactPhone; + + /** + * 企业名称 + */ + @NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String companyName; + + /** + * 用户名(创建系统用户) + */ + @NotBlank(message = "用户名不能为空", groups = { AddGroup.class }) + private String username; + + /** + * 密码(创建系统用户) + */ + @NotBlank(message = "密码不能为空", groups = { AddGroup.class }) + private String password; + + /** + * 统一社会信用代码 + */ + private String licenseNumber; + + /** + * 地址 + */ + private String address; + + /** + * 域名 + */ + private String domain; + + /** + * 企业简介 + */ + private String intro; + + /** + * 备注 + */ + private String remark; + + /** + * 租户套餐编号 + */ + @NotNull(message = "租户套餐不能为空", groups = { AddGroup.class }) + private Long packageId; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantPackageBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantPackageBo.java new file mode 100644 index 00000000..569b719b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysTenantPackageBo.java @@ -0,0 +1,59 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.system.domain.SysTenantPackage; +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import com.xmzs.common.mybatis.core.domain.BaseEntity; + +/** + * 租户套餐业务对象 sys_tenant_package + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysTenantPackage.class, reverseConvertGenerate = false) +public class SysTenantPackageBo extends BaseEntity { + + /** + * 租户套餐id + */ + @NotNull(message = "租户套餐id不能为空", groups = { EditGroup.class }) + private Long packageId; + + /** + * 套餐名称 + */ + @NotBlank(message = "套餐名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String packageName; + + /** + * 关联菜单id + */ + @AutoMapping(target = "menuIds", expression = "java(com.xmzs.common.core.utils.StringUtils.join(source.getMenuIds(), \",\"))") + private Long[] menuIds; + + /** + * 备注 + */ + private String remark; + + /** + * 菜单树选择项是否关联显示 + */ + private Boolean menuCheckStrictly; + + /** + * 状态(0正常 1停用) + */ + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserBo.java new file mode 100644 index 00000000..20aa9d28 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserBo.java @@ -0,0 +1,133 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.xss.Xss; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.common.sensitive.annotation.Sensitive; +import com.xmzs.common.sensitive.core.SensitiveStrategy; +import com.xmzs.system.domain.SysUser; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 用户信息业务对象 sys_user + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysUser.class, reverseConvertGenerate = false) +public class SysUserBo extends BaseEntity { + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") + private String userName; + + /** + * 用户昵称 + */ + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 手机号码 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 微信头像 + */ + private String avatar; + + /** + * 备注 + */ + private String remark; + + /** + * 角色组 + */ + @Size(min = 1, message = "用户角色不能为空") + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** 普通用户的标识,对当前开发者帐号唯一。一个openid对应一个公众号或小程序 */ + private String openId; + + /** 用户等级 */ + private String userGrade; + + /** 用户余额 */ + private BigDecimal userBalance; + + public SysUserBo(Long userId) { + this.userId = userId; + } + + public boolean isSuperAdmin() { + return UserConstants.SUPER_ADMIN_ID.equals(this.userId); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserProfileBo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserProfileBo.java new file mode 100644 index 00000000..d8999e51 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/bo/SysUserProfileBo.java @@ -0,0 +1,55 @@ +package com.xmzs.system.domain.bo; + +import com.xmzs.common.core.xss.Xss; +import com.xmzs.common.mybatis.core.domain.BaseEntity; +import com.xmzs.common.sensitive.annotation.Sensitive; +import com.xmzs.common.sensitive.core.SensitiveStrategy; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 个人信息业务处理 + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysUserProfileBo extends BaseEntity { + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户昵称 + */ + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") + private String nickName; + + /** + * 用户邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 手机号码 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/EmailRequest.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/EmailRequest.java new file mode 100644 index 00000000..f26578ac --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/EmailRequest.java @@ -0,0 +1,18 @@ +package com.xmzs.web.domain.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + +/** + * 用户登录 + */ +@Data +public class EmailRequest { + /** + * 账号 + */ + @NotNull(message = "账号不能为空") + private String username; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/OrderRequest.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/OrderRequest.java new file mode 100644 index 00000000..7a5a1dd2 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/OrderRequest.java @@ -0,0 +1,26 @@ +package com.xmzs.system.domain.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class OrderRequest { + + /** + * 商品金额 + */ + @NotNull(message = "商品金额") + private String money; + + /** + * 商品名称 + */ + @NotNull(message = "商品名称") + private String name; + + /** + * 订单编号 + */ + @NotNull(message = "订单编号") + private String orderNo; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/UserRequest.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/UserRequest.java new file mode 100644 index 00000000..82c5cd2a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/request/UserRequest.java @@ -0,0 +1,18 @@ +package com.xmzs.system.domain.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + +/** + * 编辑用户 + */ +@Data +public class UserRequest { + /** + * 用户名称 + */ + @NotNull(message = "用户名称不能为空") + private String nickName; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/AvatarVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/AvatarVo.java new file mode 100644 index 00000000..a6091e8c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/AvatarVo.java @@ -0,0 +1,18 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +/** + * 用户头像信息 + * + * @author Michelle.Chung + */ +@Data +public class AvatarVo { + + /** + * 头像地址 + */ + private String imgUrl; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CacheListInfoVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CacheListInfoVo.java new file mode 100644 index 00000000..21e5457c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CacheListInfoVo.java @@ -0,0 +1,23 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * 缓存监控列表信息 + * + * @author Michelle.Chung + */ +@Data +public class CacheListInfoVo { + + private Properties info; + + private Long dbSize; + + private List> commandStats; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CaptchaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CaptchaVo.java new file mode 100644 index 00000000..24971a32 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/CaptchaVo.java @@ -0,0 +1,25 @@ +package com.xmzs.web.domain.vo; + +import lombok.Data; + +/** + * 验证码信息 + * + * @author Michelle.Chung + */ +@Data +public class CaptchaVo { + + /** + * 是否开启验证码 + */ + private Boolean captchaEnabled = true; + + private String uuid; + + /** + * 验证码图片 + */ + private String img; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatMessageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatMessageVo.java new file mode 100644 index 00000000..5462beec --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatMessageVo.java @@ -0,0 +1,83 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.esotericsoftware.kryo.serializers.DefaultSerializers; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import com.xmzs.system.handler.CustomerBigDecimalSerialize; +import com.xmzs.system.domain.ChatMessage; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 聊天消息视图对象 chat_message + * + * @author Lion Li + * @date 2023-11-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = ChatMessage.class) +public class ChatMessageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 用户ID + */ + @NotBlank(message = "用户ID", groups = { AddGroup.class, EditGroup.class }) + private Long UserId; + + /** + * 消息内容 + */ + @NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String content; + + + /** + * 扣除费用 + */ + private Double deductCost; + + /** + * 累计 Tokens + */ + @NotNull(message = "累计 Tokens不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer totalTokens; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class }) + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatTokenVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatTokenVo.java new file mode 100644 index 00000000..efbdea5b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ChatTokenVo.java @@ -0,0 +1,45 @@ +package com.xmzs.system.domain.vo; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.xmzs.common.core.validate.AddGroup; +import com.xmzs.common.core.validate.EditGroup; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户token chat_token + * + * @author Lion Li + * @date 2023-11-26 + */ +@Data +public class ChatTokenVo implements Serializable { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 用户ID + */ + @NotBlank(message = "用户ID", groups = { AddGroup.class, EditGroup.class }) + private Long UserId; + + /** + * 待结算token + */ + private Integer token; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/DeptTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/DeptTreeSelectVo.java new file mode 100644 index 00000000..b3c8eb2d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/DeptTreeSelectVo.java @@ -0,0 +1,26 @@ +package com.xmzs.system.domain.vo; + +import cn.hutool.core.lang.tree.Tree; +import lombok.Data; + +import java.util.List; + +/** + * 角色部门列表树信息 + * + * @author Michelle.Chung + */ +@Data +public class DeptTreeSelectVo { + + /** + * 选中部门列表 + */ + private List checkedKeys; + + /** + * 下拉树结构列表 + */ + private List> depts; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginTenantVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginTenantVo.java new file mode 100644 index 00000000..a7852815 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginTenantVo.java @@ -0,0 +1,25 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 登录租户对象 + * + * @author Michelle.Chung + */ +@Data +public class LoginTenantVo { + + /** + * 租户开关 + */ + private Boolean tenantEnabled; + + /** + * 租户对象列表 + */ + private List voList; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginVo.java new file mode 100644 index 00000000..eae4bb46 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/LoginVo.java @@ -0,0 +1,15 @@ +package com.xmzs.web.domain.vo; + +import lombok.Data; +import com.xmzs.common.core.domain.model.LoginUser; + +/** + * 登录返回信息 + * + * @author Michelle.Chung + */ +@Data +public class LoginVo { + private String token; + private LoginUser userInfo; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MenuTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MenuTreeSelectVo.java new file mode 100644 index 00000000..a9ada189 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MenuTreeSelectVo.java @@ -0,0 +1,26 @@ +package com.xmzs.system.domain.vo; + +import cn.hutool.core.lang.tree.Tree; +import lombok.Data; + +import java.util.List; + +/** + * 角色菜单列表树信息 + * + * @author Michelle.Chung + */ +@Data +public class MenuTreeSelectVo { + + /** + * 选中菜单列表 + */ + private List checkedKeys; + + /** + * 菜单下拉树结构列表 + */ + private List> menus; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MetaVo.java new file mode 100644 index 00000000..8f6bdf73 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/MetaVo.java @@ -0,0 +1,61 @@ +package com.xmzs.system.domain.vo; + +import com.xmzs.common.core.utils.StringUtils; +import lombok.Data; + +/** + * 路由显示信息 + * + * @author ruoyi + */ + +@Data +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; + } + + public MetaVo(String title, String icon, boolean noCache, String link) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) { + this.link = link; + } + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/PaymentOrdersVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/PaymentOrdersVo.java new file mode 100644 index 00000000..d52f42f3 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/PaymentOrdersVo.java @@ -0,0 +1,86 @@ +package com.xmzs.system.domain.vo; + +import java.math.BigDecimal; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.xmzs.system.domain.PaymentOrders; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 支付订单视图对象 payment_orders + * + * @author Lion Li + * @date 2023-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PaymentOrders.class) +public class PaymentOrdersVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 订单编号 + */ + @ExcelProperty(value = "订单编号") + private String orderNo; + + /** + * 订单名称 + */ + @ExcelProperty(value = "订单名称") + private String orderName; + + /** + * 金额 + */ + @ExcelProperty(value = "金额") + private BigDecimal amount; + + /** + * 支付状态 + */ + @ExcelProperty(value = "支付状态") + private String paymentStatus; + + /** + * 支付方式 + */ + @ExcelProperty(value = "支付方式") + private String paymentMethod; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 二维码地址 + */ + private String url; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ProfileVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ProfileVo.java new file mode 100644 index 00000000..541fd33a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/ProfileVo.java @@ -0,0 +1,29 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +/** + * 用户个人信息 + * + * @author Michelle.Chung + */ +@Data +public class ProfileVo { + + /** + * 用户信息 + */ + private SysUserVo user; + + /** + * 用户所属角色组 + */ + private String roleGroup; + + /** + * 用户所属岗位组 + */ + private String postGroup; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/RouterVo.java new file mode 100644 index 00000000..feaeb21b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/RouterVo.java @@ -0,0 +1,62 @@ +package com.xmzs.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author Lion Li + */ +@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/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysConfigVo.java new file mode 100644 index 00000000..7f0d3de4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysConfigVo.java @@ -0,0 +1,72 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 参数配置视图对象 sys_config + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysConfig.class) +public class SysConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 参数主键 + */ + @ExcelProperty(value = "参数主键") + private Long configId; + + /** + * 参数名称 + */ + @ExcelProperty(value = "参数名称") + private String configName; + + /** + * 参数键名 + */ + @ExcelProperty(value = "参数键名") + private String configKey; + + /** + * 参数键值 + */ + @ExcelProperty(value = "参数键值") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String configType; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDeptVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDeptVo.java new file mode 100644 index 00000000..314ef4d7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDeptVo.java @@ -0,0 +1,91 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysDept; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 部门视图对象 sys_dept + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDept.class) +public class SysDeptVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 父部门id + */ + private Long parentId; + + /** + * 父部门名称 + */ + private String parentName; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 负责人 + */ + @ExcelProperty(value = "负责人") + private String leader; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String phone; + + /** + * 邮箱 + */ + @ExcelProperty(value = "邮箱") + private String email; + + /** + * 部门状态(0正常 1停用) + */ + @ExcelProperty(value = "部门状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictDataVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictDataVo.java new file mode 100644 index 00000000..3b6ff224 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictDataVo.java @@ -0,0 +1,95 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysDictData; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 字典数据视图对象 sys_dict_data + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDictData.class) +public class SysDictDataVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典编码 + */ + @ExcelProperty(value = "字典编码") + private Long dictCode; + + /** + * 字典排序 + */ + @ExcelProperty(value = "字典排序") + private Integer dictSort; + + /** + * 字典标签 + */ + @ExcelProperty(value = "字典标签") + private String dictLabel; + + /** + * 字典键值 + */ + @ExcelProperty(value = "字典键值") + private String dictValue; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + private String cssClass; + + /** + * 表格回显样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String isDefault; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictTypeVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictTypeVo.java new file mode 100644 index 00000000..292c1064 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysDictTypeVo.java @@ -0,0 +1,66 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysDictType; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 字典类型视图对象 sys_dict_type + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDictType.class) +public class SysDictTypeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典主键 + */ + @ExcelProperty(value = "字典主键") + private Long dictId; + + /** + * 字典名称 + */ + @ExcelProperty(value = "字典名称") + private String dictName; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + private String dictType; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysLogininforVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysLogininforVo.java new file mode 100644 index 00000000..4ee99e37 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysLogininforVo.java @@ -0,0 +1,93 @@ +package com.xmzs.system.domain.vo; + +import java.util.Date; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysLogininfor; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + + +/** + * 系统访问记录视图对象 sys_logininfor + * + * @author Michelle.Chung + * @date 2023-02-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysLogininfor.class) +public class SysLogininforVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 访问ID + */ + @ExcelProperty(value = "序号") + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "用户账号") + private String userName; + + /** + * 登录状态(0成功 1失败) + */ + @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private String status; + + /** + * 登录IP地址 + */ + @ExcelProperty(value = "登录地址") + private String ipaddr; + + /** + * 登录地点 + */ + @ExcelProperty(value = "登录地点") + private String loginLocation; + + /** + * 浏览器类型 + */ + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 操作系统 + */ + @ExcelProperty(value = "操作系统") + private String os; + + + /** + * 提示消息 + */ + @ExcelProperty(value = "提示消息") + private String msg; + + /** + * 访问时间 + */ + @ExcelProperty(value = "访问时间") + private Date loginTime; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysMenuVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysMenuVo.java new file mode 100644 index 00000000..df59e971 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysMenuVo.java @@ -0,0 +1,116 @@ +package com.xmzs.system.domain.vo; + +import com.xmzs.system.domain.SysMenu; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + + +/** + * 菜单权限视图对象 sys_menu + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysMenu.class) +public class SysMenuVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(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; + + /** + * 创建部门 + */ + private Long createDept; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 子菜单 + */ + private List children = new ArrayList<>(); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysNoticeVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysNoticeVo.java new file mode 100644 index 00000000..eeea7730 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysNoticeVo.java @@ -0,0 +1,73 @@ +package com.xmzs.system.domain.vo; + +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.system.domain.SysNotice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 通知公告视图对象 sys_notice + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysNotice.class) +public class SysNoticeVo implements Serializable { + + @Serial + 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; + + /** + * 备注 + */ + private String remark; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + private String createByName; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOperLogVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOperLogVo.java new file mode 100644 index 00000000..16adc702 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOperLogVo.java @@ -0,0 +1,144 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysOperLog; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 操作日志记录视图对象 sys_oper_log + * + * @author Michelle.Chung + * @date 2023-02-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysOperLog.class) +public class SysOperLogVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @ExcelProperty(value = "日志主键") + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 模块标题 + */ + @ExcelProperty(value = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_oper_type") + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 方法名称 + */ + @ExcelProperty(value = "请求方法") + private String method; + + /** + * 请求方式 + */ + @ExcelProperty(value = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** + * 操作人员 + */ + @ExcelProperty(value = "操作人员") + private String operName; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 请求URL + */ + @ExcelProperty(value = "请求地址") + private String operUrl; + + /** + * 主机地址 + */ + @ExcelProperty(value = "操作地址") + private String operIp; + + /** + * 操作地点 + */ + @ExcelProperty(value = "操作地点") + private String operLocation; + + /** + * 请求参数 + */ + @ExcelProperty(value = "请求参数") + private String operParam; + + /** + * 返回参数 + */ + @ExcelProperty(value = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private Integer status; + + /** + * 错误消息 + */ + @ExcelProperty(value = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @ExcelProperty(value = "操作时间") + private Date operTime; + + /** + * 消耗时间 + */ + @ExcelProperty(value = "消耗时间") + private Long costTime; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssConfigVo.java new file mode 100644 index 00000000..12ff5542 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssConfigVo.java @@ -0,0 +1,97 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.xmzs.system.domain.SysOssConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 对象存储配置视图对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysOssConfig.class) +public class SysOssConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主建 + */ + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssUploadVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssUploadVo.java new file mode 100644 index 00000000..326ec26d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssUploadVo.java @@ -0,0 +1,28 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +/** + * 上传对象信息 + * + * @author Michelle.Chung + */ +@Data +public class SysOssUploadVo { + + /** + * URL地址 + */ + private String url; + + /** + * 文件名 + */ + private String fileName; + + /** + * 对象存储主键 + */ + private String ossId; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssVo.java new file mode 100644 index 00000000..6c754f0a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysOssVo.java @@ -0,0 +1,72 @@ +package com.xmzs.system.domain.vo; + +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.system.domain.SysOss; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * OSS对象存储视图对象 sys_oss + * + * @author Lion Li + */ +@Data +@AutoMapper(target = SysOss.class) +public class SysOssVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 对象存储主键 + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 上传人 + */ + private Long createBy; + + /** + * 上传人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + private String createByName; + + /** + * 服务商 + */ + private String service; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysPostVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysPostVo.java new file mode 100644 index 00000000..0f87704d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysPostVo.java @@ -0,0 +1,73 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysPost; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 岗位信息视图对象 sys_post + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysPost.class) +public class SysPostVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + @ExcelProperty(value = "岗位序号") + private Long postId; + + /** + * 岗位编码 + */ + @ExcelProperty(value = "岗位编码") + private String postCode; + + /** + * 岗位名称 + */ + @ExcelProperty(value = "岗位名称") + private String postName; + + /** + * 显示顺序 + */ + @ExcelProperty(value = "岗位排序") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysRoleVo.java new file mode 100644 index 00000000..76d6a9f5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysRoleVo.java @@ -0,0 +1,100 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysRole; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 角色信息视图对象 sys_role + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysRole.class) +public class SysRoleVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + @ExcelProperty(value = "角色序号") + private Long roleId; + + /** + * 角色名称 + */ + @ExcelProperty(value = "角色名称") + private String roleName; + + /** + * 角色权限字符串 + */ + @ExcelProperty(value = "角色权限") + private String roleKey; + + /** + * 显示顺序 + */ + @ExcelProperty(value = "角色排序") + private Integer roleSort; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) + */ + @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** + * 菜单树选择项是否关联显示 + */ + @ExcelProperty(value = "菜单树选择项是否关联显示") + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示 + */ + @ExcelProperty(value = "部门树选择项是否关联显示") + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 用户是否存在此角色标识 默认不存在 + */ + private boolean flag = false; + + public boolean isSuperAdmin() { + return UserConstants.SUPER_ADMIN_ID.equals(this.roleId); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantPackageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantPackageVo.java new file mode 100644 index 00000000..e1dd2704 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantPackageVo.java @@ -0,0 +1,66 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysTenantPackage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 租户套餐视图对象 sys_tenant_package + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysTenantPackage.class) +public class SysTenantPackageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户套餐id + */ + @ExcelProperty(value = "租户套餐id") + private Long packageId; + + /** + * 套餐名称 + */ + @ExcelProperty(value = "套餐名称") + private String packageName; + + /** + * 关联菜单id + */ + @ExcelProperty(value = "关联菜单id") + private String menuIds; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 菜单树选择项是否关联显示 + */ + @ExcelProperty(value = "菜单树选择项是否关联显示") + private Boolean menuCheckStrictly; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantVo.java new file mode 100644 index 00000000..8d9e6c04 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysTenantVo.java @@ -0,0 +1,115 @@ +package com.xmzs.system.domain.vo; + +import java.util.Date; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import com.xmzs.system.domain.SysTenant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 租户视图对象 sys_tenant + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysTenant.class) +public class SysTenantVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @ExcelProperty(value = "id") + private Long id; + + /** + * 租户编号 + */ + @ExcelProperty(value = "租户编号") + private String tenantId; + + /** + * 联系人 + */ + @ExcelProperty(value = "联系人") + private String contactUserName; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String contactPhone; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String companyName; + + /** + * 统一社会信用代码 + */ + @ExcelProperty(value = "统一社会信用代码") + private String licenseNumber; + + /** + * 地址 + */ + @ExcelProperty(value = "地址") + private String address; + + /** + * 域名 + */ + @ExcelProperty(value = "域名") + private String domain; + + /** + * 企业简介 + */ + @ExcelProperty(value = "企业简介") + private String intro; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 租户套餐编号 + */ + @ExcelProperty(value = "租户套餐编号") + private Long packageId; + + /** + * 过期时间 + */ + @ExcelProperty(value = "过期时间") + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + @ExcelProperty(value = "用户数量") + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + @ExcelProperty(value = "租户状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserExportVo.java new file mode 100644 index 00000000..9241b82f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserExportVo.java @@ -0,0 +1,99 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.ReverseAutoMapping; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 用户对象导出VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@AutoMapper(target = SysUserVo.class, convertGenerate = false) +public class SysUserExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 最后登录IP + */ + @ExcelProperty(value = "最后登录IP") + private String loginIp; + + /** + * 最后登录时间 + */ + @ExcelProperty(value = "最后登录时间") + private Date loginDate; + + /** + * 部门名称 + */ + @ReverseAutoMapping(target = "deptName", source = "dept.deptName") + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 负责人 + */ + @ReverseAutoMapping(target = "leader", source = "dept.leader") + @ExcelProperty(value = "部门负责人") + private String leader; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserImportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserImportVo.java new file mode 100644 index 00000000..b551a33c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserImportVo.java @@ -0,0 +1,76 @@ +package com.xmzs.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.xmzs.common.excel.annotation.ExcelDictFormat; +import com.xmzs.common.excel.convert.ExcelDictConvert; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户对象导入VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +// @Accessors(chain = true) // 导入不允许使用 会找不到set方法 +public class SysUserImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 部门ID + */ + @ExcelProperty(value = "部门编号") + private Long deptId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserInfoVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserInfoVo.java new file mode 100644 index 00000000..0b5633dc --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserInfoVo.java @@ -0,0 +1,40 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 用户信息 + * + * @author Michelle.Chung + */ +@Data +public class SysUserInfoVo { + + /** + * 用户信息 + */ + private SysUserVo user; + + /** + * 角色ID列表 + */ + private List roleIds; + + /** + * 角色列表 + */ + private List roles; + + /** + * 岗位ID列表 + */ + private List postIds; + + /** + * 岗位列表 + */ + private List posts; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserVo.java new file mode 100644 index 00000000..05f84520 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/SysUserVo.java @@ -0,0 +1,148 @@ +package com.xmzs.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.common.translation.constant.TransConstant; +import com.xmzs.system.domain.SysUser; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + + +/** + * 用户信息视图对象 sys_user + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysUser.class) +public class SysUserVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 头像地址 + */ + private String avatar; + + /** + * 微信头像地址 + */ + private String wxAvatar; + + + /** + * 密码 + */ + @JsonIgnore + @JsonProperty + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 部门对象 + */ + private SysDeptVo dept; + + /** + * 角色对象 + */ + private List roles; + + /** + * 角色组 + */ + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** 用户等级 */ + private String userGrade; + + /** 用户余额 */ + private double userBalance; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/TenantListVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/TenantListVo.java new file mode 100644 index 00000000..db2eff8c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/TenantListVo.java @@ -0,0 +1,21 @@ +package com.xmzs.system.domain.vo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +/** + * 租户列表 + * + * @author Lion Li + */ +@Data +@AutoMapper(target = SysTenantVo.class) +public class TenantListVo { + + private String tenantId; + + private String companyName; + + private String domain; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/UserInfoVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/UserInfoVo.java new file mode 100644 index 00000000..55e00b28 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/domain/vo/UserInfoVo.java @@ -0,0 +1,30 @@ +package com.xmzs.system.domain.vo; + +import lombok.Data; + +import java.util.Set; + +/** + * 登录用户信息 + * + * @author Michelle.Chung + */ +@Data +public class UserInfoVo { + + /** + * 用户基本信息 + */ + private SysUserVo user; + + /** + * 菜单权限 + */ + private Set permissions; + + /** + * 角色权限 + */ + private Set roles; + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/handler/CustomerBigDecimalSerialize.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/handler/CustomerBigDecimalSerialize.java new file mode 100644 index 00000000..dab8184e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/handler/CustomerBigDecimalSerialize.java @@ -0,0 +1,25 @@ +package com.xmzs.system.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Objects; + +public class CustomerBigDecimalSerialize extends JsonSerializer { + @Override + public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if(Objects.nonNull(value)) { + //返回到前端的数据为数字类型,前端接收有可能丢失精度 + //gen.writeNumber(value.stripTrailingZeros()); + //返回到前端的数据为字符串类型 + gen.writeString(value.stripTrailingZeros().toPlainString()); + //去除0后缀,如果想统一进行保留精度,也可以采用类似处理 + }else {//如果为null的话,就写null + gen.writeNull(); + } + } +} + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SSEEventSourceListener.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SSEEventSourceListener.java new file mode 100644 index 00000000..387b3cbc --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SSEEventSourceListener.java @@ -0,0 +1,125 @@ +package com.xmzs.system.listener; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.chat.config.LocalCache; +import com.xmzs.common.chat.entity.chat.BaseChatCompletion; +import com.xmzs.common.chat.entity.chat.ChatCompletionResponse; +import com.xmzs.common.chat.utils.TikTokensUtil; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.service.ChatService; +import com.xmzs.system.service.IChatMessageService; +import com.xmzs.system.service.TextReviewService; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; + +import java.util.Objects; + +/** + * 描述:OpenAIEventSourceListener + * + * @author https:www.unfbx.com + * @date 2023-02-22 + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class SSEEventSourceListener extends EventSourceListener { + + private ResponseBodyEmitter emitter; + + private StringBuilder stringBuffer = new StringBuilder(); + + @Autowired(required = false) + public SSEEventSourceListener(ResponseBodyEmitter emitter) { + this.emitter = emitter; + } + + private String modelName; + /** + * {@inheritDoc} + */ + @Override + public void onOpen(EventSource eventSource, Response response) { + log.info("OpenAI建立sse连接..."); + } + + /** + * {@inheritDoc} + */ + @SneakyThrows + @Override + public void onEvent(@NotNull EventSource eventSource, String id, String type, String data) { + try { + if (data.equals("[DONE]")) { + //成功响应 + emitter.complete(); + if(StringUtils.isNotEmpty(modelName)){ + ChatService chatService = SpringUtils.context().getBean(ChatService.class); + // 扣除余额 + int tokens = TikTokensUtil.tokens(modelName,stringBuffer.toString()); + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setModelName(modelName); + chatMessageBo.setContent(stringBuffer.toString()); + chatMessageBo.setTotalTokens(tokens); + Long userId = (Long)LocalCache.CACHE.get("userId"); + chatMessageBo.setUserId(userId); + chatService.deductToken(chatMessageBo); + } + return; + } + ObjectMapper mapper = new ObjectMapper(); + ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); + if(completionResponse == null){ + return; + } + String content = completionResponse.getChoices().get(0).getDelta().getContent(); + if(StringUtils.isEmpty(content)){ + return; + } + if(StringUtils.isEmpty(modelName)){ + modelName = completionResponse.getModel(); + } + stringBuffer.append(content); + emitter.send(data); + } catch (Exception e) { + log.error("sse信息推送失败{}内容:{}",e.getMessage(),data); + eventSource.cancel(); + } + } + + @Override + public void onClosed(EventSource eventSource) { + log.info("OpenAI关闭sse连接..."); + } + + @SneakyThrows + @Override + public void onFailure(EventSource eventSource, Throwable t, Response response) { + if (Objects.isNull(response)) { + return; + } + ResponseBody body = response.body(); + if (Objects.nonNull(body)) { + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); + } else { + log.error("OpenAI sse连接异常data:{},异常:{}", response, t); + } + eventSource.cancel(); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SysUserImportListener.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SysUserImportListener.java new file mode 100644 index 00000000..3bd02741 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/listener/SysUserImportListener.java @@ -0,0 +1,119 @@ +package com.xmzs.system.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.ValidatorUtils; +import com.xmzs.common.excel.core.ExcelListener; +import com.xmzs.common.excel.core.ExcelResult; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysUserImportVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.service.ISysConfigService; +import com.xmzs.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * 系统用户自定义导入 + * + * @author Lion Li + */ +@Slf4j +public class SysUserImportListener extends AnalysisEventListener implements ExcelListener { + + private final ISysUserService userService; + + private final String password; + + private final Boolean isUpdateSupport; + + private final Long operUserId; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public SysUserImportListener(Boolean isUpdateSupport) { + String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.user.initPassword"); + this.userService = SpringUtils.getBean(ISysUserService.class); + this.password = BCrypt.hashpw(initPassword); + this.isUpdateSupport = isUpdateSupport; + this.operUserId = LoginHelper.getUserId(); + } + + @Override + public void invoke(SysUserImportVo userVo, AnalysisContext context) { + SysUserVo sysUser = this.userService.selectUserByUserName(userVo.getUserName()); + try { + // 验证是否存在这个用户 + if (ObjectUtil.isNull(sysUser)) { + SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class); + ValidatorUtils.validate(user); + user.setPassword(password); + user.setCreateBy(operUserId); + userService.insertUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功"); + } else if (isUpdateSupport) { + Long userId = sysUser.getUserId(); + SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class); + user.setUserId(userId); + ValidatorUtils.validate(user); + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(operUserId); + userService.updateUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功"); + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、账号 ").append(sysUser.getUserName()).append(" 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + sysUser.getUserName() + " 导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult<>() { + + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatMessageMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatMessageMapper.java new file mode 100644 index 00000000..7ffb662e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatMessageMapper.java @@ -0,0 +1,16 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.vo.ChatMessageVo; +import com.xmzs.system.domain.ChatMessage; + + +/** + * 聊天消息Mapper接口 + * + * @author Lion Li + * @date 2023-11-26 + */ +public interface ChatMessageMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatTokenMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatTokenMapper.java new file mode 100644 index 00000000..afc2f10d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/ChatTokenMapper.java @@ -0,0 +1,16 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.ChatToken; +import com.xmzs.system.domain.vo.ChatTokenVo; + + +/** + * 聊天消息Mapper接口 + * + * @author Lion Li + * @date 2023-11-26 + */ +public interface ChatTokenMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/PaymentOrdersMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/PaymentOrdersMapper.java new file mode 100644 index 00000000..2dcb7abc --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/PaymentOrdersMapper.java @@ -0,0 +1,15 @@ +package com.xmzs.system.mapper; + +import com.xmzs.system.domain.PaymentOrders; +import com.xmzs.system.domain.vo.PaymentOrdersVo; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 支付订单Mapper接口 + * + * @author Lion Li + * @date 2023-12-29 + */ +public interface PaymentOrdersMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysConfigMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysConfigMapper.java new file mode 100644 index 00000000..c34500d0 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysConfigMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysConfig; +import com.xmzs.system.domain.vo.SysConfigVo; + +/** + * 参数配置 数据层 + * + * @author Lion Li + */ +public interface SysConfigMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDeptMapper.java new file mode 100644 index 00000000..bfd8c633 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDeptMapper.java @@ -0,0 +1,46 @@ +package com.xmzs.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysDept; +import com.xmzs.system.domain.vo.SysDeptVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author Lion Li + */ +public interface SysDeptMapper extends BaseMapperPlus { + + /** + * 查询部门管理数据 + * + * @param queryWrapper 查询条件 + * @return 部门信息集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id") + }) + List selectDeptList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id") + }) + SysDeptVo selectDeptById(Long deptId); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictDataMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictDataMapper.java new file mode 100644 index 00000000..dc2c687e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictDataMapper.java @@ -0,0 +1,25 @@ +package com.xmzs.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.system.domain.SysDictData; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.vo.SysDictDataVo; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictDataMapper extends BaseMapperPlus { + + default List selectDictDataByType(String dictType) { + return selectVoList( + new LambdaQueryWrapper() + .eq(SysDictData::getStatus, UserConstants.DICT_NORMAL) + .eq(SysDictData::getDictType, dictType) + .orderByAsc(SysDictData::getDictSort)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictTypeMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictTypeMapper.java new file mode 100644 index 00000000..72fc12ff --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.system.domain.SysDictType; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.vo.SysDictTypeVo; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictTypeMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysLogininforMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysLogininforMapper.java new file mode 100644 index 00000000..6421f901 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysLogininforMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysLogininfor; +import com.xmzs.system.domain.vo.SysLogininforVo; + +/** + * 系统访问日志情况信息 数据层 + * + * @author Lion Li + */ +public interface SysLogininforMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysMenuMapper.java new file mode 100644 index 00000000..1bf6e74e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysMenuMapper.java @@ -0,0 +1,83 @@ +package com.xmzs.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.system.domain.SysMenu; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.vo.SysMenuVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author Lion Li + */ +public interface SysMenuMapper extends BaseMapperPlus { + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param queryWrapper 查询条件 + * @return 菜单列表 + */ + List selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + List selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + default List selectMenuTreeAll() { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU) + .eq(SysMenu::getStatus, UserConstants.MENU_NORMAL) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum); + return this.selectList(lqw); + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysNoticeMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysNoticeMapper.java new file mode 100644 index 00000000..9b36c0ad --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysNoticeMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysNotice; +import com.xmzs.system.domain.vo.SysNoticeVo; + +/** + * 通知公告表 数据层 + * + * @author Lion Li + */ +public interface SysNoticeMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOperLogMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOperLogMapper.java new file mode 100644 index 00000000..5b82d564 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOperLogMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysOperLog; +import com.xmzs.system.domain.vo.SysOperLogVo; + +/** + * 操作日志 数据层 + * + * @author Lion Li + */ +public interface SysOperLogMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssConfigMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssConfigMapper.java new file mode 100644 index 00000000..d07cf22c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssConfigMapper.java @@ -0,0 +1,16 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysOssConfig; +import com.xmzs.system.domain.vo.SysOssConfigVo; + +/** + * 对象存储配置Mapper接口 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +public interface SysOssConfigMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssMapper.java new file mode 100644 index 00000000..58503949 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysOssMapper.java @@ -0,0 +1,13 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysOss; +import com.xmzs.system.domain.vo.SysOssVo; + +/** + * 文件上传 数据层 + * + * @author Lion Li + */ +public interface SysOssMapper extends BaseMapperPlus { +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysPostMapper.java new file mode 100644 index 00000000..851c7b9c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysPostMapper.java @@ -0,0 +1,32 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysPost; +import com.xmzs.system.domain.vo.SysPostVo; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author Lion Li + */ +public interface SysPostMapper extends BaseMapperPlus { + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + List selectPostsByUserName(String userName); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 00000000..9dc642c9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,13 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleDeptMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMapper.java new file mode 100644 index 00000000..09cffc21 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMapper.java @@ -0,0 +1,68 @@ +package com.xmzs.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysRole; +import com.xmzs.system.domain.vo.SysRoleVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) + Page selectPageRoleList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询角色数据 + * + * @param queryWrapper 查询条件 + * @return 角色数据集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) + List selectRoleList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) + SysRoleVo selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolePermissionByUserId(Long userId); + + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + List selectRolesByUserName(String userName); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 00000000..0bdb335c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,13 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMenuMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantMapper.java new file mode 100644 index 00000000..ebe0174a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.system.domain.SysTenant; +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 租户Mapper接口 + * + * @author Michelle.Chung + */ +public interface SysTenantMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantPackageMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantPackageMapper.java new file mode 100644 index 00000000..8e5e2cd6 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysTenantPackageMapper.java @@ -0,0 +1,14 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysTenantPackage; +import com.xmzs.system.domain.vo.SysTenantPackageVo; + +/** + * 租户套餐Mapper接口 + * + * @author Michelle.Chung + */ +public interface SysTenantPackageMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserMapper.java new file mode 100644 index 00000000..e7ba4996 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserMapper.java @@ -0,0 +1,165 @@ +package com.xmzs.system.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.mybatis.annotation.DataColumn; +import com.xmzs.common.mybatis.annotation.DataPermission; +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysUserVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author Lion Li + */ +public interface SysUserMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询用户列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectAllocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + SysUserVo selectUserByUserName(String userName); + + /** + * 通过OpenId查询用户 + * + * @param OpenId 微信用户唯一标识 + * @return 用户对象信息 + */ + SysUserVo selectUserByOpenId(String OpenId); + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + SysUserVo selectUserByPhonenumber(String phonenumber); + + /** + * 通过邮箱查询用户 + * + * @param email 邮箱 + * @return 用户对象信息 + */ + SysUserVo selectUserByEmail(String email); + + /** + * 通过用户名查询用户(不走租户插件) + * + * @param userName 用户名 + * @param tenantId 租户id + * @return 用户对象信息 + */ + @InterceptorIgnore(tenantLine = "true") + SysUserVo selectTenantUserByUserName(String userName, String tenantId); + + /** + * 通过手机号查询用户(不走租户插件) + * + * @param phonenumber 手机号 + * @param tenantId 租户id + * @return 用户对象信息 + */ + @InterceptorIgnore(tenantLine = "true") + SysUserVo selectTenantUserByPhonenumber(String phonenumber, String tenantId); + + /** + * 通过邮箱查询用户(不走租户插件) + * + * @param email 邮箱 + * @param tenantId 租户id + * @return 用户对象信息 + */ + @InterceptorIgnore(tenantLine = "true") + SysUserVo selectTenantUserByEmail(String email, String tenantId); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ +// @DataPermission({ +// @DataColumn(key = "deptName", value = "d.dept_id"), +// @DataColumn(key = "userName", value = "u.user_id") +// }) + @InterceptorIgnore(dataPermission = "true") + SysUserVo selectUserById(Long userId); + + @Override +// @DataPermission({ +// @DataColumn(key = "deptName", value = "dept_id"), +// @DataColumn(key = "userName", value = "user_id") +// }) + @InterceptorIgnore(dataPermission = "true") + int update(@Param(Constants.ENTITY) SysUser user, @Param(Constants.WRAPPER) Wrapper updateWrapper); + + @Override +// @DataPermission({ +// @DataColumn(key = "deptName", value = "dept_id"), +// @DataColumn(key = "userName", value = "user_id") +// }) + @InterceptorIgnore(dataPermission = "true") + int updateById(@Param(Constants.ENTITY) SysUser user); + + + /** + * 小程序 -修改用户信息 + * + * @param user + * + */ + void updateXcxUser(SysUserBo user); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserPostMapper.java new file mode 100644 index 00000000..215698ae --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserPostMapper.java @@ -0,0 +1,13 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserPostMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserRoleMapper.java new file mode 100644 index 00000000..241cc52b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,17 @@ +package com.xmzs.system.mapper; + +import com.xmzs.common.mybatis.core.mapper.BaseMapperPlus; +import com.xmzs.system.domain.SysUserRole; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserRoleMapper extends BaseMapperPlus { + + List selectUserIdsByRoleId(Long roleId); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/runner/SystemApplicationRunner.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/runner/SystemApplicationRunner.java new file mode 100644 index 00000000..418598a0 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/runner/SystemApplicationRunner.java @@ -0,0 +1,28 @@ +package com.xmzs.system.runner; + +import com.xmzs.system.service.ISysOssConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +/** + * 初始化 system 模块对应业务数据 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class SystemApplicationRunner implements ApplicationRunner { + + private final ISysOssConfigService ossConfigService; + + @Override + public void run(ApplicationArguments args) throws Exception { + ossConfigService.init(); + log.info("初始化OSS配置成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ChatService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ChatService.java new file mode 100644 index 00000000..f12c1d39 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ChatService.java @@ -0,0 +1,27 @@ +package com.xmzs.system.service; + +import com.xmzs.system.domain.bo.ChatMessageBo; + +/** + * @author hncboy + * @date 2023/3/22 19:41 + * 聊天相关业务接口 + */ +public interface ChatService { + + + /** + * 根据消耗的tokens扣除余额 + * + * @param chatMessageBo + * @return 结果 + */ + + void deductToken(ChatMessageBo chatMessageBo); + + /** + * 扣除用户的余额 + * + */ + void deductUserBalance(Long userId, Double numberCost); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatMessageService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatMessageService.java new file mode 100644 index 00000000..572691b1 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatMessageService.java @@ -0,0 +1,50 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; + +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.domain.vo.ChatMessageVo; + + +import java.util.Collection; +import java.util.List; + +/** + * 聊天消息Service接口 + * + * @author Lion Li + * @date 2023-11-26 + */ +public interface IChatMessageService { + + /** + * 查询聊天消息 + */ + ChatMessageVo queryById(Long id); + + /** + * 查询聊天消息列表 + */ + TableDataInfo queryPageList(ChatMessageBo bo, PageQuery pageQuery); + + /** + * 查询聊天消息列表 + */ + List queryList(ChatMessageBo bo); + + /** + * 新增聊天消息 + */ + Boolean insertByBo(ChatMessageBo bo); + + /** + * 修改聊天消息 + */ + Boolean updateByBo(ChatMessageBo bo); + + /** + * 校验并批量删除聊天消息信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatTokenService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatTokenService.java new file mode 100644 index 00000000..5e168ca0 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IChatTokenService.java @@ -0,0 +1,25 @@ +package com.xmzs.system.service; + +import com.xmzs.system.domain.ChatToken; + +/** + * 聊天消息Service接口 + * + * @author Lion Li + * @date 2023-11-26 + */ +public interface IChatTokenService { + + /** + * 查询用户token + */ + ChatToken queryByUserId(Long userId,String modelName); + + /** + * 清空用户token + */ + void resetToken(Long userId,String modelName); + + void editToken(ChatToken chatToken); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IPaymentOrdersService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IPaymentOrdersService.java new file mode 100644 index 00000000..bfc07d1a --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/IPaymentOrdersService.java @@ -0,0 +1,48 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.PaymentOrdersBo; +import com.xmzs.system.domain.vo.PaymentOrdersVo; + +import java.util.Collection; +import java.util.List; + +/** + * 支付订单Service接口 + * + * @author Lion Li + * @date 2023-12-29 + */ +public interface IPaymentOrdersService { + + /** + * 查询支付订单 + */ + PaymentOrdersVo queryById(Long id); + + /** + * 查询支付订单列表 + */ + TableDataInfo queryPageList(PaymentOrdersBo bo, PageQuery pageQuery); + + /** + * 查询支付订单列表 + */ + List queryList(PaymentOrdersBo bo); + + /** + * 新增支付订单 + */ + Boolean insertByBo(PaymentOrdersBo bo); + + /** + * 修改支付订单 + */ + Boolean updateByBo(PaymentOrdersBo bo); + + /** + * 校验并批量删除支付订单信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysConfigService.java new file mode 100644 index 00000000..3fd46852 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysConfigService.java @@ -0,0 +1,87 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysConfigBo; +import com.xmzs.system.domain.vo.SysConfigVo; + +import java.util.List; + +/** + * 参数配置 服务层 + * + * @author Lion Li + */ +public interface ISysConfigService { + + + TableDataInfo selectPageConfigList(SysConfigBo config, PageQuery pageQuery); + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + SysConfigVo selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + String selectConfigByKey(String configKey); + + /** + * 获取注册开关 + * @param tenantId 租户id + * @return true开启,false关闭 + */ + boolean selectRegisterEnabled(String tenantId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + List selectConfigList(SysConfigBo config); + + /** + * 新增参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + String insertConfig(SysConfigBo bo); + + /** + * 修改参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + String updateConfig(SysConfigBo bo); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + void deleteConfigByIds(Long[] configIds); + + /** + * 重置参数缓存数据 + */ + void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + boolean checkConfigKeyUnique(SysConfigBo config); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDataScopeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDataScopeService.java new file mode 100644 index 00000000..3224f95f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDataScopeService.java @@ -0,0 +1,26 @@ +package com.xmzs.system.service; + +/** + * 通用 数据权限 服务 + * + * @author Lion Li + */ +public interface ISysDataScopeService { + + /** + * 获取角色自定义权限 + * + * @param roleId 角色id + * @return 部门id组 + */ + String getRoleCustom(Long roleId); + + /** + * 获取部门及以下权限 + * + * @param deptId 部门id + * @return 部门id组 + */ + String getDeptAndChild(Long deptId); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDeptService.java new file mode 100644 index 00000000..41449c39 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDeptService.java @@ -0,0 +1,118 @@ +package com.xmzs.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.xmzs.system.domain.SysDept; +import com.xmzs.system.domain.bo.SysDeptBo; +import com.xmzs.system.domain.vo.SysDeptVo; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author Lion Li + */ +public interface ISysDeptService { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + List selectDeptList(SysDeptBo dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + List> selectDeptTreeList(SysDeptBo dept); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + List> buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + SysDeptVo selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + long selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + boolean checkDeptNameUnique(SysDeptBo dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + int insertDept(SysDeptBo bo); + + /** + * 修改保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + int updateDept(SysDeptBo bo); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + int deleteDeptById(Long deptId); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictDataService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictDataService.java new file mode 100644 index 00000000..8894e46d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictDataService.java @@ -0,0 +1,67 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysDictDataBo; +import com.xmzs.system.domain.vo.SysDictDataVo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictDataService { + + + TableDataInfo selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery); + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + List selectDictDataList(SysDictDataBo dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + SysDictDataVo selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + List insertDictData(SysDictDataBo bo); + + /** + * 修改保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + List updateDictData(SysDictDataBo bo); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictTypeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictTypeService.java new file mode 100644 index 00000000..513a9538 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysDictTypeService.java @@ -0,0 +1,95 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysDictTypeBo; +import com.xmzs.system.domain.vo.SysDictDataVo; +import com.xmzs.system.domain.vo.SysDictTypeVo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictTypeService { + + + TableDataInfo selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery); + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + List selectDictTypeList(SysDictTypeBo dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + SysDictTypeVo selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + SysDictTypeVo selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + void deleteDictTypeByIds(Long[] dictIds); + + /** + * 重置字典缓存数据 + */ + void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + List insertDictType(SysDictTypeBo bo); + + /** + * 修改保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + List updateDictType(SysDictTypeBo bo); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + boolean checkDictTypeUnique(SysDictTypeBo dictType); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysLogininforService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysLogininforService.java new file mode 100644 index 00000000..a0ff962e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysLogininforService.java @@ -0,0 +1,47 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysLogininforBo; +import com.xmzs.system.domain.vo.SysLogininforVo; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author Lion Li + */ +public interface ISysLogininforService { + + + TableDataInfo selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery); + + /** + * 新增系统登录日志 + * + * @param bo 访问日志对象 + */ + void insertLogininfor(SysLogininforBo bo); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + List selectLogininforList(SysLogininforBo logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + void cleanLogininfor(); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysMenuService.java new file mode 100644 index 00000000..8b687eee --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysMenuService.java @@ -0,0 +1,147 @@ +package com.xmzs.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.xmzs.system.domain.SysMenu; +import com.xmzs.system.domain.bo.SysMenuBo; +import com.xmzs.system.domain.vo.RouterVo; +import com.xmzs.system.domain.vo.SysMenuVo; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author Lion Li + */ +public interface ISysMenuService { + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(SysMenuBo menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(Long roleId); + + /** + * 根据租户套餐ID查询菜单树信息 + * + * @param packageId 租户套餐ID + * @return 选中菜单列表 + */ + List selectMenuListByPackageId(Long packageId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + List buildMenus(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + List> buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + SysMenuVo selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + int insertMenu(SysMenuBo bo); + + /** + * 修改保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + int updateMenu(SysMenuBo bo); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + boolean checkMenuNameUnique(SysMenuBo menu); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysNoticeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysNoticeService.java new file mode 100644 index 00000000..87295ece --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysNoticeService.java @@ -0,0 +1,67 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysNoticeBo; +import com.xmzs.system.domain.vo.SysNoticeVo; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author Lion Li + */ +public interface ISysNoticeService { + + + TableDataInfo selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery); + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + SysNoticeVo selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + List selectNoticeList(SysNoticeBo notice); + + /** + * 新增公告 + * + * @param bo 公告信息 + * @return 结果 + */ + int insertNotice(SysNoticeBo bo); + + /** + * 修改公告 + * + * @param bo 公告信息 + * @return 结果 + */ + int updateNotice(SysNoticeBo bo); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOperLogService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOperLogService.java new file mode 100644 index 00000000..b0781ba5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOperLogService.java @@ -0,0 +1,54 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysOperLogBo; +import com.xmzs.system.domain.vo.SysOperLogVo; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author Lion Li + */ +public interface ISysOperLogService { + + TableDataInfo selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery); + + /** + * 新增操作日志 + * + * @param bo 操作日志对象 + */ + void insertOperlog(SysOperLogBo bo); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + List selectOperLogList(SysOperLogBo operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + SysOperLogVo selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + void cleanOperLog(); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssConfigService.java new file mode 100644 index 00000000..c73f54bd --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssConfigService.java @@ -0,0 +1,65 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysOssConfigBo; +import com.xmzs.system.domain.vo.SysOssConfigVo; + +import java.util.Collection; + +/** + * 对象存储配置Service接口 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +public interface ISysOssConfigService { + + /** + * 初始化OSS配置 + */ + void init(); + + /** + * 查询单个 + */ + SysOssConfigVo queryById(Long ossConfigId); + + /** + * 查询列表 + */ + TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); + + + /** + * 根据新增业务对象插入对象存储配置 + * + * @param bo 对象存储配置新增业务对象 + * @return + */ + Boolean insertByBo(SysOssConfigBo bo); + + /** + * 根据编辑业务对象修改对象存储配置 + * + * @param bo 对象存储配置编辑业务对象 + * @return + */ + Boolean updateByBo(SysOssConfigBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 启用停用状态 + */ + int updateOssConfigStatus(SysOssConfigBo bo); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssService.java new file mode 100644 index 00000000..8237b06b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysOssService.java @@ -0,0 +1,33 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysOssBo; +import com.xmzs.system.domain.vo.SysOssVo; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +/** + * 文件上传 服务层 + * + * @author Lion Li + */ +public interface ISysOssService { + + TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + + List listByIds(Collection ossIds); + + SysOssVo getById(Long ossId); + + SysOssVo upload(MultipartFile file); + + void download(Long ossId, HttpServletResponse response) throws IOException; + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPermissionService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPermissionService.java new file mode 100644 index 00000000..953f76b2 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPermissionService.java @@ -0,0 +1,28 @@ +package com.xmzs.system.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author Lion Li + */ +public interface ISysPermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPostService.java new file mode 100644 index 00000000..13e2ce23 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysPostService.java @@ -0,0 +1,106 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.bo.SysPostBo; +import com.xmzs.system.domain.vo.SysPostVo; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author Lion Li + */ +public interface ISysPostService { + + + TableDataInfo selectPagePostList(SysPostBo post, PageQuery pageQuery); + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + List selectPostList(SysPostBo post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + SysPostVo selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + boolean checkPostNameUnique(SysPostBo post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + boolean checkPostCodeUnique(SysPostBo post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + long countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + int insertPost(SysPostBo bo); + + /** + * 修改保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + int updatePost(SysPostBo bo); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysRoleService.java new file mode 100644 index 00000000..a9f7eeb7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysRoleService.java @@ -0,0 +1,183 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.SysUserRole; +import com.xmzs.system.domain.bo.SysRoleBo; +import com.xmzs.system.domain.vo.SysRoleVo; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author Lion Li + */ +public interface ISysRoleService { + + + TableDataInfo selectPageRoleList(SysRoleBo role, PageQuery pageQuery); + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + List selectRoleList(SysRoleBo role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + SysRoleVo selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + boolean checkRoleNameUnique(SysRoleBo role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + boolean checkRoleKeyUnique(SysRoleBo role); + + /** + * 校验角色是否允许操作 + * + * @param roleId 角色ID + */ + void checkRoleAllowed(Long roleId); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + long countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int insertRole(SysRoleBo bo); + + /** + * 修改保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int updateRole(SysRoleBo bo); + + /** + * 修改角色状态 + * + * @param roleId 角色ID + * @param status 角色状态 + * @return 结果 + */ + int updateRoleStatus(Long roleId, String status); + + /** + * 修改数据权限信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int authDataScope(SysRoleBo bo); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + int insertAuthUsers(Long roleId, Long[] userIds); + + void cleanOnlineUserByRole(Long roleId); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantPackageService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantPackageService.java new file mode 100644 index 00000000..94dc993d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantPackageService.java @@ -0,0 +1,57 @@ +package com.xmzs.system.service; + +import com.xmzs.system.domain.vo.SysTenantPackageVo; +import com.xmzs.system.domain.bo.SysTenantPackageBo; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 租户套餐Service接口 + * + * @author Michelle.Chung + */ +public interface ISysTenantPackageService { + + /** + * 查询租户套餐 + */ + SysTenantPackageVo queryById(Long packageId); + + /** + * 查询租户套餐列表 + */ + TableDataInfo queryPageList(SysTenantPackageBo bo, PageQuery pageQuery); + + /** + * 查询租户套餐已启用列表 + */ + List selectList(); + + /** + * 查询租户套餐列表 + */ + List queryList(SysTenantPackageBo bo); + + /** + * 新增租户套餐 + */ + Boolean insertByBo(SysTenantPackageBo bo); + + /** + * 修改租户套餐 + */ + Boolean updateByBo(SysTenantPackageBo bo); + + /** + * 修改套餐状态 + */ + int updatePackageStatus(SysTenantPackageBo bo); + + /** + * 校验并批量删除租户套餐信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantService.java new file mode 100644 index 00000000..fac850c4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysTenantService.java @@ -0,0 +1,82 @@ +package com.xmzs.system.service; + +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.system.domain.bo.SysTenantBo; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 租户Service接口 + * + * @author Michelle.Chung + */ +public interface ISysTenantService { + + /** + * 查询租户 + */ + SysTenantVo queryById(Long id); + + /** + * 基于租户ID查询租户 + */ + SysTenantVo queryByTenantId(String tenantId); + + /** + * 查询租户列表 + */ + TableDataInfo queryPageList(SysTenantBo bo, PageQuery pageQuery); + + /** + * 查询租户列表 + */ + List queryList(SysTenantBo bo); + + /** + * 新增租户 + */ + Boolean insertByBo(SysTenantBo bo); + + /** + * 修改租户 + */ + Boolean updateByBo(SysTenantBo bo); + + /** + * 修改租户状态 + */ + int updateTenantStatus(SysTenantBo bo); + + /** + * 校验租户是否允许操作 + */ + void checkTenantAllowed(String tenantId); + + /** + * 校验并批量删除租户信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 校验企业名称是否唯一 + */ + boolean checkCompanyNameUnique(SysTenantBo bo); + + /** + * 校验账号余额 + */ + boolean checkAccountBalance(String tenantId); + + /** + * 校验有效期 + */ + boolean checkExpireTime(String tenantId); + + /** + * 同步租户套餐 + */ + Boolean syncTenantPackage(String tenantId, String packageId); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysUserService.java new file mode 100644 index 00000000..bfc22bd3 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/ISysUserService.java @@ -0,0 +1,230 @@ +package com.xmzs.system.service; + +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysUserVo; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author Lion Li + */ +public interface ISysUserService { + + + TableDataInfo selectPageUserList(SysUserBo user, PageQuery pageQuery); + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + List selectUserList(SysUserBo user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + TableDataInfo selectUnallocatedList(SysUserBo user, PageQuery pageQuery); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + SysUserVo selectUserByUserName(String userName); + + /** + * 通过OpenId查询用户 + * + * @Date 2023/5/18 + * @param openId + * @return SysUserVo + **/ + SysUserVo selectUserByOpenId(String openId); + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + SysUserVo selectUserByPhonenumber(String phonenumber); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + SysUserVo selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkUserNameUnique(SysUserBo user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkPhoneUnique(SysUserBo user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkEmailUnique(SysUserBo user); + + /** + * 校验用户是否允许操作 + * + * @param userId 用户ID + */ + void checkUserAllowed(Long userId); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int insertUser(SysUserBo user); + + /** + * 注册用户信息 + * + * @Date 2023/5/18 + * @param user + * @param tenantId + * @return SysUser + **/ + SysUser registerUser(SysUserBo user, String tenantId); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUser(SysUserBo user); + + /** + * 小程序 - 修改用户信息 + * + * @param user 用户信息 + * + */ + SysUserVo updateXcxUser(SysUserBo user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param userId 用户ID + * @param status 帐号状态 + * @return 结果 + */ + int updateUserStatus(Long userId, String status); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUserProfile(SysUserBo user); + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + boolean updateUserAvatar(Long userId, String avatar); + + boolean updateUserName(Long userId, String nickName); + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + + int resetUserPwd(Long userId, String password); + + + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SseService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SseService.java new file mode 100644 index 00000000..185d0053 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SseService.java @@ -0,0 +1,49 @@ +package com.xmzs.system.service; + + +import com.xmzs.common.chat.domain.request.ChatRequest; +import com.xmzs.common.chat.domain.request.Dall3Request; +import com.xmzs.common.chat.entity.images.Item; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.List; + +/** + * 描述: + * + * @author https:www.unfbx.com + * @date 2023-04-08 + */ +public interface SseService { + + /** + * 客户端发送消息到服务端 + * @param chatRequest + */ + SseEmitter sseChat(ChatRequest chatRequest); + + /** + * 绘画接口 + * @param request + */ + List dall3(Dall3Request request); + + + /** + * mj绘画接口 + */ + void mjTask(); + + /** + * 中转接口 + */ + SseEmitter transitChat(ChatRequest chatRequest); + + /** + * azure 聊天接口 + * + * @param chatRequest + * @return + */ + SseEmitter azureChat(ChatRequest chatRequest); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysLoginService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysLoginService.java new file mode 100644 index 00000000..29dbacba --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysLoginService.java @@ -0,0 +1,418 @@ +package com.xmzs.system.service; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.util.WxMaConfigHolder; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.secure.BCrypt; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.domain.dto.RoleDTO; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.domain.model.VisitorLoginBody; +import com.xmzs.common.core.domain.model.VisitorLoginUser; +import com.xmzs.common.core.enums.*; +import com.xmzs.common.core.exception.user.CaptchaException; +import com.xmzs.common.core.exception.user.CaptchaExpireException; +import com.xmzs.common.core.exception.user.UserException; +import com.xmzs.common.core.utils.*; +import com.xmzs.common.log.event.LogininforEvent; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.tenant.exception.TenantException; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.mapper.SysUserMapper; +import com.xmzs.web.domain.vo.LoginVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.Date; +import java.util.List; +import java.util.function.Supplier; + +/** + * 登录校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLoginService { + + private final SysUserMapper userMapper; + private final ISysPermissionService permissionService; + private final ISysTenantService tenantService; + private final WxMaService wxMaService; + private final ISysUserService userService; + + @Value("${user.password.maxRetryCount}") + private Integer maxRetryCount; + + @Value("${user.password.lockTime}") + private Integer lockTime; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String tenantId, String username, String password, String code, String uuid) { + SysUserVo user = loadUserByUsername(tenantId, username); + checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.PC); + + recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId()); + return StpUtil.getTokenValue(); + } + + public String smsLogin(String tenantId, String phonenumber, String smsCode) { + // 校验租户 + checkTenant(tenantId); + // 通过手机号查找用户 + SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber); + + checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.APP); + + recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId()); + return StpUtil.getTokenValue(); + } + + public String emailLogin(String tenantId, String email, String emailCode) { + // 校验租户 + checkTenant(tenantId); + // 通过手机号查找用户 + SysUserVo user = loadUserByEmail(tenantId, email); + + checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.APP); + + recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId()); + return StpUtil.getTokenValue(); + } + + + /** + * 游客登录 + * + * @param loginBody + * @return String + * @Date 2023/5/18 + **/ + public LoginVo visitorLogin(VisitorLoginBody loginBody) { + String openid = ""; + // PC端游客登录 + if(LoginUserType.PC.getCode().equals(loginBody.getType())){ + openid = loginBody.getCode(); + }else { + // 小程序匿名登录 + try { + WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(loginBody.getCode()); + openid = session.getOpenid(); + } catch ( + WxErrorException e) { + log.error(e.getMessage(), e); + } finally { + // 清理ThreadLocal + WxMaConfigHolder.remove(); + } + } + // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 + SysUserVo user = userService.selectUserByOpenId(openid); + VisitorLoginUser loginUser = new VisitorLoginUser(); + if (ObjectUtil.isNull(user)) { + SysUserBo sysUser = new SysUserBo(); + // 改为自增 + String name = "游客" + RandomUtil.randomInt(10000); + // 设置默认用户名 + sysUser.setUserName(name); + // 设置默认昵称 + sysUser.setNickName(name); + // 设置默认密码 + sysUser.setPassword(BCrypt.hashpw("123456")); + // 设置微信openId + sysUser.setOpenId(openid); + // 注册用户,设置默认租户为0 + SysUser registerUser = userService.registerUser(sysUser, "0"); + // 构建登录用户信息 + loginUser.setTenantId("0"); + loginUser.setUserId(registerUser.getUserId()); + loginUser.setUsername(registerUser.getUserName()); + loginUser.setUserType(UserType.APP_USER.getUserType()); + loginUser.setOpenid(openid); + loginUser.setNickName(registerUser.getNickName()); + } else { + // 此处可根据登录用户的数据不同 自行创建 loginUser + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(user.getUserId()); + loginUser.setUsername(user.getUserName()); + loginUser.setUserType(user.getUserType()); + loginUser.setNickName(user.getNickName()); + loginUser.setAvatar(user.getWxAvatar()); + loginUser.setOpenid(openid); + } + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.XCX); + recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + LoginVo loginVo = new LoginVo(); + // 生成令牌 + loginVo.setToken(StpUtil.getTokenValue()); + loginVo.setUserInfo(loginUser); + return loginVo; + } + + /** + * 退出登录 + */ + public void logout() { + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { + // 超级管理员 登出清除动态租户 + TenantHelper.clearDynamic(); + } + StpUtil.logout(); + recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); + } catch (NotLoginException ignored) { + } + } + + /** + * 记录登录信息 + * + * @param tenantId 租户ID + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + */ + private void recordLogininfor(String tenantId, String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); + } + + /** + * 校验邮箱验证码 + */ + private boolean validateEmailCode(String tenantId, String email, String emailCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); + if (StringUtils.isBlank(code)) { + recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(emailCode); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + public void validateCaptcha(String tenantId, String username, String code, String uuid) { + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + private SysUserVo loadUserByUsername(String tenantId, String username) { + + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getUserName, username)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UserException("user.not.exists", username); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UserException("user.blocked", username); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByUserName(username, tenantId); + } + return userMapper.selectUserByUserName(username); + } + + private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); + throw new UserException("user.not.exists", phonenumber); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByPhonenumber(phonenumber, tenantId); + } + return userMapper.selectUserByPhonenumber(phonenumber); + } + + private SysUserVo loadUserByEmail(String tenantId, String email) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getEmail, email)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", email); + throw new UserException("user.not.exists", email); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", email); + throw new UserException("user.blocked", email); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByEmail(email, tenantId); + } + return userMapper.selectUserByEmail(email); + } + + /** + * 构建登录用户 + */ + private LoginUser buildLoginUser(SysUserVo user) { + LoginUser loginUser = new LoginUser(); + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(user.getUserId()); + loginUser.setDeptId(user.getDeptId()); + loginUser.setUsername(user.getUserName()); + loginUser.setAvatar(user.getAvatar()); + loginUser.setUserType(user.getUserType()); + loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); + loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); + loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); + List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); + loginUser.setRoles(roles); + return loginUser; + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(ServletUtils.getClientIP()); + sysUser.setLoginDate(DateUtils.getNowDate()); + sysUser.setUpdateBy(userId); + userMapper.updateById(sysUser); + } + + /** + * 登录校验 + */ + private void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { + String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username; + String loginFail = Constants.LOGIN_FAIL; + + // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip) + Integer errorNumber = RedisUtils.getCacheObject(errorKey); + // 锁定时间内登录 则踢出 + if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) { + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } + + if (supplier.get()) { + // 是否第一次 + errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; + // 达到规定错误次数 则锁定登录 + if (errorNumber.equals(maxRetryCount)) { + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } else { + // 未达到规定错误次数 则递增 + RedisUtils.setCacheObject(errorKey, errorNumber); + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); + throw new UserException(loginType.getRetryLimitCount(), errorNumber); + } + } + + // 登录成功 清空错误次数 + RedisUtils.deleteObject(errorKey); + } + + private void checkTenant(String tenantId) { + if (!TenantHelper.isEnable()) { + return; + } + if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + return; + } + SysTenantVo tenant = tenantService.queryByTenantId(tenantId); + if (ObjectUtil.isNull(tenant)) { + log.info("登录租户:{} 不存在.", tenantId); + throw new TenantException("tenant.not.exists"); + } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) { + log.info("登录租户:{} 已被停用.", tenantId); + throw new TenantException("tenant.blocked"); + } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) + && new Date().after(tenant.getExpireTime())) { + log.info("登录租户:{} 已超过有效期.", tenantId); + throw new TenantException("tenant.expired"); + } + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysRegisterService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysRegisterService.java new file mode 100644 index 00000000..985e8a5d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/SysRegisterService.java @@ -0,0 +1,145 @@ +package com.xmzs.system.service; + +import cn.dev33.satoken.secure.BCrypt; +import com.xmzs.common.chat.constant.OpenAIConst; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.constant.GlobalConstants; +import com.xmzs.common.core.domain.model.RegisterBody; +import com.xmzs.common.core.exception.base.BaseException; +import com.xmzs.common.core.exception.user.CaptchaException; +import com.xmzs.common.core.exception.user.CaptchaExpireException; +import com.xmzs.common.core.exception.user.UserException; +import com.xmzs.common.core.utils.MessageUtils; +import com.xmzs.common.core.utils.ServletUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.log.event.LogininforEvent; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.SysUserRole; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.mapper.SysUserRoleMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +/** + * 注册校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRegisterService { + + private final ISysUserService userService; + + private final SysUserRoleMapper userRoleMapper; + + /** + * 注册 + */ + public void register(RegisterBody registerBody) { + String tenantId = Constants.TENANT_ID; + if(StringUtils.isNotBlank(registerBody.getTenantId())){ + tenantId = registerBody.getTenantId(); + } + String username = registerBody.getUsername(); + String password = registerBody.getPassword(); + // 检查验证码是否正确 + validateEmail(username,registerBody.getCode()); + SysUserBo sysUser = new SysUserBo(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(BCrypt.hashpw(password)); + if (!userService.checkUserNameUnique(sysUser)) { + throw new UserException("user.register.save.error", username); + } + // 设置默认余额 + sysUser.setUserBalance(new BigDecimal(OpenAIConst.USER_BALANCE)); + SysUser user = userService.registerUser(sysUser, tenantId); + if (user == null) { + throw new UserException("user.register.error"); + } + // 设置默认角色 + SysUserRole sysRole = new SysUserRole(); + sysRole.setUserId(user.getUserId()); + sysRole.setRoleId(1L); + userRoleMapper.insert(sysRole); + recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); + } + + /** + * 重置密码 + */ + public void resetPassWord(RegisterBody registerBody) { + String username = registerBody.getUsername(); + String password = registerBody.getPassword(); + SysUserVo user = userService.selectUserByUserName(username); + if(user == null){ + throw new UserException(String.format("用户【%s】,未注册!",username)); + } + // 检查验证码是否正确 + validateEmail(username,registerBody.getCode()); + userService.resetUserPwd(user.getUserId(),BCrypt.hashpw(password)); + } + + /** + * 校验邮箱验证码 + * + * @param username 用户名 + */ + public void validateEmail(String username,String code) { + String key = GlobalConstants.CAPTCHA_CODE_KEY + username; + String captcha = RedisUtils.getCacheObject(key); + if(code.equals(captcha)){ + RedisUtils.deleteObject(captcha); + }else { + throw new BaseException("验证码错误,请重试!"); + } + } + + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + public void validateCaptcha(String tenantId, String username, String code, String uuid) { + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + /** + * 记录登录信息 + * + * @param tenantId 租户ID + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String tenantId, String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/TextReviewService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/TextReviewService.java new file mode 100644 index 00000000..3117bf9c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/TextReviewService.java @@ -0,0 +1,74 @@ +package com.xmzs.system.service; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; + +import java.io.IOException; +import java.net.URLEncoder; + + +/** + * 文本审核服务 + * + * @author: wangle + * @date: 2023/5/27 + */ +//@Service +@Slf4j +public class TextReviewService { + @Value("${baidu.textReview.apiKey}") + private String API_KEY; + @Value("${baidu.textReview.secretKey}") + private String SECRET_KEY; + + static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build(); + + /** + * 文本内容审核 + * + * @param msg + * @return String + * @Date 2023/5/27 + **/ + public String textReview(String msg) { + String conclusionType = ""; + try { + String text = URLEncoder.encode(msg); + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "text=" + text); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined?access_token=" + getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + JSONObject jsonObject = JSONObject.parseObject(response.body().string()); + conclusionType = jsonObject.getString("conclusionType"); + } catch (IOException e) { + log.info("发生错误{}", e.getMessage()); + } + return conclusionType; + } + + /** + * 从用户的AK,SK生成鉴权签名(Access Token) + * + * @return 鉴权签名(Access Token) + * @throws IOException IO异常 + */ + public String getAccessToken() throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY + + "&client_secret=" + SECRET_KEY); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/oauth/2.0/token") + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + return JSONObject.parseObject(response.body().string()).getString("access_token"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatMessageServiceImpl.java new file mode 100644 index 00000000..bed326fd --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatMessageServiceImpl.java @@ -0,0 +1,113 @@ +package com.xmzs.system.service.impl; + +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.domain.vo.ChatMessageVo; +import com.xmzs.system.mapper.ChatMessageMapper; +import com.xmzs.system.service.IChatMessageService; +import com.xmzs.system.domain.ChatMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 聊天消息Service业务层处理 + * + * @author Lion Li + * @date 2023-11-26 + */ +@RequiredArgsConstructor +@Service +public class ChatMessageServiceImpl implements IChatMessageService { + + private final ChatMessageMapper baseMapper; + + /** + * 查询聊天消息 + */ + @Override + public ChatMessageVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 查询聊天消息列表 + */ + @Override + public TableDataInfo queryPageList(ChatMessageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询聊天消息列表 + */ + @Override + public List queryList(ChatMessageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(ChatMessageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getUserId() != null, ChatMessage::getUserId, bo.getUserId()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), ChatMessage::getContent, bo.getContent()); + lqw.eq(bo.getDeductCost() != null, ChatMessage::getDeductCost, bo.getDeductCost()); + lqw.eq(bo.getTotalTokens() != null, ChatMessage::getTotalTokens, bo.getTotalTokens()); + lqw.like(StringUtils.isNotBlank(bo.getModelName()), ChatMessage::getModelName, bo.getModelName()); + return lqw; + } + + /** + * 新增聊天消息 + */ + @Override + public Boolean insertByBo(ChatMessageBo bo) { + ChatMessage add = MapstructUtils.convert(bo, ChatMessage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改聊天消息 + */ + @Override + public Boolean updateByBo(ChatMessageBo bo) { + ChatMessage update = MapstructUtils.convert(bo, ChatMessage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(ChatMessage entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除聊天消息 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatServiceImpl.java new file mode 100644 index 00000000..cb745d63 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatServiceImpl.java @@ -0,0 +1,104 @@ +package com.xmzs.system.service.impl; + + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.xmzs.common.chat.entity.chat.BaseChatCompletion; +import com.xmzs.common.chat.entity.chat.ChatCompletion; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.system.domain.ChatToken; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.mapper.SysUserMapper; +import com.xmzs.system.service.ChatService; +import com.xmzs.system.service.IChatMessageService; +import com.xmzs.system.service.IChatTokenService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author hncboy + * @date 2023/3/22 19:41 + * 聊天相关业务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ChatServiceImpl implements ChatService { + + private final SysUserMapper sysUserMapper; + + + private final IChatMessageService chatMessageService; + + private final IChatTokenService chatTokenService; + + + /** + * 根据消耗的tokens扣除余额 + * + * @param chatMessageBo + * + */ + public void deductToken(ChatMessageBo chatMessageBo) { + // 计算总token数 + ChatToken chatToken = chatTokenService.queryByUserId(chatMessageBo.getUserId(), chatMessageBo.getModelName()); + if(chatToken == null){ + chatToken = new ChatToken(); + chatToken.setToken(0); + } + int totalTokens = chatToken.getToken()+ chatMessageBo.getTotalTokens(); + // 如果总token数大于等于1000,进行费用扣除 + if (totalTokens >= 1000) { + // 计算费用 + int token1 = totalTokens / 1000; + int token2 = totalTokens % 1000; + if(token2 > 0){ + // 保存剩余tokens + chatToken.setToken(token2); + chatTokenService.editToken(chatToken); + }else { + chatTokenService.resetToken(chatMessageBo.getUserId(), chatMessageBo.getModelName()); + } + chatMessageBo.setDeductCost(token1 * ChatCompletion.getModelCost(chatMessageBo.getModelName())); + // 扣除用户余额 + deductUserBalance(chatMessageBo.getUserId(), chatMessageBo.getDeductCost()); + } else { + chatMessageBo.setDeductCost(0d); + chatMessageBo.setRemark("不满1kToken,计入下一次!"); + chatToken.setToken(totalTokens); + chatToken.setModelName(chatMessageBo.getModelName()); + chatToken.setUserId(chatMessageBo.getUserId()); + chatTokenService.editToken(chatToken); + } + + // 保存消息记录 + chatMessageService.insertByBo(chatMessageBo); + + } + + /** + * 从用户余额中扣除指定费用 + * + * @param userId 用户ID + * @param numberCost 要扣除的费用 + */ + @Override + public void deductUserBalance(Long userId, Double numberCost) { + SysUser sysUser = sysUserMapper.selectById(userId); + if (sysUser == null) { + return; + } + + Double userBalance = sysUser.getUserBalance(); + if (userBalance < numberCost) { + throw new ServiceException("余额不足,请联系管理员充值!"); + } + + sysUserMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getUserBalance, Math.max(userBalance - numberCost, 0)) + .eq(SysUser::getUserId, userId)); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatTokenServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatTokenServiceImpl.java new file mode 100644 index 00000000..23d4de05 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/ChatTokenServiceImpl.java @@ -0,0 +1,55 @@ +package com.xmzs.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xmzs.system.domain.ChatToken; +import com.xmzs.system.mapper.ChatTokenMapper; +import com.xmzs.system.service.IChatTokenService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 聊天消息Service业务层处理 + * + * @author Lion Li + * @date 2023-11-26 + */ +@RequiredArgsConstructor +@Service +public class ChatTokenServiceImpl implements IChatTokenService { + + private final ChatTokenMapper baseMapper; + + @Override + public ChatToken queryByUserId(Long userId,String modelName) { + return baseMapper.selectOne( + new LambdaQueryWrapper() + .eq(ChatToken::getUserId, userId) + .eq(ChatToken::getModelName, modelName) + .last("limit 1") + ); + } + + /** + * 清空用户token + * + */ + @Override + public void resetToken(Long userId,String modelName) { + ChatToken chatToken = queryByUserId(userId, modelName); + chatToken.setToken(0); + baseMapper.updateById(chatToken); + } + + /** + * 增加用户token + * + */ + @Override + public void editToken(ChatToken chatToken) { + if(chatToken.getId() == null){ + baseMapper.insert(chatToken); + }else { + baseMapper.updateById(chatToken); + } + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/PaymentOrdersServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/PaymentOrdersServiceImpl.java new file mode 100644 index 00000000..abae43a2 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/PaymentOrdersServiceImpl.java @@ -0,0 +1,114 @@ +package com.xmzs.system.service.impl; + +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import com.xmzs.system.domain.bo.PaymentOrdersBo; +import com.xmzs.system.domain.vo.PaymentOrdersVo; +import com.xmzs.system.domain.PaymentOrders; +import com.xmzs.system.mapper.PaymentOrdersMapper; +import com.xmzs.system.service.IPaymentOrdersService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 支付订单Service业务层处理 + * + * @author Lion Li + * @date 2023-12-29 + */ +@RequiredArgsConstructor +@Service +public class PaymentOrdersServiceImpl implements IPaymentOrdersService { + + private final PaymentOrdersMapper baseMapper; + + /** + * 查询支付订单 + */ + @Override + public PaymentOrdersVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 查询支付订单列表 + */ + @Override + public TableDataInfo queryPageList(PaymentOrdersBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询支付订单列表 + */ + @Override + public List queryList(PaymentOrdersBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PaymentOrdersBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), PaymentOrders::getOrderNo, bo.getOrderNo()); + lqw.like(StringUtils.isNotBlank(bo.getOrderName()), PaymentOrders::getOrderName, bo.getOrderName()); + lqw.eq(bo.getAmount() != null, PaymentOrders::getAmount, bo.getAmount()); + lqw.eq(StringUtils.isNotBlank(bo.getPaymentStatus()), PaymentOrders::getPaymentStatus, bo.getPaymentStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getPaymentMethod()), PaymentOrders::getPaymentMethod, bo.getPaymentMethod()); + lqw.eq(bo.getUserId() != null, PaymentOrders::getUserId, bo.getUserId()); + return lqw; + } + + /** + * 新增支付订单 + */ + @Override + public Boolean insertByBo(PaymentOrdersBo bo) { + PaymentOrders add = MapstructUtils.convert(bo, PaymentOrders.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改支付订单 + */ + @Override + public Boolean updateByBo(PaymentOrdersBo bo) { + PaymentOrders update = MapstructUtils.convert(bo, PaymentOrders.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PaymentOrders entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除支付订单 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SseServiceImpl.java new file mode 100644 index 00000000..158cf3c4 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SseServiceImpl.java @@ -0,0 +1,380 @@ +package com.xmzs.system.service.impl; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.json.JSONUtil; + +import com.azure.ai.openai.OpenAIClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.ai.openai.models.*; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.util.IterableStream; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xmzs.common.chat.config.LocalCache; +import com.xmzs.common.chat.constant.OpenAIConst; +import com.xmzs.common.chat.domain.request.ChatRequest; +import com.xmzs.common.chat.domain.request.Dall3Request; +import com.xmzs.common.chat.entity.chat.*; +import com.xmzs.common.chat.entity.images.Image; +import com.xmzs.common.chat.entity.images.ImageResponse; +import com.xmzs.common.chat.entity.images.Item; +import com.xmzs.common.chat.entity.images.ResponseFormat; +import com.xmzs.common.chat.openai.OpenAiStreamClient; +import com.xmzs.common.chat.utils.TikTokensUtil; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.exception.base.BaseException; + +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.translation.annotation.Translation; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.ChatMessageBo; +import com.xmzs.system.listener.SSEEventSourceListener; +import com.xmzs.system.mapper.SysUserMapper; +import com.xmzs.system.service.ChatService; +import com.xmzs.system.service.IChatMessageService; + +import com.xmzs.system.service.SseService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import com.azure.ai.openai.models.ImageGenerationOptions; +import com.azure.core.models.ResponseError; +import com.azure.ai.openai.OpenAIClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.ai.openai.models.ImageGenerationData; +import com.azure.ai.openai.models.ImageGenerationOptions; +import com.azure.ai.openai.models.ImageGenerations; +import com.azure.core.credential.AzureKeyCredential; +/** + * 描述: + * + * @author https:www.unfbx.com + * @date 2023-04-08 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class SseServiceImpl implements SseService { + private final OpenAiStreamClient openAiStreamClient; + + private final ChatService chatService; + + private final SysUserMapper sysUserMapper; + + private final IChatMessageService chatMessageService; + + @Value("${transit.apiKey}") + private String API_KEY; + + @Value("${transit.apiHost}") + private String API_HOST; + + private static final String DONE_SIGNAL = "[DONE]"; + + @Override + @Transactional + public SseEmitter sseChat(ChatRequest chatRequest) { + LocalCache.CACHE.put("userId",getUserId()); + SseEmitter sseEmitter = new SseEmitter(0L); + SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(sseEmitter); + checkUserGrade(sseEmitter, chatRequest.getModel()); + // 获取对话消息列表 + List msgList = chatRequest.getMessages(); + // 图文识别上下文信息 + List contentList = chatRequest.getContent(); + // 图文识别模型 + if (ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName().equals(chatRequest.getModel())) { + MessagePicture message = MessagePicture.builder().role(Message.Role.USER.getName()).content(contentList).build(); + ChatCompletionWithPicture chatCompletion = ChatCompletionWithPicture + .builder() + .messages(Collections.singletonList(message)) + .model(chatRequest.getModel()) + .temperature(chatRequest.getTemperature()) + .topP(chatRequest.getTop_p()) + .stream(true) + .build(); + openAiStreamClient.streamChatCompletion(chatCompletion, openAIEventSourceListener); + // 扣除图文对话费用 + chatService.deductUserBalance(getUserId(),OpenAIConst.GPT4_COST); + + String text = contentList.get(contentList.size() - 1).getText(); + // 保存消息记录 + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName(chatRequest.getModel()); + chatMessageBo.setContent(text); + chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST); + chatMessageBo.setTotalTokens(0); + chatMessageService.insertByBo(chatMessageBo); + } else { + ChatCompletion completion = ChatCompletion + .builder() + .messages(msgList) + .model(chatRequest.getModel()) + .temperature(chatRequest.getTemperature()) + .topP(chatRequest.getTop_p()) + .stream(true) + .build(); + openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener); + Message message = msgList.get(msgList.size() - 1); + // 扣除余额 + int tokens = TikTokensUtil.tokens(chatRequest.getModel(), msgList); + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName(chatRequest.getModel()); + chatMessageBo.setContent(message.getContent()); + chatMessageBo.setTotalTokens(tokens); + chatService.deductToken(chatMessageBo); + } + return sseEmitter; + } + + /** + * dall-e-3绘画接口 + * + * @param request + * @return + */ + public List dall3(Dall3Request request) { + checkUserGrade(null,""); + // DALL3 绘图模型 + Image image = Image.builder() + .responseFormat(ResponseFormat.URL.getName()) + .model(Image.Model.DALL_E_3.getName()) + .prompt(request.getPrompt()) + .n(1) + .quality(request.getQuality()) + .size(request.getSize()) + .style(request.getStyle()) + .build(); + ImageResponse imageResponse = openAiStreamClient.genImages(image); + + // 扣除费用 + if(Objects.equals(request.getSize(), "1792x1024") || Objects.equals(request.getSize(), "1024x1792")){ + chatService.deductUserBalance(getUserId(),OpenAIConst.DALL3_HD_COST); + }else { + chatService.deductUserBalance(getUserId(),OpenAIConst.DALL3_COST); + } + // 保存扣费记录 + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName(Image.Model.DALL_E_3.getName()); + chatMessageBo.setContent(request.getPrompt()); + chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST); + chatMessageBo.setTotalTokens(0); + chatMessageService.insertByBo(chatMessageBo); + return imageResponse.getData(); + } + + @Override + public void mjTask() { + // 检验是否是免费用户 + checkUserGrade(null,""); + chatService.deductUserBalance(getUserId(),OpenAIConst.MJ_COST); + // 保存扣费记录 + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName("mj"); + chatMessageBo.setContent("mj绘图"); + chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST); + chatMessageBo.setTotalTokens(0); + chatMessageService.insertByBo(chatMessageBo); + } + + /** + * 中转接口 + * + * @param chatRequest + * @return + */ + @Override + public SseEmitter transitChat(ChatRequest chatRequest) { + // 获取对话消息列表 + List msgList = chatRequest.getMessages(); + Message message = msgList.get(msgList.size() - 1); + SseEmitter emitter = new SseEmitter(0L); + checkUserGrade(emitter, chatRequest.getModel()); + ChatCompletion completion = ChatCompletion + .builder() + .messages(chatRequest.getMessages()) + .model(chatRequest.getModel()) + .temperature(chatRequest.getTemperature()) + .topP(chatRequest.getTop_p()) + .stream(true) + .build(); + // 启动一个新的线程来处理数据流 + new Thread(() -> { + // 启动一个新的线程来处理数据流 + try { + ObjectMapper mapper = new ObjectMapper(); + String requestBody = mapper.writeValueAsString(completion); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(API_HOST + "v1/chat/completions")) + .header("Authorization", "Bearer " + API_KEY) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + .build(); + // 发送请求并获取响应体作为InputStream + HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofInputStream()); + // 使用正确的字符编码将InputStream包装为InputStreamReader,然后创建BufferedReader + BufferedReader reader = new BufferedReader(new InputStreamReader(response.body())); + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("data: ")) { + String data = line.replace("data: ", ""); + emitter.send(data, MediaType.TEXT_PLAIN); + if (data.equals(DONE_SIGNAL)) { + //成功响应 + emitter.complete(); + } + } + } + // 关闭资源 + reader.close(); + } catch (Exception e) { + emitter.complete(); + throw new ServiceException("调用中转接口失败:"+e.getMessage()); + } + }).start(); + chatService.deductUserBalance(getUserId(),OpenAIConst.GPT4_COST); + // 保存消息记录 + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName(chatRequest.getModel()); + chatMessageBo.setContent(message.getContent()); + chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST); + chatMessageBo.setTotalTokens(0); + chatMessageService.insertByBo(chatMessageBo); + return emitter; + } + + public static void main(String[] args) { + String azureOpenaiKey = "-"; + String endpoint = "-"; + String deploymentOrModelName = "-"; + + OpenAIClient client = new OpenAIClientBuilder() + .endpoint(endpoint) + .credential(new AzureKeyCredential(azureOpenaiKey)) + .buildClient(); + + ImageGenerationOptions imageGenerationOptions = new ImageGenerationOptions( + "A drawing of the Seattle skyline in the style of Van Gogh"); + ImageGenerations images = client.getImageGenerations(deploymentOrModelName, imageGenerationOptions); + + for (ImageGenerationData imageGenerationData : images.getData()) { + System.out.printf( + "Image location URL that provides temporary access to download the generated image is %s.%n", + imageGenerationData.getUrl()); + } + } + + public SseEmitter azureChat(ChatRequest chatRequest) { + String azureOpenaiKey = "-"; + String endpoint = "-"; + String deploymentOrModelId = "-"; + OpenAIClient client = new OpenAIClientBuilder() + .endpoint(endpoint) + .credential(new AzureKeyCredential(azureOpenaiKey)) + .buildClient(); + final SseEmitter emitter = new SseEmitter(); + // 使用线程池异步执行 + ExecutorService service = Executors.newSingleThreadExecutor(); + service.execute(() -> { + try { + // 获取对话消息列表 + List chatMessages = chatRequest.getMessages(); + List messages = new ArrayList<>(); + chatMessages.forEach( + e->{ + ChatRequestMessage chatMessage; + if(Message.Role.SYSTEM.getName().equals(e.getRole())){ + chatMessage = new ChatRequestSystemMessage(e.getContent()); + }else { + chatMessage = new ChatRequestUserMessage(e.getContent()); + } + messages.add(chatMessage); + } + ); + // 获取流式响应 + IterableStream chatCompletionsStream = client.getChatCompletionsStream(deploymentOrModelId, new ChatCompletionsOptions(messages)); + + // 遍历流式响应并发送到客户端 + for (ChatCompletions chatCompletion : chatCompletionsStream) { + + if(CollectionUtil.isEmpty(chatCompletion.getChoices())){ + continue; + } + log.info("json ======{}", JSONUtil.toJsonStr(chatCompletion)); + emitter.send(chatCompletion); + } + emitter.complete(); + } catch (Exception e) { + emitter.completeWithError(e); + } + }); + return emitter; + } + + /** + * 判断用户是否付费 + */ + public void checkUserGrade(SseEmitter emitter, String model) { + SysUser sysUser = sysUserMapper.selectById(getUserId()); + if(StringUtils.isEmpty(model)){ + if("0".equals(sysUser.getUserGrade())){ + throw new ServiceException("免费用户暂时不支持此模型,请切换gpt-3.5-turbo模型或者点击《进入市场选购您的商品》充值后使用!",500); + } + } + // TODO 添加枚举 + if ("0".equals(sysUser.getUserGrade()) && !ChatCompletion.Model.GPT_3_5_TURBO.getName().equals(model)) { + // 创建并发送一个名为 "error" 的事件,带有错误消息和状态码 + SseEmitter.SseEventBuilder event = SseEmitter.event() + .name("error") // 客户端将监听这个事件名 + .data("免费用户暂时不支持此模型,请切换gpt-3.5-turbo模型或者点击《进入市场选购您的商品》充值后使用!"); + try { + emitter.send(event); + } catch (IOException e) { + throw new RuntimeException(e); + } + emitter.complete(); + } + } + + /** + * 获取用户Id + * + * @return + */ + public Long getUserId(){ + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new BaseException("用户未登录!"); + } + return loginUser.getUserId(); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 00000000..0aeb0068 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,216 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.service.ConfigService; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.system.domain.SysConfig; +import com.xmzs.system.domain.bo.SysConfigBo; +import com.xmzs.system.domain.vo.SysConfigVo; +import com.xmzs.system.mapper.SysConfigMapper; +import com.xmzs.system.service.ISysConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 参数配置 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysConfigServiceImpl implements ISysConfigService, ConfigService { + + private final SysConfigMapper baseMapper; + + @Override + public TableDataInfo selectPageConfigList(SysConfigBo config, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(config); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DS("master") + public SysConfigVo selectConfigById(Long configId) { + return baseMapper.selectVoById(configId); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") + @Override + public String selectConfigByKey(String configKey) { + SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, configKey)); + if (ObjectUtil.isNotNull(retConfig)) { + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取注册开关 + * @param tenantId 租户id + * @return true开启,false关闭 + */ + @Override + public boolean selectRegisterEnabled(String tenantId) { + SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, "sys.account.registerUser") + .eq(TenantHelper.isEnable(),SysConfig::getTenantId, tenantId)); + if (ObjectUtil.isNull(retConfig)) { + return false; + } + return Convert.toBool(retConfig.getConfigValue()); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfigBo config) { + LambdaQueryWrapper lqw = buildQueryWrapper(config); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getConfigName()), SysConfig::getConfigName, bo.getConfigName()); + lqw.eq(StringUtils.isNotBlank(bo.getConfigType()), SysConfig::getConfigType, bo.getConfigType()); + lqw.like(StringUtils.isNotBlank(bo.getConfigKey()), SysConfig::getConfigKey, bo.getConfigKey()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); + return lqw; + } + + /** + * 新增参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#bo.configKey") + @Override + public String insertConfig(SysConfigBo bo) { + SysConfig config = MapstructUtils.convert(bo, SysConfig.class); + int row = baseMapper.insert(config); + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#bo.configKey") + @Override + public String updateConfig(SysConfigBo bo) { + int row = 0; + SysConfig config = MapstructUtils.convert(bo, SysConfig.class); + if (config.getConfigId() != null) { + SysConfig temp = baseMapper.selectById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { + CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); + } + row = baseMapper.updateById(config); + } else { + row = baseMapper.update(config, new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, config.getConfigKey())); + } + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) { + for (Long configId : configIds) { + SysConfig config = baseMapper.selectById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); + } + baseMapper.deleteBatchIds(Arrays.asList(configIds)); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() { + CacheUtils.clear(CacheNames.SYS_CONFIG); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfigBo config) { + long configId = ObjectUtil.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper().eq(SysConfig::getConfigKey, config.getConfigKey())); + if (ObjectUtil.isNotNull(info) && info.getConfigId() != configId) { + return false; + } + return true; + } + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + @Override + public String getConfigValue(String configKey) { + return SpringUtils.getAopProxy(this).selectConfigByKey(configKey); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDataScopeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDataScopeServiceImpl.java new file mode 100644 index 00000000..b9166252 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDataScopeServiceImpl.java @@ -0,0 +1,61 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.xmzs.system.domain.SysDept; +import com.xmzs.common.mybatis.helper.DataBaseHelper; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.system.domain.SysRoleDept; +import com.xmzs.system.mapper.SysDeptMapper; +import com.xmzs.system.mapper.SysRoleDeptMapper; +import com.xmzs.system.service.ISysDataScopeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 数据权限 实现 + *

+ * 注意: 此Service内不允许调用标注`数据权限`注解的方法 + * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service("sdss") +public class SysDataScopeServiceImpl implements ISysDataScopeService { + + private final SysRoleDeptMapper roleDeptMapper; + private final SysDeptMapper deptMapper; + + @Override + public String getRoleCustom(Long roleId) { + List list = roleDeptMapper.selectList( + new LambdaQueryWrapper() + .select(SysRoleDept::getDeptId) + .eq(SysRoleDept::getRoleId, roleId)); + if (CollUtil.isNotEmpty(list)) { + return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); + } + return null; + } + + @Override + public String getDeptAndChild(Long deptId) { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(deptId); + List list = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .in(SysDept::getDeptId, ids)); + if (CollUtil.isNotEmpty(list)) { + return StreamUtils.join(list, d -> Convert.toStr(d.getDeptId())); + } + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 00000000..0402cf79 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,325 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.service.DeptService; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.TreeBuildUtils; +import com.xmzs.common.mybatis.helper.DataBaseHelper; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.SysDept; +import com.xmzs.system.domain.SysRole; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.bo.SysDeptBo; +import com.xmzs.system.domain.vo.SysDeptVo; +import com.xmzs.system.mapper.SysDeptMapper; +import com.xmzs.system.mapper.SysRoleMapper; +import com.xmzs.system.mapper.SysUserMapper; +import com.xmzs.system.service.ISysDeptService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 部门管理 服务实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDeptServiceImpl implements ISysDeptService, DeptService { + + private final SysDeptMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysUserMapper userMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + public List selectDeptList(SysDeptBo dept) { + LambdaQueryWrapper lqw = buildQueryWrapper(dept); + return baseMapper.selectDeptList(lqw); + } + + /** + * 查询部门树结构信息 + * + * @param bo 部门信息 + * @return 部门树信息集合 + */ + @Override + public List> selectDeptTreeList(SysDeptBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List depts = baseMapper.selectList(lqw); + return buildDeptTreeSelect(depts); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysDept::getDelFlag, "0"); + lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); + lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); + lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.orderByAsc(SysDept::getParentId); + lqw.orderByAsc(SysDept::getOrderNum); + return lqw; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List> buildDeptTreeSelect(List depts) { + if (CollUtil.isEmpty(depts)) { + return CollUtil.newArrayList(); + } + return TreeBuildUtils.build(depts, (dept, tree) -> + tree.setId(dept.getDeptId()) + .setParentId(dept.getParentId()) + .setName(dept.getDeptName()) + .setWeight(dept.getOrderNum())); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") + @Override + public SysDeptVo selectDeptById(Long deptId) { + SysDeptVo dept = baseMapper.selectVoById(deptId); + if (ObjectUtil.isNull(dept)) { + return null; + } + SysDeptVo parentDept = baseMapper.selectVoOne(new LambdaQueryWrapper() + .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId())); + dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null); + return dept; + } + + /** + * 通过部门ID查询部门名称 + * + * @param deptIds 部门ID串逗号分隔 + * @return 部门名称串逗号分隔 + */ + @Override + public String selectDeptNameByIds(String deptIds) { + List list = new ArrayList<>(); + for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) { + SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(id); + if (ObjectUtil.isNotNull(vo)) { + list.add(vo.getDeptName()); + } + } + return String.join(StringUtils.SEPARATOR, list); + } + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public long selectNormalChildrenDeptById(Long deptId) { + return baseMapper.selectCount(new LambdaQueryWrapper() + .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) { + return baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getParentId, deptId)); + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) { + return userMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getDeptId, deptId)); + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDeptBo dept) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getDeptName, dept.getDeptName()) + .eq(SysDept::getParentId, dept.getParentId()) + .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); + return !exist; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) { + if (ObjectUtil.isNull(deptId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + SysDeptVo dept = baseMapper.selectDeptById(deptId); + if (ObjectUtil.isNull(dept)) { + throw new ServiceException("没有权限访问部门数据!"); + } + } + + /** + * 新增保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDeptBo bo) { + SysDept info = baseMapper.selectById(bo.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { + throw new ServiceException("部门停用,不允许新增"); + } + SysDept dept = MapstructUtils.convert(bo, SysDept.class); + dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId()); + return baseMapper.insert(dept); + } + + /** + * 修改保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#bo.deptId") + @Override + public int updateDept(SysDeptBo bo) { + SysDept dept = MapstructUtils.convert(bo, SysDept.class); + SysDept oldDept = baseMapper.selectById(dept.getDeptId()); + if (!oldDept.getParentId().equals(dept.getParentId())) { + // 如果是新父部门 则校验是否具有新父部门权限 避免越权 + this.checkDeptDataScope(dept.getParentId()); + SysDept newParentDept = baseMapper.selectById(dept.getParentId()); + if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) { + String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + } + int result = baseMapper.updateById(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getAncestors())) { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + baseMapper.update(null, new LambdaUpdateWrapper() + .set(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .in(SysDept::getDeptId, Arrays.asList(deptIds))); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { + List children = baseMapper.selectList(new LambdaQueryWrapper() + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List list = new ArrayList<>(); + for (SysDept child : children) { + SysDept dept = new SysDept(); + dept.setDeptId(child.getDeptId()); + dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + list.add(dept); + } + if (CollUtil.isNotEmpty(list)) { + if (baseMapper.updateBatchById(list)) { + list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId())); + } + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") + @Override + public int deleteDeptById(Long deptId) { + return baseMapper.deleteById(deptId); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 00000000..caca2caa --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,139 @@ +package com.xmzs.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.system.domain.SysDictData; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.system.domain.bo.SysDictDataBo; +import com.xmzs.system.domain.vo.SysDictDataVo; +import com.xmzs.system.mapper.SysDictDataMapper; +import com.xmzs.system.service.ISysDictDataService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictDataServiceImpl implements ISysDictDataService { + + private final SysDictDataMapper baseMapper; + + @Override + public TableDataInfo selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictData); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictDataBo dictData) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictData); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDictDataBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getDictSort() != null, SysDictData::getDictSort, bo.getDictSort()); + lqw.like(StringUtils.isNotBlank(bo.getDictLabel()), SysDictData::getDictLabel, bo.getDictLabel()); + lqw.eq(StringUtils.isNotBlank(bo.getDictType()), SysDictData::getDictType, bo.getDictType()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDictData::getStatus, bo.getStatus()); + lqw.orderByAsc(SysDictData::getDictSort); + return lqw; + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysDictData::getDictLabel) + .eq(SysDictData::getDictType, dictType) + .eq(SysDictData::getDictValue, dictValue)) + .getDictLabel(); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictDataVo selectDictDataById(Long dictCode) { + return baseMapper.selectVoById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) { + for (Long dictCode : dictCodes) { + SysDictData data = baseMapper.selectById(dictCode); + baseMapper.deleteById(dictCode); + CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType()); + } + } + + /** + * 新增保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List insertDictData(SysDictDataBo bo) { + SysDictData data = MapstructUtils.convert(bo, SysDictData.class); + int row = baseMapper.insert(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List updateDictData(SysDictDataBo bo) { + SysDictData data = MapstructUtils.convert(bo, SysDictData.class); + int row = baseMapper.updateById(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 00000000..a80c72b9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,268 @@ +package com.xmzs.system.service.impl; + +import cn.dev33.satoken.context.SaHolder; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.CacheConstants; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.service.DictService; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.system.domain.SysDictData; +import com.xmzs.system.domain.SysDictType; +import com.xmzs.system.domain.bo.SysDictTypeBo; +import com.xmzs.system.domain.vo.SysDictDataVo; +import com.xmzs.system.domain.vo.SysDictTypeVo; +import com.xmzs.system.mapper.SysDictDataMapper; +import com.xmzs.system.mapper.SysDictTypeMapper; +import com.xmzs.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService { + + private final SysDictTypeMapper baseMapper; + private final SysDictDataMapper dictDataMapper; + + @Override + public TableDataInfo selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictType); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictTypeBo dictType) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictType); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDictTypeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getDictName()), SysDictType::getDictName, bo.getDictName()); + lqw.like(StringUtils.isNotBlank(bo.getDictType()), SysDictType::getDictType, bo.getDictType()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDictType::getStatus, bo.getStatus()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime")); + return lqw; + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() { + return baseMapper.selectVoList(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + @Override + public List selectDictDataByType(String dictType) { + List dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (CollUtil.isNotEmpty(dictDatas)) { + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictTypeVo selectDictTypeById(Long dictId) { + return baseMapper.selectVoById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + @Override + public SysDictTypeVo selectDictTypeByType(String dictType) { + return baseMapper.selectVoById(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) { + for (Long dictId : dictIds) { + SysDictType dictType = baseMapper.selectById(dictId); + if (dictDataMapper.exists(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType.getDictType()))) { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); + } + baseMapper.deleteBatchIds(Arrays.asList(dictIds)); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() { + CacheUtils.clear(CacheNames.SYS_DICT); + } + + /** + * 新增保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List insertDictType(SysDictTypeBo bo) { + SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); + int row = baseMapper.insert(dict); + if (row > 0) { + return new ArrayList<>(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + @Transactional(rollbackFor = Exception.class) + public List updateDictType(SysDictTypeBo bo) { + SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); + SysDictType oldDict = baseMapper.selectById(dict.getDictId()); + dictDataMapper.update(null, new LambdaUpdateWrapper() + .set(SysDictData::getDictType, dict.getDictType()) + .eq(SysDictData::getDictType, oldDict.getDictType())); + int row = baseMapper.updateById(dict); + if (row > 0) { + CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); + return dictDataMapper.selectDictDataByType(dict.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictTypeBo dictType) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDictType::getDictType, dictType.getDictType()) + .ne(ObjectUtil.isNotNull(dictType.getDictId()), SysDictType::getDictId, dictType.getDictId())); + return !exist; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + @SuppressWarnings("unchecked cast") + @Override + public String getDictLabel(String dictType, String dictValue, String separator) { + // 优先从本地缓存获取 + List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); + if (ObjectUtil.isNull(datas)) { + datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); + } + + Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); + if (StringUtils.containsAny(dictValue, separator)) { + return Arrays.stream(dictValue.split(separator)) + .map(v -> map.getOrDefault(v, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictValue, StringUtils.EMPTY); + } + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + @SuppressWarnings("unchecked cast") + @Override + public String getDictValue(String dictType, String dictLabel, String separator) { + // 优先从本地缓存获取 + List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); + if (ObjectUtil.isNull(datas)) { + datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); + } + + Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue); + if (StringUtils.containsAny(dictLabel, separator)) { + return Arrays.stream(dictLabel.split(separator)) + .map(l -> map.getOrDefault(l, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictLabel, StringUtils.EMPTY); + } + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 00000000..643b91ee --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,160 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.log.event.LogininforEvent; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.core.utils.ServletUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.ip.AddressUtils; +import com.xmzs.system.domain.SysLogininfor; +import com.xmzs.system.domain.bo.SysLogininforBo; +import com.xmzs.system.domain.vo.SysLogininforVo; +import com.xmzs.system.mapper.SysLogininforMapper; +import com.xmzs.system.service.ISysLogininforService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLogininforServiceImpl implements ISysLogininforService { + + private final SysLogininforMapper baseMapper; + + /** + * 记录登录信息 + * + * @param logininforEvent 登录事件 + */ + @Async + @EventListener + public void recordLogininfor(LogininforEvent logininforEvent) { + HttpServletRequest request = logininforEvent.getRequest(); + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(logininforEvent.getUsername())); + s.append(getBlock(logininforEvent.getStatus())); + s.append(getBlock(logininforEvent.getMessage())); + // 打印信息到日志 + log.info(s.toString(), logininforEvent.getArgs()); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininforBo logininfor = new SysLogininforBo(); + logininfor.setTenantId(logininforEvent.getTenantId()); + logininfor.setUserName(logininforEvent.getUsername()); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(logininforEvent.getMessage()); + // 日志状态 + if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + insertLogininfor(logininfor); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } + + @Override + public TableDataInfo selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery) { + Map params = logininfor.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + pageQuery.setOrderByColumn("info_id"); + pageQuery.setIsAsc("desc"); + } + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 新增系统登录日志 + * + * @param bo 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininforBo bo) { + SysLogininfor logininfor = MapstructUtils.convert(bo, SysLogininfor.class); + logininfor.setLoginTime(new Date()); + baseMapper.insert(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininforBo logininfor) { + Map params = logininfor.getParams(); + return baseMapper.selectVoList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")) + .orderByDesc(SysLogininfor::getInfoId)); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) { + return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 00000000..52a4631e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,365 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.TreeBuildUtils; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.SysMenu; +import com.xmzs.system.domain.SysRole; +import com.xmzs.system.domain.SysRoleMenu; +import com.xmzs.system.domain.SysTenantPackage; +import com.xmzs.system.domain.bo.SysMenuBo; +import com.xmzs.system.domain.vo.MetaVo; +import com.xmzs.system.domain.vo.RouterVo; +import com.xmzs.system.domain.vo.SysMenuVo; +import com.xmzs.system.mapper.SysMenuMapper; +import com.xmzs.system.mapper.SysRoleMapper; +import com.xmzs.system.mapper.SysRoleMenuMapper; +import com.xmzs.system.mapper.SysTenantPackageMapper; +import com.xmzs.system.service.ISysMenuService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 菜单 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysMenuServiceImpl implements ISysMenuService { + + private final SysMenuMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysTenantPackageMapper tenantPackageMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) { + return selectMenuList(new SysMenuBo(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenuBo menu, Long userId) { + List menuList; + // 管理员显示所有菜单信息 + if (LoginHelper.isSuperAdmin(userId)) { + menuList = baseMapper.selectVoList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum)); + } else { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("sur.user_id", userId) + .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) + .orderByAsc("m.parent_id") + .orderByAsc("m.order_num"); + List list = baseMapper.selectMenuListByUserId(wrapper); + menuList = MapstructUtils.convert(list, SysMenuVo.class); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) { + List perms = baseMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(StringUtils.splitList(perm.trim())); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) { + List perms = baseMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(StringUtils.splitList(perm.trim())); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) { + List menus; + if (LoginHelper.isSuperAdmin(userId)) { + menus = baseMapper.selectMenuTreeAll(); + } else { + menus = baseMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); + } + + /** + * 根据租户套餐ID查询菜单树信息 + * + * @param packageId 租户套餐ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByPackageId(Long packageId) { + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + if (CollUtil.isEmpty(menuIds)) { + return List.of(); + } + List parentIds = null; + if (tenantPackage.getMenuCheckStrictly()) { + parentIds = baseMapper.selectObjs(new LambdaQueryWrapper() + .select(SysMenu::getParentId) + .in(SysMenu::getMenuId, menuIds), Convert::toLong); + } + return baseMapper.selectObjs(new LambdaQueryWrapper() + .in(SysMenu::getMenuId, menuIds) + .notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), Convert::toLong); + } + + /** + * 构建前端路由所需要的菜单 + * + * @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(menu.getRouteName()); + router.setPath(menu.getRouterPath()); + router.setComponent(menu.getComponentInfo()); + router.setQuery(menu.getQueryParam()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (CollUtil.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (menu.isMenuFrame()) { + router.setMeta(null); + List childrenList = new ArrayList<>(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponentInfo()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQueryParam()); + childrenList.add(children); + router.setChildren(childrenList); + } else if (menu.getParentId().intValue() == 0 && menu.isInnerLink()) { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList<>(); + RouterVo children = new RouterVo(); + String routerPath = SysMenu.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> buildMenuTreeSelect(List menus) { + if (CollUtil.isEmpty(menus)) { + return CollUtil.newArrayList(); + } + return TreeBuildUtils.build(menus, (menu, tree) -> + tree.setId(menu.getMenuId()) + .setParentId(menu.getParentId()) + .setName(menu.getMenuName()) + .setWeight(menu.getOrderNum())); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenuVo selectMenuById(Long menuId) { + return baseMapper.selectVoById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) { + return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) { + return roleMenuMapper.exists(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); + } + + /** + * 新增保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenuBo bo) { + SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); + return baseMapper.insert(menu); + } + + /** + * 修改保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenuBo bo) { + SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); + return baseMapper.updateById(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) { + return baseMapper.deleteById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenuBo menu) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysMenu::getMenuName, menu.getMenuName()) + .eq(SysMenu::getParentId, menu.getParentId()) + .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId())); + return !exist; + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + private List getChildPerms(List list, int parentId) { + List returnList = new ArrayList<>(); + for (SysMenu t : list) { + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysMenu t) { + // 得到子节点列表 + List childList = StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId())); + t.setChildren(childList); + for (SysMenu tChild : childList) { + // 判断是否有子节点 + if (list.stream().anyMatch(n -> n.getParentId().equals(tChild.getMenuId()))) { + recursionFn(list, tChild); + } + } + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 00000000..dec307d8 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,122 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.SysNotice; +import com.xmzs.system.domain.bo.SysNoticeBo; +import com.xmzs.system.domain.vo.SysNoticeVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.mapper.SysNoticeMapper; +import com.xmzs.system.mapper.SysUserMapper; +import com.xmzs.system.service.ISysNoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysNoticeServiceImpl implements ISysNoticeService { + + private final SysNoticeMapper baseMapper; + private final SysUserMapper userMapper; + + @Override + public TableDataInfo selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(notice); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNoticeVo selectNoticeById(Long noticeId) { + return baseMapper.selectVoById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNoticeBo notice) { + LambdaQueryWrapper lqw = buildQueryWrapper(notice); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysNoticeBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); + if (StringUtils.isNotBlank(bo.getCreateByName())) { + SysUserVo sysUser = userMapper.selectUserByUserName(bo.getCreateByName()); + lqw.eq(SysNotice::getCreateBy, ObjectUtil.isNotNull(sysUser) ? sysUser.getUserId() : null); + } + return lqw; + } + + /** + * 新增公告 + * + * @param bo 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNoticeBo bo) { + SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); + return baseMapper.insert(notice); + } + + /** + * 修改公告 + * + * @param bo 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNoticeBo bo) { + SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); + return baseMapper.updateById(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) { + return baseMapper.deleteById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) { + return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 00000000..6a297a39 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,144 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.util.ArrayUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.ip.AddressUtils; +import com.xmzs.common.log.event.OperLogEvent; +import com.xmzs.system.domain.SysOperLog; +import com.xmzs.system.domain.bo.SysOperLogBo; +import com.xmzs.system.domain.vo.SysOperLogVo; +import com.xmzs.system.mapper.SysOperLogMapper; +import com.xmzs.system.service.ISysOperLogService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 操作日志 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysOperLogServiceImpl implements ISysOperLogService { + + private final SysOperLogMapper baseMapper; + + /** + * 操作日志记录 + * + * @param operLogEvent 操作日志事件 + */ + @Async + @EventListener + public void recordOper(OperLogEvent operLogEvent) { + SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class); + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + insertOperlog(operLog); + } + + @Override + public TableDataInfo selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) { + Map params = operLog.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null, + SysOperLog::getStatus, operLog.getStatus()) + .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + pageQuery.setOrderByColumn("oper_id"); + pageQuery.setIsAsc("desc"); + } + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 新增操作日志 + * + * @param bo 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLogBo bo) { + SysOperLog operLog = MapstructUtils.convert(bo, SysOperLog.class); + operLog.setOperTime(new Date()); + baseMapper.insert(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLogBo operLog) { + Map params = operLog.getParams(); + return baseMapper.selectVoList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null && operLog.getStatus() > 0, + SysOperLog::getStatus, operLog.getStatus()) + .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")) + .orderByDesc(SysOperLog::getOperId)); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) { + return baseMapper.deleteBatchIds(Arrays.asList(operIds)); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLogVo selectOperLogById(Long operId) { + return baseMapper.selectVoById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssConfigServiceImpl.java new file mode 100644 index 00000000..2d5cb104 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssConfigServiceImpl.java @@ -0,0 +1,186 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.json.utils.JsonUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.oss.constant.OssConstant; +import com.xmzs.common.redis.utils.CacheUtils; +import com.xmzs.common.redis.utils.RedisUtils; +import com.xmzs.common.tenant.core.TenantEntity; +import com.xmzs.common.tenant.helper.TenantHelper; +import com.xmzs.system.domain.SysOssConfig; +import com.xmzs.system.domain.bo.SysOssConfigBo; +import com.xmzs.system.domain.vo.SysOssConfigVo; +import com.xmzs.system.mapper.SysOssConfigMapper; +import com.xmzs.system.service.ISysOssConfigService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 对象存储配置Service业务层处理 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysOssConfigServiceImpl implements ISysOssConfigService { + + private final SysOssConfigMapper baseMapper; + + /** + * 项目启动时,初始化参数到缓存,加载配置类 + */ + @Override + public void init() { + List list = TenantHelper.ignore(() -> + baseMapper.selectList( + new LambdaQueryWrapper().orderByAsc(TenantEntity::getTenantId)) + ); + Map> map = StreamUtils.groupByKey(list, SysOssConfig::getTenantId); + try { + for (String tenantId : map.keySet()) { + TenantHelper.setDynamic(tenantId); + // 加载OSS初始化配置 + for (SysOssConfig config : map.get(tenantId)) { + String configKey = config.getConfigKey(); + if ("0".equals(config.getStatus())) { + RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey); + } + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + } + } finally { + TenantHelper.clearDynamic(); + } + } + + @Override + public SysOssConfigVo queryById(Long ossConfigId) { + return baseMapper.selectVoById(ossConfigId); + } + + @Override + public TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + + private LambdaQueryWrapper buildQueryWrapper(SysOssConfigBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey()); + lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus()); + return lqw; + } + + @Override + public Boolean insertByBo(SysOssConfigBo bo) { + SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); + validEntityBeforeSave(config); + boolean flag = baseMapper.insert(config) > 0; + if (flag) { + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + return flag; + } + + @Override + public Boolean updateByBo(SysOssConfigBo bo) { + SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); + validEntityBeforeSave(config); + LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); + luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); + luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); + luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); + luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); + luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); + boolean flag = baseMapper.update(config, luw) > 0; + if (flag) { + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysOssConfig entity) { + if (StringUtils.isNotEmpty(entity.getConfigKey()) + && !checkConfigKeyUnique(entity)) { + throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, 配置key已存在!"); + } + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) { + throw new ServiceException("系统内置, 不可删除!"); + } + } + List list = CollUtil.newArrayList(); + for (Long configId : ids) { + SysOssConfig config = baseMapper.selectById(configId); + list.add(config); + } + boolean flag = baseMapper.deleteBatchIds(ids) > 0; + if (flag) { + list.forEach(sysOssConfig -> + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); + } + return flag; + } + + /** + * 判断configKey是否唯一 + */ + private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { + long ossConfigId = ObjectUtil.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId(); + SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) + .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey())); + if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { + return false; + } + return true; + } + + /** + * 启用禁用状态 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateOssConfigStatus(SysOssConfigBo bo) { + SysOssConfig sysOssConfig = MapstructUtils.convert(bo, SysOssConfig.class); + int row = baseMapper.update(null, new LambdaUpdateWrapper() + .set(SysOssConfig::getStatus, "1")); + row += baseMapper.updateById(sysOssConfig); + if (row > 0) { + RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); + } + return row; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssServiceImpl.java new file mode 100644 index 00000000..cfb75a37 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysOssServiceImpl.java @@ -0,0 +1,171 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.service.OssService; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.core.utils.file.FileUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.oss.core.OssClient; +import com.xmzs.common.oss.entity.UploadResult; +import com.xmzs.common.oss.enumd.AccessPolicyType; +import com.xmzs.common.oss.factory.OssFactory; +import com.xmzs.system.domain.SysOss; +import com.xmzs.system.domain.bo.SysOssBo; +import com.xmzs.system.domain.vo.SysOssVo; +import com.xmzs.system.mapper.SysOssMapper; +import com.xmzs.system.service.ISysOssService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +/** + * 文件上传 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysOssServiceImpl implements ISysOssService, OssService { + + private final SysOssMapper baseMapper; + + @Override + public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + List filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl); + result.setRecords(filterResult); + return TableDataInfo.build(result); + } + + @Override + public List listByIds(Collection ossIds) { + List list = new ArrayList<>(); + for (Long id : ossIds) { + SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + if (ObjectUtil.isNotNull(vo)) { + list.add(this.matchingUrl(vo)); + } + } + return list; + } + + @Override + public String selectUrlByIds(String ossIds) { + List list = new ArrayList<>(); + for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { + SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + if (ObjectUtil.isNotNull(vo)) { + list.add(this.matchingUrl(vo).getUrl()); + } + } + return String.join(StringUtils.SEPARATOR, list); + } + + private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); + lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); + lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); + lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); + lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); + return lqw; + } + + @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") + @Override + public SysOssVo getById(Long ossId) { + return baseMapper.selectVoById(ossId); + } + + @Override + public void download(Long ossId, HttpServletResponse response) throws IOException { + SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); + if (ObjectUtil.isNull(sysOss)) { + throw new ServiceException("文件数据不存在!"); + } + FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); + OssClient storage = OssFactory.instance(); + try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { + int available = inputStream.available(); + IoUtil.copy(inputStream, response.getOutputStream(), available); + response.setContentLength(available); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + @Override + public SysOssVo upload(MultipartFile file) { + String originalfileName = file.getOriginalFilename(); + String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); + OssClient storage = OssFactory.instance(); + UploadResult uploadResult; + try { + uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + } catch (IOException e) { + throw new ServiceException(e.getMessage()); + } + // 保存文件信息 + SysOss oss = new SysOss(); + oss.setUrl(uploadResult.getUrl()); + oss.setFileSuffix(suffix); + oss.setFileName(uploadResult.getFilename()); + oss.setOriginalName(originalfileName); + oss.setService(storage.getConfigKey()); + baseMapper.insert(oss); + SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); + return this.matchingUrl(sysOssVo); + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + } + List list = baseMapper.selectBatchIds(ids); + for (SysOss sysOss : list) { + OssClient storage = OssFactory.instance(sysOss.getService()); + storage.delete(sysOss.getUrl()); + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + /** + * 匹配Url + * + * @param oss OSS对象 + * @return oss 匹配Url的OSS对象 + */ + private SysOssVo matchingUrl(SysOssVo oss) { + OssClient storage = OssFactory.instance(oss.getService()); + // 仅修改桶类型为 private 的URL,临时URL时长为120s + if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { + oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120)); + } + return oss; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPermissionServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 00000000..f4ccf3a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,61 @@ +package com.xmzs.system.service.impl; + +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.service.ISysMenuService; +import com.xmzs.system.service.ISysPermissionService; +import com.xmzs.system.service.ISysRoleService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@RequiredArgsConstructor +@Service +public class SysPermissionServiceImpl implements ISysPermissionService { + + private final ISysRoleService roleService; + private final ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + @Override + public Set getRolePermission(Long userId) { + Set roles = new HashSet<>(); + // 管理员拥有所有权限 + if (LoginHelper.isSuperAdmin(userId)) { + roles.add(TenantConstants.SUPER_ADMIN_ROLE_KEY); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(userId)); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + @Override + public Set getMenuPermission(Long userId) { + Set perms = new HashSet<>(); + // 管理员拥有所有权限 + if (LoginHelper.isSuperAdmin(userId)) { + perms.add("*:*:*"); + } else { + perms.addAll(menuService.selectMenuPermsByUserId(userId)); + } + return perms; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 00000000..90b0ed11 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,188 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.SysPost; +import com.xmzs.system.domain.SysUserPost; +import com.xmzs.system.domain.bo.SysPostBo; +import com.xmzs.system.domain.vo.SysPostVo; +import com.xmzs.system.mapper.SysPostMapper; +import com.xmzs.system.mapper.SysUserPostMapper; +import com.xmzs.system.service.ISysPostService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysPostServiceImpl implements ISysPostService { + + private final SysPostMapper baseMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPagePostList(SysPostBo post, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(post); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPostBo post) { + LambdaQueryWrapper lqw = buildQueryWrapper(post); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysPostBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode()); + lqw.like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus()); + lqw.orderByAsc(SysPost::getPostSort); + return lqw; + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() { + return baseMapper.selectVoList(new QueryWrapper<>()); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPostVo selectPostById(Long postId) { + return baseMapper.selectVoById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) { + return baseMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPostBo post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostName, post.getPostName()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + return !exist; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPostBo post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostCode, post.getPostCode()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + return !exist; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public long countUserPostById(Long postId) { + return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) { + return baseMapper.deleteById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) { + for (Long postId : postIds) { + SysPost post = baseMapper.selectById(postId); + if (countUserPostById(postId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return baseMapper.deleteBatchIds(Arrays.asList(postIds)); + } + + /** + * 新增保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPostBo bo) { + SysPost post = MapstructUtils.convert(bo, SysPost.class); + return baseMapper.insert(post); + } + + /** + * 修改保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPostBo bo) { + SysPost post = MapstructUtils.convert(bo, SysPost.class); + return baseMapper.updateById(post); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 00000000..4a744e72 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,458 @@ +package com.xmzs.system.service.impl; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.SysRole; +import com.xmzs.system.domain.SysRoleDept; +import com.xmzs.system.domain.SysRoleMenu; +import com.xmzs.system.domain.SysUserRole; +import com.xmzs.system.domain.bo.SysRoleBo; +import com.xmzs.system.domain.vo.SysRoleVo; +import com.xmzs.system.mapper.SysRoleDeptMapper; +import com.xmzs.system.mapper.SysRoleMapper; +import com.xmzs.system.mapper.SysRoleMenuMapper; +import com.xmzs.system.mapper.SysUserRoleMapper; +import com.xmzs.system.service.ISysRoleService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRoleServiceImpl implements ISysRoleService { + + private final SysRoleMapper baseMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysRoleDeptMapper roleDeptMapper; + + @Override + public TableDataInfo selectPageRoleList(SysRoleBo role, PageQuery pageQuery) { + Page page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + public List selectRoleList(SysRoleBo role) { + return baseMapper.selectRoleList(this.buildQueryWrapper(role)); + } + + private Wrapper buildQueryWrapper(SysRoleBo bo) { + Map params = bo.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("r.del_flag", UserConstants.ROLE_NORMAL) + .eq(ObjectUtil.isNotNull(bo.getRoleId()), "r.role_id", bo.getRoleId()) + .like(StringUtils.isNotBlank(bo.getRoleName()), "r.role_name", bo.getRoleName()) + .eq(StringUtils.isNotBlank(bo.getStatus()), "r.status", bo.getStatus()) + .like(StringUtils.isNotBlank(bo.getRoleKey()), "r.role_key", bo.getRoleKey()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "r.create_time", params.get("beginTime"), params.get("endTime")) + .orderByAsc("r.role_sort").orderByAsc("r.create_time");; + return wrapper; + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) { + List userRoles = baseMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRoleVo role : roles) { + for (SysRoleVo 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 = baseMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRoleVo perm : perms) { + if (ObjectUtil.isNotNull(perm)) { + permsSet.addAll(StringUtils.splitList(perm.getRoleKey().trim())); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() { + return this.selectRoleList(new SysRoleBo()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + return baseMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRoleVo selectRoleById(Long roleId) { + return baseMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRoleBo role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleName, role.getRoleName()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + return !exist; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRoleBo role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleKey, role.getRoleKey()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + return !exist; + } + + /** + * 校验角色是否允许操作 + * + * @param roleId 角色ID + */ + @Override + public void checkRoleAllowed(Long roleId) { + if (ObjectUtil.isNotNull(roleId) && LoginHelper.isSuperAdmin(roleId)) { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) { + if (ObjectUtil.isNull(roleId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + List roles = this.selectRoleList(new SysRoleBo(roleId)); + if (CollUtil.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色数据!"); + } + + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public long countUserRoleByRoleId(Long roleId) { + return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + } + + /** + * 新增保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertRole(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + // 新增角色信息 + baseMapper.insert(role); + bo.setRoleId(role.getRoleId()); + return insertRoleMenu(bo); + } + + /** + * 修改保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); + return insertRoleMenu(bo); + } + + /** + * 修改角色状态 + * + * @param roleId 角色ID + * @param status 角色状态 + * @return 结果 + */ + @Override + public int updateRoleStatus(Long roleId, String status) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysRole::getStatus, status) + .eq(SysRole::getRoleId, roleId)); + } + + /** + * 修改数据权限信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int authDataScope(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); + // 新增角色和部门信息(数据权限) + return insertRoleDept(bo); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + private int insertRoleMenu(SysRoleBo 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.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + private int insertRoleDept(SysRoleBo 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.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleById(Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); + return baseMapper.deleteById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds(Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(roleId); + checkRoleDataScope(roleId); + SysRole role = baseMapper.selectById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + List ids = Arrays.asList(roleIds); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); + return baseMapper.deleteBatchIds(ids); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) { + int rows = userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, userRole.getRoleId()) + .eq(SysUserRole::getUserId, userRole.getUserId())); + if (rows > 0) { + cleanOnlineUserByRole(userRole.getRoleId()); + } + return rows; + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) { + int rows = userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, roleId) + .in(SysUserRole::getUserId, Arrays.asList(userIds))); + if (rows > 0) { + cleanOnlineUserByRole(roleId); + } + return rows; + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) { + // 新增用户与角色管理 + int rows = 1; + List list = StreamUtils.toList(List.of(userIds), userId -> { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + return ur; + }); + if (CollUtil.isNotEmpty(list)) { + rows = userRoleMapper.insertBatch(list) ? list.size() : 0; + } + if (rows > 0) { + cleanOnlineUserByRole(roleId); + } + return rows; + } + + @Override + public void cleanOnlineUserByRole(Long roleId) { + List keys = StpUtil.searchTokenValue("", 0, -1, false); + if (CollUtil.isEmpty(keys)) { + return; + } + // 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作 + keys.parallelStream().forEach(key -> { + String token = StringUtils.substringAfterLast(key, ":"); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { + return; + } + LoginUser loginUser = LoginHelper.getLoginUser(token); + if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(roleId))) { + try { + StpUtil.logoutByTokenValue(token); + } catch (NotLoginException ignored) { + } + } + }); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysSensitiveServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysSensitiveServiceImpl.java new file mode 100644 index 00000000..ac396fbe --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysSensitiveServiceImpl.java @@ -0,0 +1,26 @@ +package com.xmzs.system.service.impl; + +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.common.sensitive.core.SensitiveService; +import org.springframework.stereotype.Service; + +/** + * 脱敏服务 + * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + */ +@Service +public class SysSensitiveServiceImpl implements SensitiveService { + + /** + * 是否脱敏 + */ + @Override + public boolean isSensitive() { + return !LoginHelper.isSuperAdmin() || !LoginHelper.isTenantAdmin(); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantPackageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantPackageServiceImpl.java new file mode 100644 index 00000000..5bcef2da --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantPackageServiceImpl.java @@ -0,0 +1,146 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xmzs.system.domain.SysTenant; +import com.xmzs.system.mapper.SysTenantMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import com.xmzs.system.domain.bo.SysTenantPackageBo; +import com.xmzs.system.domain.vo.SysTenantPackageVo; +import com.xmzs.system.domain.SysTenantPackage; +import com.xmzs.system.mapper.SysTenantPackageMapper; +import com.xmzs.system.service.ISysTenantPackageService; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 租户套餐Service业务层处理 + * + * @author Michelle.Chung + */ +@RequiredArgsConstructor +@Service +public class SysTenantPackageServiceImpl implements ISysTenantPackageService { + + private final SysTenantPackageMapper baseMapper; + private final SysTenantMapper tenantMapper; + + /** + * 查询租户套餐 + */ + @Override + public SysTenantPackageVo queryById(Long packageId){ + return baseMapper.selectVoById(packageId); + } + + /** + * 查询租户套餐列表 + */ + @Override + public TableDataInfo queryPageList(SysTenantPackageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List selectList() { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .eq(SysTenantPackage::getStatus, TenantConstants.NORMAL)); + } + + /** + * 查询租户套餐列表 + */ + @Override + public List queryList(SysTenantPackageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysTenantPackageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), SysTenantPackage::getPackageName, bo.getPackageName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenantPackage::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 新增租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysTenantPackageBo bo) { + SysTenantPackage add = MapstructUtils.convert(bo, SysTenantPackage.class); + // 保存菜单id + List menuIds = Arrays.asList(bo.getMenuIds()); + if (CollUtil.isNotEmpty(menuIds)) { + add.setMenuIds(StringUtils.join(menuIds, ", ")); + } else { + add.setMenuIds(""); + } + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setPackageId(add.getPackageId()); + } + return flag; + } + + /** + * 修改租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(SysTenantPackageBo bo) { + SysTenantPackage update = MapstructUtils.convert(bo, SysTenantPackage.class); + // 保存菜单id + List menuIds = Arrays.asList(bo.getMenuIds()); + if (CollUtil.isNotEmpty(menuIds)) { + update.setMenuIds(StringUtils.join(menuIds, ", ")); + } else { + update.setMenuIds(""); + } + return baseMapper.updateById(update) > 0; + } + + /** + * 修改套餐状态 + * + * @param bo 套餐信息 + * @return 结果 + */ + @Override + public int updatePackageStatus(SysTenantPackageBo bo) { + SysTenantPackage tenantPackage = MapstructUtils.convert(bo, SysTenantPackage.class); + return baseMapper.updateById(tenantPackage); + } + + /** + * 批量删除租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + boolean exists = tenantMapper.exists(new LambdaQueryWrapper().in(SysTenant::getPackageId, ids)); + if (exists) { + throw new ServiceException("租户套餐已被使用"); + } + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantServiceImpl.java new file mode 100644 index 00000000..72425411 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysTenantServiceImpl.java @@ -0,0 +1,367 @@ +package com.xmzs.system.service.impl; + +import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.constant.Constants; +import com.xmzs.common.core.constant.TenantConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.SpringUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.system.domain.*; +import com.xmzs.system.domain.bo.SysTenantBo; +import com.xmzs.system.domain.vo.SysTenantVo; +import com.xmzs.system.mapper.*; +import com.xmzs.system.service.ISysTenantService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * 租户Service业务层处理 + * + * @author Michelle.Chung + */ +@RequiredArgsConstructor +@Service +public class SysTenantServiceImpl implements ISysTenantService { + + private final SysTenantMapper baseMapper; + private final SysTenantPackageMapper tenantPackageMapper; + private final SysUserMapper userMapper; + private final SysDeptMapper deptMapper; + private final SysRoleMapper roleMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysRoleDeptMapper roleDeptMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysDictTypeMapper dictTypeMapper; + private final SysDictDataMapper dictDataMapper; + private final SysConfigMapper configMapper; + + /** + * 查询租户 + */ + @Override + public SysTenantVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 基于租户ID查询租户 + */ + @Cacheable(cacheNames = CacheNames.SYS_TENANT, key = "#tenantId") + @Override + public SysTenantVo queryByTenantId(String tenantId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysTenant::getTenantId, tenantId)); + } + + /** + * 查询租户列表 + */ + @Override + public TableDataInfo queryPageList(SysTenantBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询租户列表 + */ + @Override + public List queryList(SysTenantBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysTenantBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId()); + lqw.like(StringUtils.isNotBlank(bo.getContactUserName()), SysTenant::getContactUserName, bo.getContactUserName()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), SysTenant::getContactPhone, bo.getContactPhone()); + lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), SysTenant::getCompanyName, bo.getCompanyName()); + lqw.eq(StringUtils.isNotBlank(bo.getLicenseNumber()), SysTenant::getLicenseNumber, bo.getLicenseNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getAddress()), SysTenant::getAddress, bo.getAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getIntro()), SysTenant::getIntro, bo.getIntro()); + lqw.like(StringUtils.isNotBlank(bo.getDomain()), SysTenant::getDomain, bo.getDomain()); + lqw.eq(bo.getPackageId() != null, SysTenant::getPackageId, bo.getPackageId()); + lqw.eq(bo.getExpireTime() != null, SysTenant::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getAccountCount() != null, SysTenant::getAccountCount, bo.getAccountCount()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenant::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 新增租户 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysTenantBo bo) { + SysTenant add = MapstructUtils.convert(bo, SysTenant.class); + + // 获取所有租户编号 + List tenantIds = baseMapper.selectObjs( + new LambdaQueryWrapper().select(SysTenant::getTenantId), Convert::toStr); + String tenantId = generateTenantId(tenantIds); + add.setTenantId(tenantId); + boolean flag = baseMapper.insert(add) > 0; + if (!flag) { + throw new ServiceException("创建租户失败"); + } + bo.setId(add.getId()); + + // 根据套餐创建角色 + Long roleId = createTenantRole(tenantId, bo.getPackageId()); + + // 创建部门: 公司名是部门名称 + SysDept dept = new SysDept(); + dept.setTenantId(tenantId); + dept.setDeptName(bo.getCompanyName()); + dept.setLeader(bo.getUsername()); + dept.setParentId(Constants.TOP_PARENT_ID); + dept.setAncestors(Constants.TOP_PARENT_ID.toString()); + deptMapper.insert(dept); + Long deptId = dept.getDeptId(); + + // 角色和部门关联表 + SysRoleDept roleDept = new SysRoleDept(); + roleDept.setRoleId(roleId); + roleDept.setDeptId(deptId); + roleDeptMapper.insert(roleDept); + + // 创建系统用户 + SysUser user = new SysUser(); + user.setTenantId(tenantId); + user.setUserName(bo.getUsername()); + user.setNickName(bo.getUsername()); + user.setPassword(BCrypt.hashpw(bo.getPassword())); + user.setDeptId(deptId); + userMapper.insert(user); + + // 用户和角色关联表 + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(user.getUserId()); + userRole.setRoleId(roleId); + userRoleMapper.insert(userRole); + + String defaultTenantId = TenantConstants.DEFAULT_TENANT_ID; + List dictTypeList = dictTypeMapper.selectList( + new LambdaQueryWrapper().eq(SysDictType::getTenantId, defaultTenantId)); + List dictDataList = dictDataMapper.selectList( + new LambdaQueryWrapper().eq(SysDictData::getTenantId, defaultTenantId)); + for (SysDictType dictType : dictTypeList) { + dictType.setDictId(null); + dictType.setTenantId(tenantId); + } + for (SysDictData dictData : dictDataList) { + dictData.setDictCode(null); + dictData.setTenantId(tenantId); + } + dictTypeMapper.insertBatch(dictTypeList); + dictDataMapper.insertBatch(dictDataList); + + List sysConfigList = configMapper.selectList( + new LambdaQueryWrapper().eq(SysConfig::getTenantId, defaultTenantId)); + for (SysConfig config : sysConfigList) { + config.setConfigId(null); + config.setTenantId(tenantId); + } + configMapper.insertBatch(sysConfigList); + return true; + } + + /** + * 生成租户id + * + * @param tenantIds 已有租户id列表 + * @return 租户id + */ + private String generateTenantId(List tenantIds) { + // 随机生成6位 + String numbers = RandomUtil.randomNumbers(6); + // 判断是否存在,如果存在则重新生成 + if (tenantIds.contains(numbers)) { + generateTenantId(tenantIds); + } + return numbers; + } + + /** + * 根据租户菜单创建租户角色 + * + * @param tenantId 租户编号 + * @param packageId 租户套餐id + * @return 角色id + */ + private Long createTenantRole(String tenantId, Long packageId) { + // 获取租户套餐 + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + if (ObjectUtil.isNull(tenantPackage)) { + throw new ServiceException("套餐不存在"); + } + // 获取套餐菜单id + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + + // 创建角色 + SysRole role = new SysRole(); + role.setTenantId(tenantId); + role.setRoleName(TenantConstants.TENANT_ADMIN_ROLE_NAME); + role.setRoleKey(TenantConstants.TENANT_ADMIN_ROLE_KEY); + role.setRoleSort(1); + role.setStatus(TenantConstants.NORMAL); + roleMapper.insert(role); + Long roleId = role.getRoleId(); + + // 创建角色菜单 + List roleMenus = new ArrayList<>(menuIds.size()); + menuIds.forEach(menuId -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menuId); + roleMenus.add(roleMenu); + }); + roleMenuMapper.insertBatch(roleMenus); + + return roleId; + } + + /** + * 修改租户 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId") + @Override + public Boolean updateByBo(SysTenantBo bo) { + SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class); + tenant.setTenantId(null); + tenant.setPackageId(null); + return baseMapper.updateById(tenant) > 0; + } + + /** + * 修改租户状态 + * + * @param bo 租户信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId") + @Override + public int updateTenantStatus(SysTenantBo bo) { + SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class); + return baseMapper.updateById(tenant); + } + + /** + * 校验租户是否允许操作 + * + * @param tenantId 租户ID + */ + @Override + public void checkTenantAllowed(String tenantId) { + if (ObjectUtil.isNotNull(tenantId) && TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + throw new ServiceException("不允许操作管理租户"); + } + } + + /** + * 批量删除租户 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, allEntries = true) + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + if (ids.contains(TenantConstants.SUPER_ADMIN_ID)) { + throw new ServiceException("超管租户不能删除"); + } + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + /** + * 校验企业名称是否唯一 + */ + @Override + public boolean checkCompanyNameUnique(SysTenantBo bo) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysTenant::getCompanyName, bo.getCompanyName()) + .ne(ObjectUtil.isNotNull(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId())); + return !exist; + } + + /** + * 校验账号余额 + */ + @Override + public boolean checkAccountBalance(String tenantId) { + SysTenantVo tenant = SpringUtils.getAopProxy(this).queryByTenantId(tenantId); + // 如果余额为-1代表不限制 + if (tenant.getAccountCount() == -1) { + return true; + } + Long userNumber = userMapper.selectCount(new LambdaQueryWrapper<>()); + // 如果余额大于0代表还有可用名额 + return tenant.getAccountCount() - userNumber > 0; + } + + /** + * 校验有效期 + */ + @Override + public boolean checkExpireTime(String tenantId) { + SysTenantVo tenant = SpringUtils.getAopProxy(this).queryByTenantId(tenantId); + // 如果未设置过期时间代表不限制 + if (ObjectUtil.isNull(tenant.getExpireTime())) { + return true; + } + // 如果当前时间在过期时间之前则通过 + return new Date().before(tenant.getExpireTime()); + } + + /** + * 同步租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean syncTenantPackage(String tenantId, String packageId) { + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + List roles = roleMapper.selectList( + new LambdaQueryWrapper().eq(SysRole::getTenantId, tenantId)); + List roleIds = new ArrayList<>(roles.size() - 1); + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + roles.forEach(item -> { + if (TenantConstants.TENANT_ADMIN_ROLE_KEY.equals(item.getRoleKey())) { + List roleMenus = new ArrayList<>(menuIds.size()); + menuIds.forEach(menuId -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(item.getRoleId()); + roleMenu.setMenuId(menuId); + roleMenus.add(roleMenu); + }); + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, item.getRoleId())); + roleMenuMapper.insertBatch(roleMenus); + } else { + roleIds.add(item.getRoleId()); + } + }); + if (!roleIds.isEmpty()) { + roleMenuMapper.delete( + new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, roleIds).notIn(!menuIds.isEmpty(), SysRoleMenu::getMenuId, menuIds)); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 00000000..776ff147 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,562 @@ +package com.xmzs.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xmzs.common.core.domain.model.LoginUser; +import com.xmzs.common.core.exception.base.BaseException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.xmzs.common.core.constant.CacheNames; +import com.xmzs.common.core.constant.UserConstants; +import com.xmzs.common.core.exception.ServiceException; +import com.xmzs.common.core.service.UserService; +import com.xmzs.common.core.utils.MapstructUtils; +import com.xmzs.common.core.utils.StreamUtils; +import com.xmzs.common.core.utils.StringUtils; +import com.xmzs.common.mybatis.core.page.PageQuery; +import com.xmzs.common.mybatis.core.page.TableDataInfo; +import com.xmzs.common.mybatis.helper.DataBaseHelper; +import com.xmzs.common.satoken.utils.LoginHelper; +import com.xmzs.system.domain.SysDept; +import com.xmzs.system.domain.SysUser; +import com.xmzs.system.domain.SysUserPost; +import com.xmzs.system.domain.SysUserRole; +import com.xmzs.system.domain.bo.SysUserBo; +import com.xmzs.system.domain.vo.SysPostVo; +import com.xmzs.system.domain.vo.SysRoleVo; +import com.xmzs.system.domain.vo.SysUserVo; +import com.xmzs.system.mapper.*; +import com.xmzs.system.service.ISysUserService; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 用户 业务层处理 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysUserServiceImpl implements ISysUserService, UserService { + + private final SysUserMapper baseMapper; + private final SysDeptMapper deptMapper; + private final SysRoleMapper roleMapper; + private final SysPostMapper postMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPageUserList(SysUserBo user, PageQuery pageQuery) { + Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public List selectUserList(SysUserBo user) { + return baseMapper.selectUserList(this.buildQueryWrapper(user)); + } + + private Wrapper buildQueryWrapper(SysUserBo user) { + Map params = user.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }); + return wrapper; + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery) { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); + Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectUnallocatedList(SysUserBo user, PageQuery pageQuery) { + List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); + Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserByUserName(String userName) { + return baseMapper.selectUserByUserName(userName); + } + + /** + * 通过OpenId查询用户 + * + * @param openId 用户名 + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserByOpenId(String openId) { + return baseMapper.selectUserByOpenId(openId); + } + + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserByPhonenumber(String phonenumber) { + return baseMapper.selectUserByPhonenumber(phonenumber); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserById(Long userId) { + return baseMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) { + List list = roleMapper.selectRolesByUserName(userName); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysRoleVo::getRoleName); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) { + List list = postMapper.selectPostsByUserName(userName); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysPostVo::getPostName); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + */ + @Override + public boolean checkPhoneUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + */ + @Override + public boolean checkEmailUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验用户是否允许操作 + * + * @param userId 用户ID + */ + @Override + public void checkUserAllowed(Long userId) { + if (ObjectUtil.isNotNull(userId) && LoginHelper.isSuperAdmin(userId)) { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) { + if (ObjectUtil.isNull(userId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + if (ObjectUtil.isNull(baseMapper.selectUserById(userId))) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser(SysUserBo user) { + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + // 新增用户信息 + int rows = baseMapper.insert(sysUser); + user.setUserId(sysUser.getUserId()); + // 新增用户岗位关联 + insertUserPost(user, false); + // 新增用户与角色管理 + insertUserRole(user, false); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public SysUser registerUser(SysUserBo user, String tenantId) { + user.setCreateBy(user.getUserId()); + user.setUpdateBy(user.getUserId()); + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + if (sysUser != null) { + sysUser.setTenantId(tenantId); + } + baseMapper.insert(sysUser); + return sysUser; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser(SysUserBo user) { + // 新增用户与角色管理 + insertUserRole(user, true); + // 新增用户与岗位管理 + insertUserPost(user, true); + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + // 防止错误更新后导致的数据误删除 + int flag = baseMapper.updateById(sysUser); + if (flag < 1) { + throw new ServiceException("修改用户" + user.getUserName() + "信息失败"); + } + return flag; + } + + /** + * 小程序 - 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public SysUserVo updateXcxUser(SysUserBo user) { + baseMapper.updateXcxUser(user); + return baseMapper.selectUserByOpenId(user.getOpenId()); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void insertUserAuth(Long userId, Long[] roleIds) { + insertUserRole(userId, roleIds, true); + } + + /** + * 修改用户状态 + * + * @param userId 用户ID + * @param status 帐号状态 + * @return 结果 + */ + @Override + public int updateUserStatus(Long userId, String status) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId)); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUserBo user) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhonenumber, user.getPhonenumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getSex, user.getSex()) + .eq(SysUser::getUserId, user.getUserId())); + } + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(Long userId, String avatar) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId)) > 0; + } + + @Override + public boolean updateUserName(Long userId, String nickName) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getNickName, nickName) + .eq(SysUser::getUserId, userId)) > 0; + } + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(Long userId, String password) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId)); + } + + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + * @param clear 清除已存在的关联数据 + */ + private void insertUserRole(SysUserBo user, boolean clear) { + this.insertUserRole(user.getUserId(), user.getRoleIds(), clear); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + * @param clear 清除已存在的关联数据 + */ + private void insertUserPost(SysUserBo user, boolean clear) { + Long[] posts = user.getPostIds(); + if (ArrayUtil.isNotEmpty(posts)) { + if (clear) { + // 删除用户与岗位关联 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, user.getUserId())); + } + // 新增用户与岗位管理 + List list = StreamUtils.toList(List.of(posts), postId -> { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + return up; + }); + userPostMapper.insertBatch(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + * @param clear 清除已存在的关联数据 + */ + private void insertUserRole(Long userId, Long[] roleIds, boolean clear) { + if (ArrayUtil.isNotEmpty(roleIds)) { + // 判断是否具有此角色的操作权限 + List roles = roleMapper.selectRoleList(new LambdaQueryWrapper<>()); + if (CollUtil.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色的数据"); + } + List roleList = StreamUtils.toList(roles, SysRoleVo::getRoleId); + if (!LoginHelper.isSuperAdmin(userId)) { + roleList.remove(UserConstants.SUPER_ADMIN_ID); + } + List canDoRoleList = StreamUtils.filter(List.of(roleIds), roleList::contains); + if (CollUtil.isEmpty(canDoRoleList)) { + throw new ServiceException("没有权限访问角色的数据"); + } + if (clear) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + } + // 新增用户与角色管理 + List list = StreamUtils.toList(canDoRoleList, roleId -> { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + return ur; + }); + userRoleMapper.insertBatch(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById(Long userId) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); + // 防止更新失败导致的数据删除 + int flag = baseMapper.deleteById(userId); + if (flag < 1) { + throw new ServiceException("删除用户失败!"); + } + return flag; + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds(Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(userId); + checkUserDataScope(userId); + } + List ids = List.of(userIds); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); + // 防止更新失败导致的数据删除 + int flag = baseMapper.deleteBatchIds(ids); + if (flag < 1) { + throw new ServiceException("删除用户失败!"); + } + return flag; + } + + @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") + @Override + public String selectUserNameById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/util/OrderNumberGenerator.java b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/util/OrderNumberGenerator.java new file mode 100644 index 00000000..819fe984 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/xmzs/system/util/OrderNumberGenerator.java @@ -0,0 +1,24 @@ +package com.xmzs.system.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ThreadLocalRandom; +public class OrderNumberGenerator { + // 订单编号前缀 + private static final String PREFIX = "NO"; + + // 时间格式化 + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); + + // 生成订单编号 + public static String generate() { + // 获取当前日期时间字符串 + String dateTimeStr = DATE_FORMAT.format(new Date()); + + // 生成随机数 (这里举例生成一个5位随机数) + int randomNum = ThreadLocalRandom.current().nextInt(10000, 99999); + + // 拼接订单编号 + return dateTimeStr + randomNum; + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md new file mode 100644 index 00000000..c938b1e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 00000000..3edbf94e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 00000000..a5c7b0a0 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 00000000..e27d8a31 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 00000000..536b7b0f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 00000000..7e12aa29 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 00000000..8290abe7 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 00000000..f821637f --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 00000000..91ba8847 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml new file mode 100644 index 00000000..a547b006 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml new file mode 100644 index 00000000..4b5bab15 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 00000000..59ba703b --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 00000000..5aa2328c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 00000000..e8fcc343 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + 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 sur on sur.role_id = r.role_id + left join sys_user u on u.user_id = sur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 00000000..1409a868 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml new file mode 100644 index 00000000..4e3a5c79 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml new file mode 100644 index 00000000..f1937969 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 00000000..f24f084c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, + u.tenant_id, + u.user_balance, + u.user_grade, + u.dept_id, + u.user_name, + u.nick_name, + u.user_type, + u.email, + u.avatar, + u.wx_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 sur on u.user_id = sur.user_id + left join sys_role r on r.role_id = sur.role_id + + + + update sys_user + set nick_name = #{nickName}, + wx_avatar = #{wxAvatar} + WHERE user_id = #{userId} + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 00000000..7391427c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 00000000..c819a578 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/script/bin/ry.bat b/script/bin/ry.bat new file mode 100644 index 00000000..94434a91 --- /dev/null +++ b/script/bin/ry.bat @@ -0,0 +1,68 @@ +rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 +@echo off + +rem jar平级目录 +set AppName=ruoyi-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" + + +ECHO. + ECHO. [1] 启动%AppName% + ECHO. [2] 关闭%AppName% + ECHO. [3] 重启%AppName% + ECHO. [4] 启动状态 %AppName% + ECHO. [5] 退 出 +ECHO. + +ECHO.请输入选择项目的序号: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting…… +echo Start %AppName% success... +goto:eof + +rem 函数stop通过jps命令查找pid并结束进程 +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem 根据进程ID,kill进程 + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/script/bin/ry.sh b/script/bin/ry.sh new file mode 100644 index 00000000..0cf5ce9b --- /dev/null +++ b/script/bin/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=ruoyi-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/script/docker/database.yml b/script/docker/database.yml new file mode 100644 index 00000000..0368fd27 --- /dev/null +++ b/script/docker/database.yml @@ -0,0 +1,61 @@ +version: '3' + +services: + # 此镜像仅用于测试 正式环境需自行安装数据库 + # SID: XE user: system password: oracle + oracle: + image: tekintian/oracle12c:latest + container_name: oracle + environment: + # 时区上海 + TZ: Asia/Shanghai + DBCA_TOTAL_MEMORY: 16192 + ports: + - "18080:8080" + - "1521:1521" + volumes: + # 数据挂载 + - "/docker/oracle/data:/u01/app/oracle" + network_mode: "host" + + # 此镜像仅用于测试 正式环境需自行安装数据库 + sqlserver: + image: mcr.microsoft.com/mssql/server:2017-latest + container_name: sqlserver + environment: + # 时区上海 + TZ: Asia/Shanghai + ACCEPT_EULA: "Y" + SA_PASSWORD: "Ruoyi@123" + ports: + - "1433:1433" + volumes: + # 数据挂载 + - "/docker/sqlserver/data:/var/opt/mssql" + network_mode: "host" + + postgres: + image: postgres:14.2 + container_name: postgres + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5432:5432" + volumes: + - /docker/postgres/data:/var/lib/postgresql/data + network_mode: "host" + + postgres13: + image: postgres:13.6 + container_name: postgres13 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5433:5432" + volumes: + - /docker/postgres13/data:/var/lib/postgresql/data + network_mode: "host" diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml new file mode 100644 index 00000000..7f5f5b33 --- /dev/null +++ b/script/docker/docker-compose.yml @@ -0,0 +1,154 @@ +version: '3' + +services: + mysql: + image: mysql:8.0.33 + container_name: mysql + environment: + # 时区上海 + TZ: Asia/Shanghai + # root 密码 + MYSQL_ROOT_PASSWORD: root + # 初始化数据库(后续的初始化sql会在这个库执行) + MYSQL_DATABASE: ry-vue + ports: + - "3306:3306" + volumes: + # 数据挂载 + - /docker/mysql/data/:/var/lib/mysql/ + # 配置挂载 + - /docker/mysql/conf/:/etc/mysql/conf.d/ + command: + # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配) + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + privileged: true + network_mode: "host" + + nginx-web: + image: nginx:1.23.4 + container_name: nginx-web + environment: + # 时区上海 + TZ: Asia/Shanghai + ports: + - "80:80" + - "443:443" + volumes: + # 证书映射 + - /docker/nginx/cert:/etc/nginx/cert + # 配置文件映射 + - /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf + # 页面目录 + - /docker/nginx/html:/usr/share/nginx/html + # 日志目录 + - /docker/nginx/log:/var/log/nginx + privileged: true + network_mode: "host" + + redis: + image: redis:6.2.12 + container_name: redis + ports: + - "6379:6379" + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/redis/conf:/redis/config:rw + # 数据文件 + - /docker/redis/data/:/redis/data/:rw + command: "redis-server /redis/config/redis.conf" + privileged: true + network_mode: "host" + + minio: + image: minio/minio:RELEASE.2023-04-13T03-08-07Z + container_name: minio + ports: + # api 端口 + - "9000:9000" + # 控制台端口 + - "9001:9001" + environment: + # 时区上海 + TZ: Asia/Shanghai + # 管理后台用户名 + MINIO_ROOT_USER: ruoyi + # 管理后台密码,最小8个字符 + MINIO_ROOT_PASSWORD: ruoyi123 + # https需要指定域名 + #MINIO_SERVER_URL: "https://xxx.com:9000" + #MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001" + # 开启压缩 on 开启 off 关闭 + MINIO_COMPRESS: "off" + # 扩展名 .pdf,.doc 为空 所有类型均压缩 + MINIO_COMPRESS_EXTENSIONS: "" + # mime 类型 application/pdf 为空 所有类型均压缩 + MINIO_COMPRESS_MIME_TYPES: "" + volumes: + # 映射当前目录下的data目录至容器内/data目录 + - /docker/minio/data:/data + # 映射配置目录 + - /docker/minio/config:/root/.minio/ + command: server --address ':9000' --console-address ':9001' /data # 指定容器中的目录 /data + privileged: true + network_mode: "host" + + ruoyi-server1: + image: ruoyi/ruoyi-server:5.0.0 + container_name: ruoyi-server1 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8080 + volumes: + # 配置文件 + - /docker/server1/logs/:/ruoyi/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/ruoyi/skywalking/agent + privileged: true + network_mode: "host" + + ruoyi-server2: + image: ruoyi/ruoyi-server:5.0.0 + container_name: ruoyi-server2 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8081 + volumes: + # 配置文件 + - /docker/server2/logs/:/ruoyi/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/ruoyi/skywalking/agent + privileged: true + network_mode: "host" + + ruoyi-monitor-admin: + image: ruoyi/ruoyi-monitor-admin:5.0.0 + container_name: ruoyi-monitor-admin + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/monitor/logs/:/ruoyi/monitor/logs + privileged: true + network_mode: "host" + + ruoyi-xxl-job-admin: + image: ruoyi/ruoyi-xxl-job-admin:5.0.0 + container_name: ruoyi-xxl-job-admin + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/xxljob/logs/:/ruoyi/xxljob/logs + privileged: true + network_mode: "host" diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf new file mode 100644 index 00000000..e9630aaf --- /dev/null +++ b/script/docker/nginx/conf/nginx.conf @@ -0,0 +1,111 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + # 限制body大小 + client_max_body_size 100m; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + upstream server { + ip_hash; + server 127.0.0.1:8080; + server 127.0.0.1:8081; + } + + upstream monitor-admin { + server 127.0.0.1:9090; + } + + upstream xxljob-admin { + server 127.0.0.1:9100; + } + + server { + listen 80; + server_name localhost; + + # https配置参考 start + #listen 443 ssl; + + # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径 + #ssl on; + #ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_session_timeout 5m; + #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + #ssl_prefer_server_ciphers on; + # https配置参考 end + + # 演示环境配置 拦截除 GET POST 之外的所有请求 + # if ($request_method !~* GET|POST) { + # rewrite ^/(.*)$ /403; + # } + + # location = /403 { + # default_type application/json; + # return 200 '{"msg":"演示模式,不允许操作","code":500}'; + # } + + # 限制外网访问内网 actuator 相关路径 + location ~ ^(/[^/]*)?/actuator(/.*)?$ { + return 403; + } + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html index.htm; + } + + location /prod-api/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://server/; + } + + # https 会拦截内链所有的 http 请求 造成功能无法使用 + # 解决方案1 将 admin 服务 也配置成 https + # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 + location /admin/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://monitor-admin/admin/; + } + + # https 会拦截内链所有的 http 请求 造成功能无法使用 + # 解决方案1 将 xxljob 服务 也配置成 https + # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 + location /xxl-job-admin/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://xxljob-admin/xxl-job-admin/; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/script/docker/redis/conf/redis.conf b/script/docker/redis/conf/redis.conf new file mode 100644 index 00000000..72255c61 --- /dev/null +++ b/script/docker/redis/conf/redis.conf @@ -0,0 +1,28 @@ +# redis 密码 +requirepass ruoyi123 + +# key 监听器配置 +# notify-keyspace-events Ex + +# 配置持久化文件存储路径 +dir /redis/data +# 配置rdb +# 15分钟内有至少1个key被更改则进行快照 +save 900 1 +# 5分钟内有至少10个key被更改则进行快照 +save 300 10 +# 1分钟内有至少10000个key被更改则进行快照 +save 60 10000 +# 开启压缩 +rdbcompression yes +# rdb文件名 用默认的即可 +dbfilename dump.rdb + +# 开启aof +appendonly yes +# 文件名 +appendfilename "appendonly.aof" +# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢 +# appendfsync always +appendfsync everysec +# appendfsync no diff --git a/script/docker/redis/data/README.md b/script/docker/redis/data/README.md new file mode 100644 index 00000000..fbc5474b --- /dev/null +++ b/script/docker/redis/data/README.md @@ -0,0 +1 @@ +数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据 \ No newline at end of file diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql new file mode 100644 index 00000000..0456f11e --- /dev/null +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -0,0 +1,1119 @@ +-- ---------------------------- +-- 租户表 +-- ---------------------------- +create table sys_tenant ( + id number(20) not null, + tenant_id varchar2(20) not null, + contact_user_name varchar2(20) default '', + contact_phone varchar2(20) default '', + company_name varchar2(50) default '', + license_number varchar2(30) default '', + address varchar2(200) default '', + intro varchar2(200) default '', + domain varchar2(200) default '', + remark varchar2(200) default '', + package_id number(20) default null, + expire_time date default null, + account_count number(4) default -1, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_tenant add constraint pk_sys_tenant primary key (id); + +comment on table sys_tenant is '租户表'; +comment on column sys_tenant.tenant_id is '租户编号'; +comment on column sys_tenant.contact_phone is '联系电话'; +comment on column sys_tenant.company_name is '企业名称'; +comment on column sys_tenant.company_name is '联系人'; +comment on column sys_tenant.license_number is '统一社会信用代码'; +comment on column sys_tenant.address is '地址'; +comment on column sys_tenant.intro is '企业简介'; +comment on column sys_tenant.remark is '备注'; +comment on column sys_tenant.package_id is '租户套餐编号'; +comment on column sys_tenant.expire_time is '过期时间'; +comment on column sys_tenant.account_count is '用户数量(-1不限制)'; +comment on column sys_tenant.status is '租户状态(0正常 1停用)'; +comment on column sys_tenant.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_tenant.create_dept is '创建部门'; +comment on column sys_tenant.create_by is '创建者'; +comment on column sys_tenant.create_time is '创建时间'; +comment on column sys_tenant.update_by is '更新者'; +comment on column sys_tenant.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-租户表数据 +-- ---------------------------- + +insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, sysdate, null, null); + + +-- ---------------------------- +-- 租户套餐表 +-- ---------------------------- +create table sys_tenant_package ( + package_id number(20) not null, + package_name varchar2(20) default '', + menu_ids varchar2(3000) default '', + remark varchar2(200) default '', + menu_check_strictly number(1) default 1, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_tenant_package add constraint pk_sys_tenant_package primary key (package_id); + +comment on table sys_tenant_package is '租户套餐表'; +comment on column sys_tenant_package.package_id is '租户套餐id'; +comment on column sys_tenant_package.package_name is '套餐名称'; +comment on column sys_tenant_package.menu_ids is '关联菜单id'; +comment on column sys_tenant_package.remark is '备注'; +comment on column sys_tenant_package.status is '状态(0正常 1停用)'; +comment on column sys_tenant_package.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_tenant_package.create_dept is '创建部门'; +comment on column sys_tenant_package.create_by is '创建者'; +comment on column sys_tenant_package.create_time is '创建时间'; +comment on column sys_tenant_package.update_by is '更新者'; +comment on column sys_tenant_package.update_time is '更新时间'; + + +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +create table sys_dept ( + dept_id number(20) not null, + tenant_id varchar2(20) default '000000', + parent_id number(20) default 0, + ancestors varchar2(500) default '', + dept_name varchar2(30) default '', + order_num number(4) default 0, + leader varchar2(20) default null, + phone varchar2(11) default null, + email varchar2(50) default null, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_dept add constraint pk_sys_dept primary key (dept_id); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门id'; +comment on column sys_dept.tenant_id is '租户编号'; +comment on column sys_dept.parent_id is '父部门id'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_dept.create_dept is '创建部门'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', 0, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', 3, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', 4, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', 5, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +create table sys_user ( + user_id number(20) not null, + tenant_id varchar2(20) default '000000', + dept_id number(20) default null, + user_name varchar2(40) not null, + nick_name varchar2(40) not null, + user_type varchar2(10) default 'sys_user', + email varchar2(50) default '', + phonenumber varchar2(11) default '', + sex char(1) default '0', + avatar number(20) default null, + password varchar2(100) default '', + status char(1) default '0', + del_flag char(1) default '0', + login_ip varchar2(128) default '', + login_date date, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default '' +); + +alter table sys_user add constraint pk_sys_user primary key (user_id); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.tenant_id is '租户编号'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像路径'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_user.login_ip is '最后登录IP'; +comment on column sys_user.login_date is '最后登录时间'; +comment on column sys_user.create_dept is '创建部门'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '管理员'); +insert into sys_user values(2, '000000', 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +create table sys_post ( + post_id number(20) not null, + tenant_id varchar2(20) default '000000', + post_code varchar2(64) not null, + post_name varchar2(50) not null, + post_sort number(4) not null, + status char(1) not null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) +); + +alter table sys_post add constraint pk_sys_post primary key (post_id); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.tenant_id is '租户编号'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_dept is '创建部门'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, '000000', 'ceo', '董事长', 1, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(2, '000000', 'se', '项目经理', 2, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(3, '000000', 'hr', '人力资源', 3, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(4, '000000', 'user', '普通员工', 4, '0', 103, 1, sysdate, null, null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +create table sys_role ( + role_id number(20) not null, + tenant_id varchar2(20) default '000000', + role_name varchar2(30) not null, + role_key varchar2(100) not null, + role_sort number(4) not null, + data_scope char(1) default '1', + menu_check_strictly number(1) default 1, + dept_check_strictly number(1) default 1, + status char(1) not null, + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_role add constraint pk_sys_role primary key (role_id); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.tenant_id is '租户编号'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_role.create_dept is '创建部门'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '000000', '超级管理员', 'superadmin', 1, 1, 1, 1, '0', '0', 103, 1, sysdate, null, null, '超级管理员'); +insert into sys_role values('2', '000000', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 103, 1, sysdate, null, null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +create table sys_menu ( + menu_id number(20) not null, + menu_name varchar2(50) not null, + parent_id number(20) default 0, + order_num number(4) default 0, + path varchar(200) default '', + component varchar(255) default null, + query_param varchar(255) default null, + is_frame number(1) default 1, + is_cache number(1) default 0, + menu_type char(1) default '', + visible char(1) default 0, + status char(1) default 0, + perms varchar2(100) default null, + icon varchar2(100) default '#', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date , + remark varchar2(500) default '' +); + +alter table sys_menu add constraint pk_sys_menu primary key (menu_id); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '请求地址'; +comment on column sys_menu.component is '路由地址'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_dept is '创建部门'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, sysdate, null, null, '系统管理目录'); +insert into sys_menu values('6', '租户管理', '0', '2', 'tenant', null, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, sysdate, null, null, '租户管理目录'); +insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, sysdate, null, null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, sysdate, null, null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 103, 1, sysdate, null, null, 'RuoYi-Vue-Plus官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, sysdate, null, null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, sysdate, null, null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, sysdate, null, null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, sysdate, null, null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, sysdate, null, null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, sysdate, null, null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, sysdate, null, null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, sysdate, null, null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, sysdate, null, null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, sysdate, null, null, '在线用户菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, sysdate, null, null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 103, 1, sysdate, null, null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, sysdate, null, null, '代码生成菜单'); +insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate, null, null, '租户管理菜单'); +insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate, null, null, '租户套餐管理菜单'); +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate, null, null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, sysdate, null, null, '文件管理菜单'); +-- xxl-job-admin控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list', 'job', 103, 1, sysdate, null, null, 'Xxl-Job控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, sysdate, null, null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, sysdate, null, null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, sysdate, null, null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, sysdate, null, null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, sysdate, null, null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, sysdate, null, null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, sysdate, null, null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, sysdate, null, null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, sysdate, null, null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, sysdate, null, null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, sysdate, null, null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, sysdate, null, null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, sysdate, null, null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 103, 1, sysdate, null, null, ''); +-- 租户管理相关按钮 +insert into sys_menu values('1606', '租户查询', '121', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1607', '租户新增', '121', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1608', '租户修改', '121', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1609', '租户删除', '121', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1610', '租户导出', '121', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, sysdate, null, null, ''); +-- 租户套餐管理相关按钮 +insert into sys_menu values('1611', '租户套餐查询', '122', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1612', '租户套餐新增', '122', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1613', '租户套餐修改', '122', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1614', '租户套餐删除', '122', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1615', '租户套餐导出', '122', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, sysdate, null, null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +create table sys_user_role ( + user_id number(20) not null, + role_id number(20) not null +); + +alter table sys_user_role add constraint pk_sys_user_role primary key (user_id, role_id); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +create table sys_role_menu ( + role_id number(20) not null, + menu_id number(20) not null +); + +alter table sys_role_menu add constraint pk_sys_role_menu primary key (role_id, menu_id); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +create table sys_role_dept ( + role_id number(20) not null, + dept_id number(20) not null +); + +alter table sys_role_dept add constraint pk_sys_role_dept primary key (role_id, dept_id); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +create table sys_user_post ( + user_id number(20) not null, + post_id number(20) not null +); + +alter table sys_user_post add constraint pk_sys_user_post primary key (user_id, post_id); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +create table sys_oper_log ( + oper_id number(20) not null, + tenant_id varchar2(20) default '000000', + title varchar2(50) default '', + business_type number(2) default 0, + method varchar2(100) default '', + request_method varchar(10) default '', + operator_type number(1) default 0, + oper_name varchar2(50) default '', + dept_name varchar2(50) default '', + oper_url varchar2(255) default '', + oper_ip varchar2(128) default '', + oper_location varchar2(255) default '', + oper_param varchar2(2100) default '', + json_result varchar2(2100) default '', + status number(1) default 0, + error_msg varchar2(2100) default '', + oper_time date, + cost_time number(20) default 0 +); + +alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id); +create index idx_sys_oper_log_bt on sys_oper_log (business_type); +create index idx_sys_oper_log_s on sys_oper_log (status); +create index idx_sys_oper_log_ot on sys_oper_log (oper_time); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.tenant_id is '租户编号'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; +comment on column sys_oper_log.cost_time is '消耗时间'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +create table sys_dict_type ( + dict_id number(20) not null, + tenant_id varchar2(20) default '000000', + dict_name varchar2(100) default '', + dict_type varchar2(100) default '', + status char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_type add constraint pk_sys_dict_type primary key (dict_id); +create unique index sys_dict_type_index1 on sys_dict_type (tenant_id, dict_type); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.status is '状态(0正常 1停用)'; +comment on column sys_dict_type.create_dept is '创建部门'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '000000', '用户性别', 'sys_user_sex', '0', 103, 1, sysdate, null, null, '用户性别列表'); +insert into sys_dict_type values(2, '000000', '菜单状态', 'sys_show_hide', '0', 103, 1, sysdate, null, null, '菜单状态列表'); +insert into sys_dict_type values(3, '000000', '系统开关', 'sys_normal_disable', '0', 103, 1, sysdate, null, null, '系统开关列表'); +insert into sys_dict_type values(6, '000000', '系统是否', 'sys_yes_no', '0', 103, 1, sysdate, null, null, '系统是否列表'); +insert into sys_dict_type values(7, '000000', '通知类型', 'sys_notice_type', '0', 103, 1, sysdate, null, null, '通知类型列表'); +insert into sys_dict_type values(8, '000000', '通知状态', 'sys_notice_status', '0', 103, 1, sysdate, null, null, '通知状态列表'); +insert into sys_dict_type values(9, '000000', '操作类型', 'sys_oper_type', '0', 103, 1, sysdate, null, null, '操作类型列表'); +insert into sys_dict_type values(10, '000000', '系统状态', 'sys_common_status', '0', 103, 1, sysdate, null, null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +create table sys_dict_data ( + dict_code number(20) not null, + tenant_id varchar2(20) default '000000', + dict_sort number(4) default 0, + dict_label varchar2(100) default '', + dict_value varchar2(100) default '', + dict_type varchar2(100) default '', + css_class varchar2(100) default null, + list_class varchar2(100) default null, + is_default char(1) default 'N', + status char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_data add constraint pk_sys_dict_data primary key (dict_code); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典主键'; +comment on column sys_dict_data.tenant_id is '租户编号'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.status is '状态(0正常 1停用)'; +comment on column sys_dict_data.create_dept is '创建部门'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 103, 1, sysdate, null, null, '性别男'); +insert into sys_dict_data values(2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 103, 1, sysdate, null, null, '性别女'); +insert into sys_dict_data values(3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 103, 1, sysdate, null, null, '性别未知'); +insert into sys_dict_data values(4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 103, 1, sysdate, null, null, '显示菜单'); +insert into sys_dict_data values(5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '隐藏菜单'); +insert into sys_dict_data values(6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '停用状态'); +insert into sys_dict_data values(12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 103, 1, sysdate, null, null, '系统默认是'); +insert into sys_dict_data values(13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '系统默认否'); +insert into sys_dict_data values(14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 103, 1, sysdate, null, null, '通知'); +insert into sys_dict_data values(15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 103, 1, sysdate, null, null, '公告'); +insert into sys_dict_data values(16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '关闭状态'); +insert into sys_dict_data values(29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, sysdate, null, null, '其他操作'); +insert into sys_dict_data values(18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, sysdate, null, null, '新增操作'); +insert into sys_dict_data values(19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, sysdate, null, null, '修改操作'); +insert into sys_dict_data values(20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '删除操作'); +insert into sys_dict_data values(21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 103, 1, sysdate, null, null, '授权操作'); +insert into sys_dict_data values(22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, sysdate, null, null, '导出操作'); +insert into sys_dict_data values(23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, sysdate, null, null, '导入操作'); +insert into sys_dict_data values(24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '强退操作'); +insert into sys_dict_data values(25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, sysdate, null, null, '生成操作'); +insert into sys_dict_data values(26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '清空操作'); +insert into sys_dict_data values(27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 103, 1, sysdate, null, null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +create table sys_config ( + config_id number(20) not null, + tenant_id varchar2(20) default '000000', + config_name varchar2(100) default '', + config_key varchar2(100) default '', + config_value varchar2(100) default '', + config_type char(1) default 'N', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); +alter table sys_config add constraint pk_sys_config primary key (config_id); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.tenant_id is '租户编号'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_dept is '创建部门'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, sysdate, null, null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, sysdate, null, null, '初始化密码 123456' ); +insert into sys_config values(3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, sysdate, null, null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, sysdate, null, null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, sysdate, null, null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +create table sys_logininfor ( + info_id number(20) not null, + tenant_id varchar2(20) default '000000', + user_name varchar2(50) default '', + ipaddr varchar2(128) default '', + login_location varchar2(255) default '', + browser varchar2(50) default '', + os varchar2(50) default '', + status char(1) default '0', + msg varchar2(255) default '', + login_time date +); + +alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id); +create index idx_sys_logininfor_s on sys_logininfor (status); +create index idx_sys_logininfor_lt on sys_logininfor (login_time); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.tenant_id is '租户编号'; +comment on column sys_logininfor.user_name is '登录账号'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +create table sys_notice ( + notice_id number(20) not null, + tenant_id varchar2(20) default '000000', + notice_title varchar2(50) not null, + notice_type char(1) not null, + notice_content clob default null, + status char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(255) default null +); + +alter table sys_notice add constraint pk_sys_notice primary key (notice_id); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告主键'; +comment on column sys_notice.tenant_id is '租户编号'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_dept is '创建部门'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, sysdate, null, null, '管理员'); +insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, sysdate, null, null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +create table gen_table ( + table_id number(20) not null, + table_name varchar2(200) default '', + table_comment varchar2(500) default '', + sub_table_name varchar(64) default null, + sub_table_fk_name varchar(64) default null, + class_name varchar2(100) default '', + tpl_category varchar2(200) default 'crud', + package_name varchar2(100), + module_name varchar2(30), + business_name varchar2(30), + function_name varchar2(50), + function_author varchar2(50), + gen_type char(1) default '0', + gen_path varchar2(200) default '/', + options varchar2(1000), + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table gen_table add constraint pk_gen_table primary key (table_id); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(crud单表操作 tree树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_dept is '创建部门'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +create table gen_table_column ( + column_id number(20) not null, + table_id number(20), + column_name varchar2(200), + column_comment varchar2(500), + column_type varchar2(100), + java_type varchar2(500), + java_field varchar2(200), + is_pk char(1), + is_increment char(1), + is_required char(1), + is_insert char(1), + is_edit char(1), + is_list char(1), + is_query char(1), + query_type varchar(200) default 'EQ', + html_type varchar(200), + dict_type varchar(200) default '', + sort number(4), + create_dept number(20) default null, + create_by number(20) default null, + create_time date , + update_by number(20) default null, + update_time date +); + +alter table gen_table_column add constraint pk_gen_table_column primary key (column_id); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_dept is '创建部门'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +create table sys_oss ( + oss_id number(20) not null, + tenant_id varchar2(20) default '000000', + file_name varchar(255) not null, + original_name varchar(255) not null, + file_suffix varchar(10) not null, + url varchar(500) not null, + service varchar(20) default 'minio' not null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_oss add constraint pk_sys_oss primary key (oss_id); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.tenant_id is '租户编码'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.service is '服务商'; +comment on column sys_oss.create_dept is '创建部门'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.create_by is '上传者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.update_by is '更新者'; + + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +create table sys_oss_config ( + oss_config_id number(20) not null, + tenant_id varchar2(20) default '000000', + config_key varchar(20) not null, + access_key varchar(255) default '', + secret_key varchar(255) default '', + bucket_name varchar(255) default '', + prefix varchar(255) default '', + endpoint varchar(255) default '', + domain varchar(255) default '', + is_https char(1) default 'N', + region varchar(255) default '', + access_policy char(1) default '1' not null, + status char(1) default '1', + ext1 varchar(255) default '', + remark varchar(500) default null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主建'; +comment on column sys_oss_config.tenant_id is '租户编码'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accesskey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.remark is '备注'; +comment on column sys_oss_config.create_dept is '创建部门'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; + +insert into sys_oss_config values (1, '000000', 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1', '0', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); + + +-- ---------------------------- +-- 钩子 ,用于session连接之后 自动设置默认的date类型格式化 简化时间查询 +-- 如需设置其它配置 可在此钩子内任意增加处理语句 +-- 例如: SELECT * FROM sys_user WHERE create_time BETWEEN '2022-03-01 00:00:00' AND '2022-04-01 00:00:00' +-- ---------------------------- +create or replace trigger login_trg +after logon on database +begin +execute immediate 'alter session set nls_date_format=''YYYY-MM-DD HH24:MI:SS'''; +end; diff --git a/script/sql/oracle/oracle_test.sql b/script/sql/oracle/oracle_test.sql new file mode 100644 index 00000000..fd93786d --- /dev/null +++ b/script/sql/oracle/oracle_test.sql @@ -0,0 +1,204 @@ +create table test_demo ( + id number(20) not null, + tenant_id varchar2(20) default '000000', + dept_id number(20) default null, + user_id number(20) default null, + order_num number(10) default 0, + test_key varchar2(255) default null, + value varchar2(255) default null, + version number(10) default 0, + create_dept number(20) default null, + create_time date, + create_by number(20) default null, + update_time date, + update_by number(20) default null, + del_flag number(2) default 0 +); + +alter table test_demo add constraint pk_test_demo primary key (id); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.tenant_id is '租户编号'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_dept is '创建部门'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +create table test_tree ( + id number(20) not null, + tenant_id varchar2(20) default '000000', + parent_id number(20) default 0, + dept_id number(20) default null, + user_id number(20) default null, + tree_name varchar2(255) default null, + version number(10) default 0, + create_dept number(20) default null, + create_time date, + create_by number(20) default null, + update_time date, + update_by number(20) default null, + del_flag number(2) default 0 +); + +alter table test_tree add constraint pk_test_tree primary key (id); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.tenant_id is '租户编号'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_dept is '创建部门'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +insert into sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) values (3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, 3, sysdate, null); +insert into sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) values (4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, 4, sysdate, null); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (5, '测试菜单', 0, 5, 'demo', null, 1, 0, 'M', '0', '0', null, 'star', 103, 1, sysdate, 1, sysdate, ''); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate, null, null, '测试单表菜单'); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate, null, null, '测试树表菜单'); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) values (3, '000000', '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 103, 1, sysdate, null, null, null); +insert into sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) values (4, '000000', '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 103, 1, sysdate, null, null, null); + +insert into sys_role_menu(role_id, menu_id) values (3, 1); +insert into sys_role_menu(role_id, menu_id) values (3, 5); +insert into sys_role_menu(role_id, menu_id) values (3, 100); +insert into sys_role_menu(role_id, menu_id) values (3, 101); +insert into sys_role_menu(role_id, menu_id) values (3, 102); +insert into sys_role_menu(role_id, menu_id) values (3, 103); +insert into sys_role_menu(role_id, menu_id) values (3, 104); +insert into sys_role_menu(role_id, menu_id) values (3, 105); +insert into sys_role_menu(role_id, menu_id) values (3, 106); +insert into sys_role_menu(role_id, menu_id) values (3, 107); +insert into sys_role_menu(role_id, menu_id) values (3, 108); +insert into sys_role_menu(role_id, menu_id) values (3, 500); +insert into sys_role_menu(role_id, menu_id) values (3, 501); +insert into sys_role_menu(role_id, menu_id) values (3, 1001); +insert into sys_role_menu(role_id, menu_id) values (3, 1002); +insert into sys_role_menu(role_id, menu_id) values (3, 1003); +insert into sys_role_menu(role_id, menu_id) values (3, 1004); +insert into sys_role_menu(role_id, menu_id) values (3, 1005); +insert into sys_role_menu(role_id, menu_id) values (3, 1006); +insert into sys_role_menu(role_id, menu_id) values (3, 1007); +insert into sys_role_menu(role_id, menu_id) values (3, 1008); +insert into sys_role_menu(role_id, menu_id) values (3, 1009); +insert into sys_role_menu(role_id, menu_id) values (3, 1010); +insert into sys_role_menu(role_id, menu_id) values (3, 1011); +insert into sys_role_menu(role_id, menu_id) values (3, 1012); +insert into sys_role_menu(role_id, menu_id) values (3, 1013); +insert into sys_role_menu(role_id, menu_id) values (3, 1014); +insert into sys_role_menu(role_id, menu_id) values (3, 1015); +insert into sys_role_menu(role_id, menu_id) values (3, 1016); +insert into sys_role_menu(role_id, menu_id) values (3, 1017); +insert into sys_role_menu(role_id, menu_id) values (3, 1018); +insert into sys_role_menu(role_id, menu_id) values (3, 1019); +insert into sys_role_menu(role_id, menu_id) values (3, 1020); +insert into sys_role_menu(role_id, menu_id) values (3, 1021); +insert into sys_role_menu(role_id, menu_id) values (3, 1022); +insert into sys_role_menu(role_id, menu_id) values (3, 1023); +insert into sys_role_menu(role_id, menu_id) values (3, 1024); +insert into sys_role_menu(role_id, menu_id) values (3, 1025); +insert into sys_role_menu(role_id, menu_id) values (3, 1026); +insert into sys_role_menu(role_id, menu_id) values (3, 1027); +insert into sys_role_menu(role_id, menu_id) values (3, 1028); +insert into sys_role_menu(role_id, menu_id) values (3, 1029); +insert into sys_role_menu(role_id, menu_id) values (3, 1030); +insert into sys_role_menu(role_id, menu_id) values (3, 1031); +insert into sys_role_menu(role_id, menu_id) values (3, 1032); +insert into sys_role_menu(role_id, menu_id) values (3, 1033); +insert into sys_role_menu(role_id, menu_id) values (3, 1034); +insert into sys_role_menu(role_id, menu_id) values (3, 1035); +insert into sys_role_menu(role_id, menu_id) values (3, 1036); +insert into sys_role_menu(role_id, menu_id) values (3, 1037); +insert into sys_role_menu(role_id, menu_id) values (3, 1038); +insert into sys_role_menu(role_id, menu_id) values (3, 1039); +insert into sys_role_menu(role_id, menu_id) values (3, 1040); +insert into sys_role_menu(role_id, menu_id) values (3, 1041); +insert into sys_role_menu(role_id, menu_id) values (3, 1042); +insert into sys_role_menu(role_id, menu_id) values (3, 1043); +insert into sys_role_menu(role_id, menu_id) values (3, 1044); +insert into sys_role_menu(role_id, menu_id) values (3, 1045); +insert into sys_role_menu(role_id, menu_id) values (3, 1500); +insert into sys_role_menu(role_id, menu_id) values (3, 1501); +insert into sys_role_menu(role_id, menu_id) values (3, 1502); +insert into sys_role_menu(role_id, menu_id) values (3, 1503); +insert into sys_role_menu(role_id, menu_id) values (3, 1504); +insert into sys_role_menu(role_id, menu_id) values (3, 1505); +insert into sys_role_menu(role_id, menu_id) values (3, 1506); +insert into sys_role_menu(role_id, menu_id) values (3, 1507); +insert into sys_role_menu(role_id, menu_id) values (3, 1508); +insert into sys_role_menu(role_id, menu_id) values (3, 1509); +insert into sys_role_menu(role_id, menu_id) values (3, 1510); +insert into sys_role_menu(role_id, menu_id) values (3, 1511); +insert into sys_role_menu(role_id, menu_id) values (4, 5); +insert into sys_role_menu(role_id, menu_id) values (4, 1500); +insert into sys_role_menu(role_id, menu_id) values (4, 1501); +insert into sys_role_menu(role_id, menu_id) values (4, 1502); +insert into sys_role_menu(role_id, menu_id) values (4, 1503); +insert into sys_role_menu(role_id, menu_id) values (4, 1504); +insert into sys_role_menu(role_id, menu_id) values (4, 1505); +insert into sys_role_menu(role_id, menu_id) values (4, 1506); +insert into sys_role_menu(role_id, menu_id) values (4, 1507); +insert into sys_role_menu(role_id, menu_id) values (4, 1508); +insert into sys_role_menu(role_id, menu_id) values (4, 1509); +insert into sys_role_menu(role_id, menu_id) values (4, 1510); +insert into sys_role_menu(role_id, menu_id) values (4, 1511); + +insert into sys_user_role(user_id, role_id) values (3, 3); +insert into sys_user_role(user_id, role_id) values (4, 4); + +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, sysdate, 1, null, null, 0); + +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (2, '000000', 1, 102, 3, '子节点1', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (3, '000000', 2, 102, 3, '子节点2', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (4, '000000', 0, 108, 4, '测试树1', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (5, '000000', 4, 108, 3, '子节点11', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (6, '000000', 4, 108, 3, '子节点22', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (7, '000000', 4, 108, 3, '子节点33', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (8, '000000', 5, 108, 3, '子节点44', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (9, '000000', 6, 108, 3, '子节点55', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (10, '000000', 7, 108, 3, '子节点66', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (11, '000000', 7, 108, 3, '子节点77', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (12, '000000', 10, 108, 3, '子节点88', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (13, '000000', 10, 108, 3, '子节点99', 0, 103, sysdate, 1, null, null, 0); diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql new file mode 100644 index 00000000..bdf28b49 --- /dev/null +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -0,0 +1,1138 @@ +-- ---------------------------- +-- 租户表 +-- ---------------------------- +drop table if exists sys_tenant; +create table if not exists sys_tenant +( + id int8, + tenant_id varchar(20) not null, + contact_user_name varchar(20) default null::varchar, + contact_phone varchar(20) default null::varchar, + company_name varchar(50) default null::varchar, + license_number varchar(30) default null::varchar, + address varchar(200) default null::varchar, + intro varchar(200) default null::varchar, + domain varchar(200) default null::varchar, + remark varchar(200) default null::varchar, + package_id int8, + expire_time timestamp, + account_count int4 default -1, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "pk_sys_tenant" primary key (id) +); + + +comment on table sys_tenant is '租户表'; +comment on column sys_tenant.tenant_id is '租户编号'; +comment on column sys_tenant.contact_phone is '联系电话'; +comment on column sys_tenant.company_name is '企业名称'; +comment on column sys_tenant.company_name is '联系人'; +comment on column sys_tenant.license_number is '统一社会信用代码'; +comment on column sys_tenant.address is '地址'; +comment on column sys_tenant.intro is '企业简介'; +comment on column sys_tenant.domain is '域名'; +comment on column sys_tenant.remark is '备注'; +comment on column sys_tenant.package_id is '租户套餐编号'; +comment on column sys_tenant.expire_time is '过期时间'; +comment on column sys_tenant.account_count is '用户数量(-1不限制)'; +comment on column sys_tenant.status is '租户状态(0正常 1停用)'; +comment on column sys_tenant.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_tenant.create_dept is '创建部门'; +comment on column sys_tenant.create_by is '创建者'; +comment on column sys_tenant.create_time is '创建时间'; +comment on column sys_tenant.update_by is '更新者'; +comment on column sys_tenant.update_time is '更新时间'; + + +-- ---------------------------- +-- 初始化-租户表数据 +-- ---------------------------- + +insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, now(), null, null); + + +-- ---------------------------- +-- 租户套餐表 +-- ---------------------------- +drop table if exists sys_tenant_package; +create table if not exists sys_tenant_package +( + package_id int8, + package_name varchar(20) default ''::varchar, + menu_ids varchar(3000) default ''::varchar, + remark varchar(200) default ''::varchar, + menu_check_strictly bool default true, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "pk_sys_tenant_package" primary key (package_id) +); + + +comment on table sys_tenant_package is '租户套餐表'; +comment on column sys_tenant_package.package_id is '租户套餐id'; +comment on column sys_tenant_package.package_name is '套餐名称'; +comment on column sys_tenant_package.menu_ids is '关联菜单id'; +comment on column sys_tenant_package.remark is '备注'; +comment on column sys_tenant_package.status is '状态(0正常 1停用)'; +comment on column sys_tenant_package.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_tenant_package.create_dept is '创建部门'; +comment on column sys_tenant_package.create_by is '创建者'; +comment on column sys_tenant_package.create_time is '创建时间'; +comment on column sys_tenant_package.update_by is '更新者'; +comment on column sys_tenant_package.update_time is '更新时间'; + + +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table if not exists sys_dept +( + dept_id int8, + tenant_id varchar(20) default '000000'::varchar, + parent_id int8 default 0, + ancestors varchar(500)default ''::varchar, + dept_name varchar(30) default ''::varchar, + order_num int4 default 0, + leader varchar(20) default null::varchar, + phone varchar(11) default null::varchar, + email varchar(50) default null::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "sys_dept_pk" primary key (dept_id) +); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门ID'; +comment on column sys_dept.tenant_id is '租户编号'; +comment on column sys_dept.parent_id is '父部门ID'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_dept.create_dept is '创建部门'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', 0, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', 3, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', 4, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', 5, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table if not exists sys_user +( + user_id int8, + tenant_id varchar(20) default '000000'::varchar, + dept_id int8, + user_name varchar(30) not null, + nick_name varchar(30) not null, + user_type varchar(10) default 'sys_user'::varchar, + email varchar(50) default ''::varchar, + phonenumber varchar(11) default ''::varchar, + sex char default '0'::bpchar, + avatar int8, + password varchar(100) default ''::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + login_ip varchar(128) default ''::varchar, + login_date timestamp, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_user_pk" primary key (user_id) +); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.tenant_id is '租户编号'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像地址'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_user.login_ip is '最后登陆IP'; +comment on column sys_user.login_date is '最后登陆时间'; +comment on column sys_user.create_dept is '创建部门'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- + +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 103, 1, now(), null, null, '管理员'); +insert into sys_user values(2, '000000', 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 103, 1, now(), null, null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table if not exists sys_post +( + post_id int8, + tenant_id varchar(20) default '000000'::varchar, + post_code varchar(64) not null, + post_name varchar(50) not null, + post_sort int4 not null, + status char not null, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_post_pk" primary key (post_id) +); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.tenant_id is '租户编号'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_dept is '创建部门'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, '000000', 'ceo', '董事长', 1, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(2, '000000', 'se', '项目经理', 2, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(3, '000000', 'hr', '人力资源', 3, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(4, '000000', 'user', '普通员工', 4, '0', 103, 1, now(), null, null, ''); + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table if not exists sys_role +( + role_id int8, + tenant_id varchar(20) default '000000'::varchar, + role_name varchar(30) not null, + role_key varchar(100) not null, + role_sort int4 not null, + data_scope char default '1'::bpchar, + menu_check_strictly bool default true, + dept_check_strictly bool default true, + status char not null, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_role_pk" primary key (role_id) +); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.tenant_id is '租户编号'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_role.create_dept is '创建部门'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '000000', '超级管理员', 'superadmin', 1, '1', 't', 't', '0', '0', 103, 1, now(), null, null, '超级管理员'); +insert into sys_role values('2', '000000', '普通角色', 'common', 2, '2', 't', 't', '0', '0', 103, 1, now(), null, null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table if not exists sys_menu +( + menu_id int8, + menu_name varchar(50) not null, + parent_id int8 default 0, + order_num int4 default 0, + path varchar(200) default ''::varchar, + component varchar(255) default null::varchar, + query_param varchar(255) default null::varchar, + is_frame char default '1'::bpchar, + is_cache char default '0'::bpchar, + menu_type char default ''::bpchar, + visible char default '0'::bpchar, + status char default '0'::bpchar, + perms varchar(100) default null::varchar, + icon varchar(100) default '#'::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint "sys_menu_pk" primary key (menu_id) +); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '路由地址'; +comment on column sys_menu.component is '组件路径'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_dept is '创建部门'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '1', '0', 'M', '0', '0', '', 'system', 103, 1, now(), null, null, '系统管理目录'); +insert into sys_menu values('6', '系统管理', '0', '2', 'tenant', null, '', '1', '0', 'M', '0', '0', '', 'chart', 103, 1, now(), null, null, '租户管理目录'); +insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), null, null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', '1', '0', 'M', '0', '0', '', 'tool', 103, 1, now(), null, null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide', 103, 1, now(), null, null, 'RuoYi-Vue-Plus官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '1', '0', 'C', '0', '0', 'system:user:list', 'user', 103, 1, now(), null, null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '1', '0', 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, now(), null, null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '1', '0', 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, now(), null, null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '1', '0', 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, now(), null, null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', '1', '0', 'C', '0', '0', 'system:post:list', 'post', 103, 1, now(), null, null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', '1', '0', 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, now(), null, null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', '1', '0', 'C', '0', '0', 'system:config:list', 'edit', 103, 1, now(), null, null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '1', '0', 'C', '0', '0', 'system:notice:list', 'message', 103, 1, now(), null, null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', '1', '0', 'M', '0', '0', '', 'log', 103, 1, now(), null, null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '1', '0', 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, now(), null, null, '在线用户菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, now(), null, null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', '1', '0', 'C', '0', '0', 'tool:build:list', 'build', 103, 1, now(), null, null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '1', '0', 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, now(), null, null, '代码生成菜单'); +insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', '1', '0', 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, now(), null, null, '租户管理菜单'); +insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', '1', '0', 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, now(), null, null, '租户套餐管理菜单'); + +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, now(), null, null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', '1', '0', 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, now(), null, null, '文件管理菜单'); +-- xxl-job-admin控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', '1', '0', 'C', '0', '0', 'monitor:xxljob:list', 'job', 103, 1, now(), null, null, 'Xxl-Job控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', '1', '0', 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, now(), null, null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '1', '0', 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, now(), null, null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:user:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:user:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:user:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:user:export', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', '1', '0', 'F', '0', '0', 'system:user:import', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', '1', '0', 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, now(), null, null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:role:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:role:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:role:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:role:export', '#', 103, 1, now(), null, null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:remove', '#', 103, 1, now(), null, null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:remove', '#', 103, 1, now(), null, null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:post:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:post:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:post:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:post:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:post:export', '#', 103, 1, now(), null, null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:export', '#', 103, 1, now(), null, null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:export', '#', 103, 1, now(), null, null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:remove', '#', 103, 1, now(), null, null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, now(), null, null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, now(), null, null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:import', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:code', '#', 103, 1, now(), null, null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:edit', '#', 103, 1, now(), null, null, ''); +-- 租户管理相关按钮 +insert into sys_menu values('1606', '租户查询', '121', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1607', '租户新增', '121', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1608', '租户修改', '121', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1609', '租户删除', '121', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1610', '租户导出', '121', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:export', '#', 103, 1, now(), null, null, ''); +-- 租户套餐管理相关按钮 +insert into sys_menu values('1611', '租户套餐查询', '122', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1612', '租户套餐新增', '122', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1613', '租户套餐修改', '122', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1614', '租户套餐删除', '122', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1615', '租户套餐导出', '122', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, now(), null, null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table if not exists sys_user_role +( + user_id int8 not null, + role_id int8 not null, + constraint sys_user_role_pk primary key (user_id, role_id) +); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table if not exists sys_role_menu +( + role_id int8 not null, + menu_id int8 not null, + constraint sys_role_menu_pk primary key (role_id, menu_id) +); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table if not exists sys_role_dept +( + role_id int8 not null, + dept_id int8 not null, + constraint sys_role_dept_pk primary key (role_id, dept_id) +); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table if not exists sys_user_post +( + user_id int8 not null, + post_id int8 not null, + constraint sys_user_post_pk primary key (user_id, post_id) +); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table if not exists sys_oper_log +( + oper_id int8, + tenant_id varchar(20) default '000000'::varchar, + title varchar(50) default ''::varchar, + business_type int4 default 0, + method varchar(100) default ''::varchar, + request_method varchar(10) default ''::varchar, + operator_type int4 default 0, + oper_name varchar(50) default ''::varchar, + dept_name varchar(50) default ''::varchar, + oper_url varchar(255) default ''::varchar, + oper_ip varchar(128) default ''::varchar, + oper_location varchar(255) default ''::varchar, + oper_param varchar(2000) default ''::varchar, + json_result varchar(2000) default ''::varchar, + status int4 default 0, + error_msg varchar(2000) default ''::varchar, + oper_time timestamp, + cost_time int8 default 0, + constraint sys_oper_log_pk primary key (oper_id) +); + +create index idx_sys_oper_log_bt ON sys_oper_log (business_type); +create index idx_sys_oper_log_s ON sys_oper_log (status); +create index idx_sys_oper_log_ot ON sys_oper_log (oper_time); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.tenant_id is '租户编号'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; +comment on column sys_oper_log.cost_time is '消耗时间'; + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table if not exists sys_dict_type +( + dict_id int8, + tenant_id varchar(20) default '000000'::varchar, + dict_name varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + status char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_type_pk primary key (dict_id) +); + +create unique index sys_dict_type_index1 ON sys_dict_type (tenant_id, dict_type); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.status is '状态(0正常 1停用)'; +comment on column sys_dict_type.create_dept is '创建部门'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '000000', '用户性别', 'sys_user_sex', '0', 103, 1, now(), null, null, '用户性别列表'); +insert into sys_dict_type values(2, '000000', '菜单状态', 'sys_show_hide', '0', 103, 1, now(), null, null, '菜单状态列表'); +insert into sys_dict_type values(3, '000000', '系统开关', 'sys_normal_disable', '0', 103, 1, now(), null, null, '系统开关列表'); +insert into sys_dict_type values(6, '000000', '系统是否', 'sys_yes_no', '0', 103, 1, now(), null, null, '系统是否列表'); +insert into sys_dict_type values(7, '000000', '通知类型', 'sys_notice_type', '0', 103, 1, now(), null, null, '通知类型列表'); +insert into sys_dict_type values(8, '000000', '通知状态', 'sys_notice_status', '0', 103, 1, now(), null, null, '通知状态列表'); +insert into sys_dict_type values(9, '000000', '操作类型', 'sys_oper_type', '0', 103, 1, now(), null, null, '操作类型列表'); +insert into sys_dict_type values(10, '000000', '系统状态', 'sys_common_status', '0', 103, 1, now(), null, null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table if not exists sys_dict_data +( + dict_code int8, + tenant_id varchar(20) default '000000'::varchar, + dict_sort int4 default 0, + dict_label varchar(100) default ''::varchar, + dict_value varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + css_class varchar(100) default null::varchar, + list_class varchar(100) default null::varchar, + is_default char default 'N'::bpchar, + status char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_data_pk primary key (dict_code) +); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典编码'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.status is '状态(0正常 1停用)'; +comment on column sys_dict_data.create_dept is '创建部门'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 103, 1, now(), null, null, '性别男'); +insert into sys_dict_data values(2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 103, 1, now(), null, null, '性别女'); +insert into sys_dict_data values(3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 103, 1, now(), null, null, '性别未知'); +insert into sys_dict_data values(4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 103, 1, now(), null, null, '显示菜单'); +insert into sys_dict_data values(5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 103, 1, now(), null, null, '隐藏菜单'); +insert into sys_dict_data values(6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 103, 1, now(), null, null, '停用状态'); +insert into sys_dict_data values(12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 103, 1, now(), null, null, '系统默认是'); +insert into sys_dict_data values(13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 103, 1, now(), null, null, '系统默认否'); +insert into sys_dict_data values(14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 103, 1, now(), null, null, '通知'); +insert into sys_dict_data values(15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 103, 1, now(), null, null, '公告'); +insert into sys_dict_data values(16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 103, 1, now(), null, null, '关闭状态'); +insert into sys_dict_data values(29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, now(), null, null, '其他操作'); +insert into sys_dict_data values(18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, now(), null, null, '新增操作'); +insert into sys_dict_data values(19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, now(), null, null, '修改操作'); +insert into sys_dict_data values(20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, now(), null, null, '删除操作'); +insert into sys_dict_data values(21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 103, 1, now(), null, null, '授权操作'); +insert into sys_dict_data values(22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, now(), null, null, '导出操作'); +insert into sys_dict_data values(23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, now(), null, null, '导入操作'); +insert into sys_dict_data values(24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, now(), null, null, '强退操作'); +insert into sys_dict_data values(25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, now(), null, null, '生成操作'); +insert into sys_dict_data values(26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, now(), null, null, '清空操作'); +insert into sys_dict_data values(27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 103, 1, now(), null, null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table if not exists sys_config +( + config_id int8, + tenant_id varchar(20) default '000000'::varchar, + config_name varchar(100) default ''::varchar, + config_key varchar(100) default ''::varchar, + config_value varchar(500) default ''::varchar, + config_type char default 'N'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_config_pk primary key (config_id) +); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.tenant_id is '租户编号'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_dept is '创建部门'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, now(), null, null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, now(), null, null, '初始化密码 123456' ); +insert into sys_config values(3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, now(), null, null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, now(), null, null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, now(), null, null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table if not exists sys_logininfor +( + info_id int8, + tenant_id varchar(20) default '000000'::varchar, + user_name varchar(50) default ''::varchar, + ipaddr varchar(128) default ''::varchar, + login_location varchar(255) default ''::varchar, + browser varchar(50) default ''::varchar, + os varchar(50) default ''::varchar, + status char default '0'::bpchar, + msg varchar(255) default ''::varchar, + login_time timestamp, + constraint sys_logininfor_pk primary key (info_id) +); + +create index idx_sys_logininfor_s ON sys_logininfor (status); +create index idx_sys_logininfor_lt ON sys_logininfor (login_time); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.tenant_id is '租户编号'; +comment on column sys_logininfor.user_name is '用户账号'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table if not exists sys_notice +( + notice_id int8, + tenant_id varchar(20) default '000000'::varchar, + notice_title varchar(50) not null, + notice_type char not null, + notice_content text, + status char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(255) default null::varchar, + constraint sys_notice_pk primary key (notice_id) +); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告ID'; +comment on column sys_notice.tenant_id is '租户编号'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_dept is '创建部门'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, now(), null, null, '管理员'); +insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, now(), null, null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table if not exists gen_table +( + table_id int8, + table_name varchar(200) default ''::varchar, + table_comment varchar(500) default ''::varchar, + sub_table_name varchar(64) default ''::varchar, + sub_table_fk_name varchar(64) default ''::varchar, + class_name varchar(100) default ''::varchar, + tpl_category varchar(200) default 'crud'::varchar, + package_name varchar(100) default null::varchar, + module_name varchar(30) default null::varchar, + business_name varchar(30) default null::varchar, + function_name varchar(50) default null::varchar, + function_author varchar(50) default null::varchar, + gen_type char default '0'::bpchar not null, + gen_path varchar(200) default '/'::varchar, + options varchar(1000) default null::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint gen_table_pk primary key (table_id) +); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(CRUD单表操作 TREE树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_dept is '创建部门'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table if not exists gen_table_column +( + column_id int8, + table_id int8, + column_name varchar(200) default null::varchar, + column_comment varchar(500) default null::varchar, + column_type varchar(100) default null::varchar, + java_type varchar(500) default null::varchar, + java_field varchar(200) default null::varchar, + is_pk char default null::bpchar, + is_increment char default null::bpchar, + is_required char default null::bpchar, + is_insert char default null::bpchar, + is_edit char default null::bpchar, + is_list char default null::bpchar, + is_query char default null::bpchar, + query_type varchar(200) default 'EQ'::varchar, + html_type varchar(200) default null::varchar, + dict_type varchar(200) default ''::varchar, + sort int4, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint gen_table_column_pk primary key (column_id) +); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_dept is '创建部门'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +drop table if exists sys_oss; +create table if not exists sys_oss +( + oss_id int8, + tenant_id varchar(20) default '000000'::varchar, + file_name varchar(255) default ''::varchar not null, + original_name varchar(255) default ''::varchar not null, + file_suffix varchar(10) default ''::varchar not null, + url varchar(500) default ''::varchar not null, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + service varchar(20) default 'minio'::varchar, + constraint sys_oss_pk primary key (oss_id) +); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.tenant_id is '租户编码'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.create_by is '上传人'; +comment on column sys_oss.create_dept is '创建部门'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.update_by is '更新者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.service is '服务商'; + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +drop table if exists sys_oss_config; +create table if not exists sys_oss_config +( + oss_config_id int8, + tenant_id varchar(20) default '000000'::varchar, + config_key varchar(20) default ''::varchar not null, + access_key varchar(255) default ''::varchar, + secret_key varchar(255) default ''::varchar, + bucket_name varchar(255) default ''::varchar, + prefix varchar(255) default ''::varchar, + endpoint varchar(255) default ''::varchar, + domain varchar(255) default ''::varchar, + is_https char default 'N'::bpchar, + region varchar(255) default ''::varchar, + access_policy char(1) default '1'::bpchar not null, + status char default '1'::bpchar, + ext1 varchar(255) default ''::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint sys_oss_config_pk primary key (oss_config_id) +); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主建'; +comment on column sys_oss_config.tenant_id is '租户编码'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accessKey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.create_dept is '创建部门'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; +comment on column sys_oss_config.remark is '备注'; + +insert into sys_oss_config values (1, '000000', 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1', '0', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), NULL); + +-- 字符串自动转时间 避免框架时间查询报错问题 +create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ +select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); +$$ language sql strict ; + +create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; diff --git a/script/sql/postgres/postgres_test.sql b/script/sql/postgres/postgres_test.sql new file mode 100644 index 00000000..179096b3 --- /dev/null +++ b/script/sql/postgres/postgres_test.sql @@ -0,0 +1,204 @@ +DROP TABLE if EXISTS test_demo; +create table if not exists test_demo +( + id int8, + tenant_id varchar(20) default '000000', + dept_id int8, + user_id int8, + order_num int4 default 0, + test_key varchar(255), + value varchar(255), + version int4 default 0, + create_time timestamp, + create_dept int8, + create_by int8, + update_time timestamp, + update_by int8, + del_flag int4 default 0 +); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.tenant_id is '租户编号'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_dept is '创建部门'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +DROP TABLE if EXISTS test_tree; +create table if not exists test_tree +( + id int8, + tenant_id varchar(20) default '000000', + parent_id int8 default 0, + dept_id int8, + user_id int8, + tree_name varchar(255), + version int4 default 0, + create_time timestamp, + create_dept int8, + create_by int8, + update_time timestamp, + update_by int8, + del_flag integer default 0 +); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.tenant_id is '租户编号'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_dept is '创建部门'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 3, now(), NULL); +INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 4, now(), NULL); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, now(), NULL, NULL, '测试单表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, now(), NULL, NULL, '测试树表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', '本部门及以下', 'test1', 3, '4', 't', 't', '0', '0', 103, 1, now(), 1, NULL, NULL); +INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', '仅本人', 'test2', 4, '5', 't', 't', '0', '0', 103, 1, now(), 1, NULL, NULL); + +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); + +INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); +INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); + +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, now(), 1, NULL, NULL, 0); + +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 1, 102, 3, '子节点1', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 2, 102, 3, '子节点2', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 0, 108, 4, '测试树1', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 4, 108, 3, '子节点11', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 4, 108, 3, '子节点22', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 4, 108, 3, '子节点33', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 5, 108, 3, '子节点44', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 6, 108, 3, '子节点55', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 7, 108, 3, '子节点66', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 7, 108, 3, '子节点77', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 10, 108, 3, '子节点88', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 10, 108, 3, '子节点99', 0, 103, now(), 1, NULL, NULL, 0); diff --git a/script/sql/ry-vue.sql b/script/sql/ry-vue.sql new file mode 100644 index 00000000..b54356e2 --- /dev/null +++ b/script/sql/ry-vue.sql @@ -0,0 +1,1983 @@ +/* + Navicat MySQL Data Transfer + + Source Server : 本地 + Source Server Type : MySQL + Source Server Version : 80034 + Source Host : localhost:3306 + Source Schema : ry-vue + + Target Server Type : MySQL + Target Server Version : 80034 + File Encoding : 65001 + + Date: 16/01/2024 12:05:28 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for chat_message +-- ---------------------------- +DROP TABLE IF EXISTS `chat_message`; +CREATE TABLE `chat_message` ( + `id` bigint NOT NULL COMMENT '主键', + `user_id` bigint NOT NULL COMMENT '用户id', + `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容', + `deduct_cost` double(20, 10) NULL DEFAULT 0.0000000000 COMMENT '扣除金额\r\n\r\n', + `total_tokens` int NULL DEFAULT NULL COMMENT '累计 Tokens', + `model_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模型名称', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of chat_message +-- ---------------------------- + +-- ---------------------------- +-- Table structure for chat_token +-- ---------------------------- +DROP TABLE IF EXISTS `chat_token`; +CREATE TABLE `chat_token` ( + `id` bigint NOT NULL COMMENT '主键', + `user_id` bigint NOT NULL COMMENT '用户', + `token` int NULL DEFAULT NULL COMMENT '待结算token', + `model_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模型名称', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'token信息' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of chat_token +-- ---------------------------- + +-- ---------------------------- +-- Table structure for gen_table +-- ---------------------------- +DROP TABLE IF EXISTS `gen_table`; +CREATE TABLE `gen_table` ( + `table_id` bigint NOT NULL COMMENT '编号', + `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '表名称', + `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '表描述', + `sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '关联子表的表名', + `sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '子表关联的外键名', + `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '实体类名称', + `tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'crud' COMMENT '使用的模板(crud单表操作 tree树表操作)', + `package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成包路径', + `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成模块名', + `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成业务名', + `function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成功能名', + `function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成功能作者', + `gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '生成代码方式(0zip压缩包 1自定义路径)', + `gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '/' COMMENT '生成路径(不填默认项目路径)', + `options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '其它生成选项', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`table_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成业务表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of gen_table +-- ---------------------------- +INSERT INTO `gen_table` VALUES (1661288222902505474, 'sys_notice', '通知公告表', NULL, NULL, 'SysNotice', 'crud', 'org.dromara.system', 'system', 'notice', '通知公告', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223338713089, 'sys_oper_log', '操作日志记录', NULL, NULL, 'SysOperLog', 'crud', 'org.dromara.system', 'system', 'operLog', '操作日志记录', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223477125122, 'sys_oss', 'OSS对象存储表', NULL, NULL, 'SysOss', 'crud', 'org.dromara.system', 'system', 'oss', 'OSS对象存储', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223586177025, 'sys_oss_config', '对象存储配置表', NULL, NULL, 'SysOssConfig', 'crud', 'org.dromara.system', 'system', 'ossConfig', '对象存储配置', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223728783361, 'sys_post', '岗位信息表', NULL, NULL, 'SysPost', 'crud', 'org.dromara.system', 'system', 'post', '岗位信息', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223821058050, 'sys_role', '角色信息表', NULL, NULL, 'SysRole', 'crud', 'org.dromara.system', 'system', 'role', '角色信息', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223925915650, 'sys_user_post', '用户与岗位关联表', NULL, NULL, 'SysUserPost', 'crud', 'org.dromara.system', 'system', 'userPost', '用户与岗位关联', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288223967858689, 'sys_user_role', '用户和角色关联表', NULL, NULL, 'SysUserRole', 'crud', 'org.dromara.system', 'system', 'userRole', '用户和角色关联', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288224013996034, 'test_demo', '测试单表', NULL, NULL, 'TestDemo', 'crud', 'org.dromara.system', 'system', 'demo', '测试单', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288224185962497, 'test_tree', '测试树表', NULL, NULL, 'TestTree', 'crud', 'org.dromara.system', 'system', 'tree', '测试树', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:11', 1, '2023-05-20 18:05:11', NULL); +INSERT INTO `gen_table` VALUES (1661288385096241154, 'sys_config', '参数配置表', NULL, NULL, 'SysConfig', 'crud', 'org.dromara.system', 'system', 'config', '参数配置', 'Lion Li', '0', '/', NULL, 103, 1, '2023-05-20 18:05:10', 1, '2023-05-20 18:05:10', NULL); +INSERT INTO `gen_table` VALUES (1680196323445579778, 'sys_file_detail', '文件记录表', NULL, NULL, 'SysFileDetail', 'crud', 'com.xmzs.system', 'system', 'fileDetail', '文件记录', 'Lion Li', '0', '/', NULL, 103, 1, '2023-07-15 20:40:00', 1, '2023-07-15 20:40:00', NULL); +INSERT INTO `gen_table` VALUES (1680196323521077249, 'sys_file_detail', '文件记录表', NULL, NULL, 'SysFileDetail', 'crud', 'com.xmzs.system', 'system', 'fileDetail', '文件记录', 'Lion Li', '0', '/', NULL, 103, 1, '2023-07-15 20:40:00', 1, '2023-07-15 20:40:00', NULL); +INSERT INTO `gen_table` VALUES (1680199147407806465, 'sys_file_info', '文件记录表', NULL, NULL, 'SysFileInfo', 'crud', 'com.xmzs.system', 'system', 'fileInfo', '文件记录', 'Lion Li', '0', '/', NULL, 103, 1, '2023-07-15 20:53:56', 1, '2023-07-15 20:53:56', NULL); +INSERT INTO `gen_table` VALUES (1680481752850145282, 'sd_model_param', '模型参数信息表', NULL, NULL, 'SdModelParam', 'crud', 'com.xmzs.system', 'system', 'modelParam', '模型参数信息', 'Lion Li', '0', '/', NULL, 103, 1, '2023-07-16 15:18:34', 1, '2023-07-16 15:18:34', NULL); +INSERT INTO `gen_table` VALUES (1728684654923988993, 'chat_message', '聊天消息表', NULL, NULL, 'ChatMessage', 'crud', 'com.xmzs.system', 'system', 'message', '聊天消息', 'Lion Li', '0', '/', NULL, 103, 1, '2023-11-26 15:28:10', 1, '2023-11-26 15:28:10', NULL); +INSERT INTO `gen_table` VALUES (1740573614897897473, 'payment_orders', '支付订单表', NULL, NULL, 'PaymentOrders', 'crud', 'com.xmzs.system', 'system', 'orders', '支付订单', 'Lion Li', '0', '/', NULL, 103, 1, '2023-12-27 23:04:45', 1, '2023-12-27 23:04:45', NULL); + +-- ---------------------------- +-- Table structure for gen_table_column +-- ---------------------------- +DROP TABLE IF EXISTS `gen_table_column`; +CREATE TABLE `gen_table_column` ( + `column_id` bigint NOT NULL COMMENT '编号', + `table_id` bigint NULL DEFAULT NULL COMMENT '归属表编号', + `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列名称', + `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列描述', + `column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列类型', + `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'JAVA类型', + `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'JAVA字段名', + `is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否主键(1是)', + `is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否自增(1是)', + `is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否必填(1是)', + `is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否为插入字段(1是)', + `is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否编辑字段(1是)', + `is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否列表字段(1是)', + `is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否查询字段(1是)', + `query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)', + `html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型', + `sort` int NULL DEFAULT NULL COMMENT '排序', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`column_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成业务表字段' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of gen_table_column +-- ---------------------------- +INSERT INTO `gen_table_column` VALUES (1661288223078666241, 1661288222902505474, 'notice_id', '公告ID', 'bigint(20)', 'Long', 'noticeId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223108026369, 1661288222902505474, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223108026370, 1661288222902505474, 'notice_title', '公告标题', 'varchar(50)', 'String', 'noticeTitle', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223108026371, 1661288222902505474, 'notice_type', '公告类型(1通知 2公告)', 'char(1)', 'String', 'noticeType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223108026372, 1661288222902505474, 'notice_content', '公告内容', 'longblob', 'String', 'noticeContent', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'editor', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609282, 1661288222902505474, 'status', '公告状态(0正常 1关闭)', 'char(1)', 'String', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609283, 1661288222902505474, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609284, 1661288222902505474, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609285, 1661288222902505474, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609286, 1661288222902505474, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609287, 1661288222902505474, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223120609288, 1661288222902505474, 'remark', '备注', 'varchar(255)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223363878913, 1661288223338713089, 'oper_id', '日志主键', 'bigint(20)', 'Long', 'operId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223363878914, 1661288223338713089, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223363878915, 1661288223338713089, 'title', '模块标题', 'varchar(50)', 'String', 'title', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223363878916, 1661288223338713089, 'business_type', '业务类型(0其它 1新增 2修改 3删除)', 'int(2)', 'Integer', 'businessType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627649, 1661288223338713089, 'method', '方法名称', 'varchar(100)', 'String', 'method', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627650, 1661288223338713089, 'request_method', '请求方式', 'varchar(10)', 'String', 'requestMethod', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627651, 1661288223338713089, 'operator_type', '操作类别(0其它 1后台用户 2手机端用户)', 'int(1)', 'Integer', 'operatorType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627652, 1661288223338713089, 'oper_name', '操作人员', 'varchar(50)', 'String', 'operName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627653, 1661288223338713089, 'dept_name', '部门名称', 'varchar(50)', 'String', 'deptName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627654, 1661288223338713089, 'oper_url', '请求URL', 'varchar(255)', 'String', 'operUrl', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627655, 1661288223338713089, 'oper_ip', '主机地址', 'varchar(128)', 'String', 'operIp', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627656, 1661288223338713089, 'oper_location', '操作地点', 'varchar(255)', 'String', 'operLocation', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627657, 1661288223338713089, 'oper_param', '请求参数', 'varchar(2000)', 'String', 'operParam', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 13, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627658, 1661288223338713089, 'json_result', '返回参数', 'varchar(2000)', 'String', 'jsonResult', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 14, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627659, 1661288223338713089, 'status', '操作状态(0正常 1异常)', 'int(1)', 'Integer', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 15, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627660, 1661288223338713089, 'error_msg', '错误消息', 'varchar(2000)', 'String', 'errorMsg', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 16, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627661, 1661288223338713089, 'oper_time', '操作时间', 'datetime', 'Date', 'operTime', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'datetime', '', 17, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223401627662, 1661288223338713089, 'cost_time', '消耗时间', 'bigint(20)', 'Long', 'costTime', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 18, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290946, 1661288223477125122, 'oss_id', '对象存储主键', 'bigint(20)', 'Long', 'ossId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290947, 1661288223477125122, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290948, 1661288223477125122, 'file_name', '文件名', 'varchar(255)', 'String', 'fileName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290949, 1661288223477125122, 'original_name', '原名', 'varchar(255)', 'String', 'originalName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290950, 1661288223477125122, 'file_suffix', '文件后缀名', 'varchar(10)', 'String', 'fileSuffix', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290951, 1661288223477125122, 'url', 'URL地址', 'varchar(500)', 'String', 'url', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290952, 1661288223477125122, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290953, 1661288223477125122, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290954, 1661288223477125122, 'create_by', '上传人', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290955, 1661288223477125122, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290956, 1661288223477125122, 'update_by', '更新人', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223502290957, 1661288223477125122, 'service', '服务商', 'varchar(20)', 'String', 'service', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342850, 1661288223586177025, 'oss_config_id', '主建', 'bigint(20)', 'Long', 'ossConfigId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342851, 1661288223586177025, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342852, 1661288223586177025, 'config_key', '配置key', 'varchar(20)', 'String', 'configKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342853, 1661288223586177025, 'access_key', 'accessKey', 'varchar(255)', 'String', 'accessKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342854, 1661288223586177025, 'secret_key', '秘钥', 'varchar(255)', 'String', 'secretKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342855, 1661288223586177025, 'bucket_name', '桶名称', 'varchar(255)', 'String', 'bucketName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342856, 1661288223586177025, 'prefix', '前缀', 'varchar(255)', 'String', 'prefix', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342857, 1661288223586177025, 'endpoint', '访问站点', 'varchar(255)', 'String', 'endpoint', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342858, 1661288223586177025, 'domain', '自定义域名', 'varchar(255)', 'String', 'domain', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342859, 1661288223586177025, 'is_https', '是否https(Y=是,N=否)', 'char(1)', 'String', 'isHttps', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223611342860, 1661288223586177025, 'region', '域', 'varchar(255)', 'String', 'region', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285889, 1661288223586177025, 'access_policy', '桶权限类型(0=private 1=public 2=custom)', 'char(1)', 'String', 'accessPolicy', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285890, 1661288223586177025, 'status', '是否默认(0=是,1=否)', 'char(1)', 'String', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 13, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285891, 1661288223586177025, 'ext1', '扩展字段', 'varchar(255)', 'String', 'ext1', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 14, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285892, 1661288223586177025, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 15, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285893, 1661288223586177025, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 16, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285894, 1661288223586177025, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 17, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285895, 1661288223586177025, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 18, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285896, 1661288223586177025, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 19, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223653285897, 1661288223586177025, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 20, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754881, 1661288223728783361, 'post_id', '岗位ID', 'bigint(20)', 'Long', 'postId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754882, 1661288223728783361, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754883, 1661288223728783361, 'post_code', '岗位编码', 'varchar(64)', 'String', 'postCode', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754884, 1661288223728783361, 'post_name', '岗位名称', 'varchar(50)', 'String', 'postName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754885, 1661288223728783361, 'post_sort', '显示顺序', 'int(4)', 'Integer', 'postSort', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754886, 1661288223728783361, 'status', '状态(0正常 1停用)', 'char(1)', 'String', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754887, 1661288223728783361, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754888, 1661288223728783361, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754889, 1661288223728783361, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754890, 1661288223728783361, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754891, 1661288223728783361, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223749754892, 1661288223728783361, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223874, 1661288223821058050, 'role_id', '角色ID', 'bigint(20)', 'Long', 'roleId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223875, 1661288223821058050, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223876, 1661288223821058050, 'role_name', '角色名称', 'varchar(30)', 'String', 'roleName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 3, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223877, 1661288223821058050, 'role_key', '角色权限字符串', 'varchar(100)', 'String', 'roleKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223878, 1661288223821058050, 'role_sort', '显示顺序', 'int(4)', 'Integer', 'roleSort', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223879, 1661288223821058050, 'data_scope', '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', 'char(1)', 'String', 'dataScope', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223880, 1661288223821058050, 'menu_check_strictly', '菜单树选择项是否关联显示', 'tinyint(1)', 'Integer', 'menuCheckStrictly', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223881, 1661288223821058050, 'dept_check_strictly', '部门树选择项是否关联显示', 'tinyint(1)', 'Integer', 'deptCheckStrictly', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223882, 1661288223821058050, 'status', '角色状态(0正常 1停用)', 'char(1)', 'String', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 9, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223883, 1661288223821058050, 'del_flag', '删除标志(0代表存在 2代表删除)', 'char(1)', 'String', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223884, 1661288223821058050, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 11, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223885, 1661288223821058050, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223886, 1661288223821058050, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 13, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223887, 1661288223821058050, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 14, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223888, 1661288223821058050, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 15, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223846223889, 1661288223821058050, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 16, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223951081474, 1661288223925915650, 'user_id', '用户ID', 'bigint(20)', 'Long', 'userId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223951081475, 1661288223925915650, 'post_id', '岗位ID', 'bigint(20)', 'Long', 'postId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:12', 1, '2023-05-24 16:29:12'); +INSERT INTO `gen_table_column` VALUES (1661288223993024514, 1661288223967858689, 'user_id', '用户ID', 'bigint(20)', 'Long', 'userId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288223993024515, 1661288223967858689, 'role_id', '角色ID', 'bigint(20)', 'Long', 'roleId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356162, 1661288224013996034, 'id', '主键', 'bigint(20)', 'Long', 'id', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356163, 1661288224013996034, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356164, 1661288224013996034, 'dept_id', '部门id', 'bigint(20)', 'Long', 'deptId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356165, 1661288224013996034, 'user_id', '用户id', 'bigint(20)', 'Long', 'userId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356166, 1661288224013996034, 'order_num', '排序号', 'int(11)', 'Long', 'orderNum', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356167, 1661288224013996034, 'test_key', 'key键', 'varchar(255)', 'String', 'testKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356168, 1661288224013996034, 'value', '值', 'varchar(255)', 'String', 'value', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356169, 1661288224013996034, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356170, 1661288224013996034, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 9, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356171, 1661288224013996034, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 10, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356172, 1661288224013996034, 'create_by', '创建人', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 11, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224043356173, 1661288224013996034, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 12, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224118853634, 1661288224013996034, 'update_by', '更新人', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224123047938, 1661288224013996034, 'del_flag', '删除标志', 'int(11)', 'Long', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 14, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224206934017, 1661288224185962497, 'id', '主键', 'bigint(20)', 'Long', 'id', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224206934018, 1661288224185962497, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224206934019, 1661288224185962497, 'parent_id', '父id', 'bigint(20)', 'Long', 'parentId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224206934020, 1661288224185962497, 'dept_id', '部门id', 'bigint(20)', 'Long', 'deptId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224206934021, 1661288224185962497, 'user_id', '用户id', 'bigint(20)', 'Long', 'userId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711233, 1661288224185962497, 'tree_name', '值', 'varchar(255)', 'String', 'treeName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 6, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711234, 1661288224185962497, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711235, 1661288224185962497, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711236, 1661288224185962497, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 9, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711237, 1661288224185962497, 'create_by', '创建人', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711238, 1661288224185962497, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711239, 1661288224185962497, 'update_by', '更新人', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 12, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288224223711240, 1661288224185962497, 'del_flag', '删除标志', 'int(11)', 'Long', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-05-24 16:29:13', 1, '2023-05-24 16:29:13'); +INSERT INTO `gen_table_column` VALUES (1661288385121406978, 1661288385096241154, 'config_id', '参数主键', 'bigint(20)', 'Long', 'configId', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406979, 1661288385096241154, 'tenant_id', '租户编号', 'varchar(20)', 'String', 'tenantId', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 2, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406980, 1661288385096241154, 'config_name', '参数名称', 'varchar(100)', 'String', 'configName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 3, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406981, 1661288385096241154, 'config_key', '参数键名', 'varchar(100)', 'String', 'configKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406982, 1661288385096241154, 'config_value', '参数键值', 'varchar(500)', 'String', 'configValue', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 5, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406983, 1661288385096241154, 'config_type', '系统内置(Y是 N否)', 'char(1)', 'String', 'configType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 6, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385121406984, 1661288385096241154, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 7, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385142378498, 1661288385096241154, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385142378499, 1661288385096241154, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 9, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385142378500, 1661288385096241154, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385142378501, 1661288385096241154, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1661288385142378502, 1661288385096241154, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 12, 103, 1, '2023-05-24 16:29:51', 1, '2023-05-24 16:29:51'); +INSERT INTO `gen_table_column` VALUES (1680196323806289921, 1680196323521077249, 'id', '文件id', 'bigint(20) unsigned', 'Long', 'id', '1', '1', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323806289922, 1680196323521077249, 'url', '文件访问地址', 'varchar(512)', 'String', 'url', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 2, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323835650050, 1680196323445579778, 'id', '文件id', 'bigint(20) unsigned', 'Long', 'id', '1', '1', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323835650051, 1680196323445579778, 'url', '文件访问地址', 'varchar(512)', 'String', 'url', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 2, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323835650052, 1680196323445579778, 'size', '文件大小,单位字节', 'bigint(20)', 'Long', 'size', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398785, 1680196323445579778, 'filename', '文件名称', 'varchar(256)', 'String', 'filename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398786, 1680196323445579778, 'original_filename', '原始文件名', 'varchar(256)', 'String', 'originalFilename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 5, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398787, 1680196323445579778, 'base_path', '基础存储路径', 'varchar(256)', 'String', 'basePath', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398788, 1680196323445579778, 'path', '存储路径', 'varchar(256)', 'String', 'path', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398789, 1680196323445579778, 'ext', '文件扩展名', 'varchar(32)', 'String', 'ext', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398790, 1680196323445579778, 'object_id', '文件所属对象id', 'varchar(32)', 'String', 'objectId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 9, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323873398791, 1680196323445579778, 'file_type', '文件类型', 'varchar(32)', 'String', 'fileType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 10, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323932119041, 1680196323445579778, 'attr', '附加属性', 'text', 'String', 'attr', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 11, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323932119042, 1680196323445579778, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 12, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507649, 1680196323445579778, 'create_by', '创建者', 'varchar(64)', 'String', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507650, 1680196323445579778, 'update_by', '更新者', 'varchar(64)', 'String', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 14, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507651, 1680196323445579778, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 15, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507652, 1680196323445579778, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 16, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507653, 1680196323445579778, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 17, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323940507654, 1680196323445579778, 'del_flag', '删除标志(0代表存在 1代表删除)', 'char(1)', 'String', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 18, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479170, 1680196323521077249, 'size', '文件大小,单位字节', 'bigint(20)', 'Long', 'size', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479171, 1680196323521077249, 'filename', '文件名称', 'varchar(256)', 'String', 'filename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479172, 1680196323521077249, 'original_filename', '原始文件名', 'varchar(256)', 'String', 'originalFilename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 5, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479173, 1680196323521077249, 'base_path', '基础存储路径', 'varchar(256)', 'String', 'basePath', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479174, 1680196323521077249, 'path', '存储路径', 'varchar(256)', 'String', 'path', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479175, 1680196323521077249, 'ext', '文件扩展名', 'varchar(32)', 'String', 'ext', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479176, 1680196323521077249, 'object_id', '文件所属对象id', 'varchar(32)', 'String', 'objectId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 9, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479177, 1680196323521077249, 'file_type', '文件类型', 'varchar(32)', 'String', 'fileType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 10, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323961479178, 1680196323521077249, 'attr', '附加属性', 'text', 'String', 'attr', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 11, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323999227905, 1680196323445579778, 'update_ip', '更新IP', 'varchar(128)', 'String', 'updateIp', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 19, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196323999227906, 1680196323445579778, 'tenant_id', '租户Id', 'bigint(20)', 'Long', 'tenantId', '0', '0', '1', NULL, NULL, NULL, NULL, 'EQ', 'input', '', 20, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199425, 1680196323521077249, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 12, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199426, 1680196323521077249, 'create_by', '创建者', 'varchar(64)', 'String', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199427, 1680196323521077249, 'update_by', '更新者', 'varchar(64)', 'String', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 14, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199428, 1680196323521077249, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 15, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199429, 1680196323521077249, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 16, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199430, 1680196323521077249, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 17, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199431, 1680196323521077249, 'del_flag', '删除标志(0代表存在 1代表删除)', 'char(1)', 'String', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 18, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199432, 1680196323521077249, 'update_ip', '更新IP', 'varchar(128)', 'String', 'updateIp', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 19, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680196324020199433, 1680196323521077249, 'tenant_id', '租户Id', 'bigint(20)', 'Long', 'tenantId', '0', '0', '1', NULL, NULL, NULL, NULL, 'EQ', 'input', '', 20, 103, 1, '2023-07-15 20:43:15', 1, '2023-07-15 20:43:15'); +INSERT INTO `gen_table_column` VALUES (1680199147667853313, 1680199147407806465, 'id', '文件id', 'bigint(20) unsigned', 'Long', 'id', '1', '1', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147667853314, 1680199147407806465, 'url', '文件访问地址', 'varchar(512)', 'String', 'url', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 2, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147667853315, 1680199147407806465, 'size', '文件大小,单位字节', 'bigint(20)', 'Long', 'size', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147667853316, 1680199147407806465, 'filename', '文件名称', 'varchar(256)', 'String', 'filename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147667853317, 1680199147407806465, 'original_filename', '原始文件名', 'varchar(256)', 'String', 'originalFilename', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 5, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147667853318, 1680199147407806465, 'base_path', '基础存储路径', 'varchar(256)', 'String', 'basePath', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962178, 1680199147407806465, 'path', '存储路径', 'varchar(256)', 'String', 'path', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962179, 1680199147407806465, 'ext', '文件扩展名', 'varchar(32)', 'String', 'ext', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962180, 1680199147407806465, 'object_id', '文件所属对象id', 'varchar(32)', 'String', 'objectId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 9, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962181, 1680199147407806465, 'file_type', '文件类型', 'varchar(32)', 'String', 'fileType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 10, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962182, 1680199147407806465, 'attr', '附加属性', 'text', 'String', 'attr', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 11, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962183, 1680199147407806465, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 12, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962184, 1680199147407806465, 'create_by', '创建者', 'varchar(64)', 'String', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962185, 1680199147407806465, 'update_by', '更新者', 'varchar(64)', 'String', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 14, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962186, 1680199147407806465, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 15, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962187, 1680199147407806465, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 16, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962188, 1680199147407806465, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 17, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962189, 1680199147407806465, 'del_flag', '删除标志(0代表存在 1代表删除)', 'char(1)', 'String', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 18, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962190, 1680199147407806465, 'update_ip', '更新IP', 'varchar(128)', 'String', 'updateIp', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 19, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680199147734962191, 1680199147407806465, 'tenant_id', '租户Id', 'bigint(20)', 'Long', 'tenantId', '0', '0', '1', NULL, NULL, NULL, NULL, 'EQ', 'input', '', 20, 103, 1, '2023-07-15 20:54:28', 1, '2023-07-15 20:54:28'); +INSERT INTO `gen_table_column` VALUES (1680481753240215553, 1680481752850145282, 'id', 'id', 'bigint(20) unsigned', 'Long', 'id', '1', '1', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215554, 1680481752850145282, 'prompt', '描述词', 'text', 'String', 'prompt', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 2, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215555, 1680481752850145282, 'negative_prompt', '负面词', 'text', 'String', 'negativePrompt', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 3, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215556, 1680481752850145282, 'model_name', '模型名称', 'varchar(256)', 'String', 'modelName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 4, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215557, 1680481752850145282, 'steps', '迭代步数', 'int(10)', 'Integer', 'steps', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215558, 1680481752850145282, 'seed', '种子', 'varchar(256)', 'String', 'seed', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215559, 1680481752850145282, 'width', '图片宽度', 'varchar(256)', 'String', 'width', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215560, 1680481752850145282, 'height', '图片高度', 'varchar(32)', 'String', 'height', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 8, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215561, 1680481752850145282, 'sampler_name', '采样方法', 'varchar(32)', 'String', 'samplerName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 9, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215562, 1680481752850145282, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215563, 1680481752850145282, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215564, 1680481752850145282, 'create_by', '创建者', 'varchar(64)', 'String', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 12, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215565, 1680481752850145282, 'update_by', '更新者', 'varchar(64)', 'String', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 13, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215566, 1680481752850145282, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 14, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215567, 1680481752850145282, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 15, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215568, 1680481752850145282, 'version', '版本', 'int(11)', 'Long', 'version', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 16, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215569, 1680481752850145282, 'del_flag', '删除标志(0代表存在 1代表删除)', 'char(1)', 'String', 'delFlag', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 17, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215570, 1680481752850145282, 'update_ip', '更新IP', 'varchar(128)', 'String', 'updateIp', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 18, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1680481753240215571, 1680481752850145282, 'tenant_id', '租户Id', 'bigint(20)', 'Long', 'tenantId', '0', '0', '1', NULL, NULL, NULL, NULL, 'EQ', 'input', '', 19, 103, 1, '2023-07-16 15:37:26', 1, '2023-07-16 15:37:26'); +INSERT INTO `gen_table_column` VALUES (1728684655246950402, 1728684654923988993, 'id', '主键', 'bigint(20)', 'Long', 'id', '1', '0', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950403, 1728684654923988993, 'message_id', '消息 id', 'varchar(64)', 'String', 'messageId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 2, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950404, 1728684654923988993, 'parent_message_id', '父级消息 id', 'varchar(64)', 'String', 'parentMessageId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 3, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950405, 1728684654923988993, 'parent_answer_message_id', '父级回答消息 id', 'varchar(64)', 'String', 'parentAnswerMessageId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950406, 1728684654923988993, 'parent_question_message_id', '父级问题消息 id', 'varchar(64)', 'String', 'parentQuestionMessageId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 5, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950407, 1728684654923988993, 'context_count', '上下文数量', 'bigint(20)', 'Long', 'contextCount', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950408, 1728684654923988993, 'question_context_count', '问题上下文数量', 'bigint(20)', 'Long', 'questionContextCount', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 7, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950409, 1728684654923988993, 'message_type', '消息类型枚举', 'int(11)', 'Long', 'messageType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 8, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950410, 1728684654923988993, 'chat_room_id', '聊天室 id', 'bigint(20)', 'Long', 'chatRoomId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 9, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950411, 1728684654923988993, 'conversation_id', '对话 id', 'varchar(255)', 'String', 'conversationId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 10, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950412, 1728684654923988993, 'api_type', 'API 类型', 'varchar(20)', 'String', 'apiType', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'select', '', 11, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950413, 1728684654923988993, 'api_key', 'ApiKey', 'varchar(255)', 'String', 'apiKey', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 12, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950414, 1728684654923988993, 'content', '消息内容', 'varchar(5000)', 'String', 'content', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'editor', '', 13, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950415, 1728684654923988993, 'original_data', '消息的原始请求或响应数据', 'text', 'String', 'originalData', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 14, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950416, 1728684654923988993, 'response_error_data', '错误的响应数据', 'text', 'String', 'responseErrorData', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'textarea', '', 15, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950417, 1728684654923988993, 'prompt_tokens', '输入消息的 tokens', 'bigint(20)', 'Long', 'promptTokens', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 16, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950418, 1728684654923988993, 'completion_tokens', '输出消息的 tokens', 'bigint(20)', 'Long', 'completionTokens', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 17, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950419, 1728684654923988993, 'total_tokens', '累计 Tokens', 'bigint(20)', 'Long', 'totalTokens', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 18, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950420, 1728684654923988993, 'model_name', '模型名称', 'varchar(255)', 'String', 'modelName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 19, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950421, 1728684654923988993, 'status', '聊天记录状态', 'int(11)', 'Long', 'status', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 20, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950422, 1728684654923988993, 'is_hide', '是否隐藏 0 否 1 是', 'tinyint(4)', 'Integer', 'isHide', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 21, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950423, 1728684654923988993, 'create_dept', '创建部门', 'bigint(20)', 'Long', 'createDept', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 22, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950424, 1728684654923988993, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 23, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950425, 1728684654923988993, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 24, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950426, 1728684654923988993, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 25, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950427, 1728684654923988993, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 26, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1728684655246950428, 1728684654923988993, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 27, 103, 1, '2023-11-26 15:58:34', 1, '2023-11-26 15:58:34'); +INSERT INTO `gen_table_column` VALUES (1740573615225053185, 1740573614897897473, 'id', '主键', 'int(11)', 'Long', 'id', '1', '1', '1', NULL, '1', '1', NULL, 'EQ', 'input', '', 1, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053186, 1740573614897897473, 'order_no', '订单编号', 'varchar(20)', 'String', 'orderNo', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 2, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053187, 1740573614897897473, 'order_name', '订单名称', 'varchar(100)', 'String', 'orderName', '0', '0', '1', '1', '1', '1', '1', 'LIKE', 'input', '', 3, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053188, 1740573614897897473, 'amount', '金额', 'decimal(10,2)', 'BigDecimal', 'amount', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 4, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053189, 1740573614897897473, 'payment_status', '支付状态', 'char(1)', 'String', 'paymentStatus', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'radio', '', 5, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053190, 1740573614897897473, 'payment_method', '支付方式', 'char(1)', 'String', 'paymentMethod', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'input', '', 6, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053191, 1740573614897897473, 'user_id', '用户ID', 'timestamp', 'Date', 'userId', '0', '0', '1', '1', '1', '1', '1', 'EQ', 'datetime', '', 7, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053192, 1740573614897897473, 'create_by', '创建者', 'bigint(20)', 'Long', 'createBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 8, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053193, 1740573614897897473, 'create_time', '创建时间', 'datetime', 'Date', 'createTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 9, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053194, 1740573614897897473, 'update_by', '更新者', 'bigint(20)', 'Long', 'updateBy', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'input', '', 10, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053195, 1740573614897897473, 'update_time', '更新时间', 'datetime', 'Date', 'updateTime', '0', '0', NULL, NULL, NULL, NULL, NULL, 'EQ', 'datetime', '', 11, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); +INSERT INTO `gen_table_column` VALUES (1740573615225053196, 1740573614897897473, 'remark', '备注', 'varchar(500)', 'String', 'remark', '0', '0', '1', '1', '1', '1', NULL, 'EQ', 'textarea', '', 12, 103, 1, '2023-12-29 11:21:03', 1, '2023-12-29 11:21:03'); + +-- ---------------------------- +-- Table structure for mj_room +-- ---------------------------- +DROP TABLE IF EXISTS `mj_room`; +CREATE TABLE `mj_room` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户ID', + `mj_guild_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'MJ服务器ID', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建日期', + `expiration_date` datetime NULL DEFAULT NULL COMMENT '截止有效期', + `enabled_local_sensitive_word` int NULL DEFAULT 0 COMMENT '是否启用本地敏感词过滤,0:不启用;1:启用', + `enabled_baidu_aip` int NULL DEFAULT 0 COMMENT '是否启用百度内容审核,0:不启用;1:启用', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'MJ创作会话' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of mj_room +-- ---------------------------- + +-- ---------------------------- +-- Table structure for mj_room_msg +-- ---------------------------- +DROP TABLE IF EXISTS `mj_room_msg`; +CREATE TABLE `mj_room_msg` ( + `id` bigint NOT NULL COMMENT '主键', + `mj_room_id` bigint NOT NULL COMMENT '会话 id', + `guild_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '服务器ID', + `user_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户 id', + `type` tinyint NOT NULL COMMENT '消息类型', + `prompt` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户输入', + `final_prompt` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '最终的输入', + `response_content` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '响应内容', + `action` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '指令动作', + `compressed_image_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '压缩图名称', + `original_image_name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '原图名称', + `uv_parent_id` bigint NULL DEFAULT NULL COMMENT 'uv 指令的父消息 id', + `u_use_bit` int NULL DEFAULT NULL COMMENT 'u 指令使用比特位', + `uv_index` tinyint(1) NULL DEFAULT NULL COMMENT 'uv 位置', + `status` tinyint NOT NULL COMMENT '状态', + `discord_finish_time` datetime NULL DEFAULT NULL COMMENT 'discord 结束时间', + `discord_start_time` datetime NULL DEFAULT NULL COMMENT 'discord 开始时间', + `discord_message_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'discord 消息 id', + `discord_channel_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'discord 频道 id', + `discord_image_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'discord 图片地址', + `template_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模板ID', + `failure_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '失败原因', + `is_deleted` tinyint(1) NOT NULL COMMENT '是否删除 0 否 1 是', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Midjourney 创作记录表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of mj_room_msg +-- ---------------------------- + +-- ---------------------------- +-- Table structure for mj_server +-- ---------------------------- +DROP TABLE IF EXISTS `mj_server`; +CREATE TABLE `mj_server` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `guild_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '服务器ID', + `channel_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '频道ID', + `bot_token` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '机器人token', + `bot_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `user_token` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户token', + `user_agent` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `max_execute_queue_size` int NULL DEFAULT NULL COMMENT '执行队列最大长度', + `max_file_size` int NULL DEFAULT NULL COMMENT '最大文件大小', + `max_wait_queue_size` int NULL DEFAULT NULL COMMENT '等待队列最大长度', + `status` int NULL DEFAULT NULL COMMENT '状态:0:禁用,1:可用', + `discord_api_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Discord Api Url', + `discord_upload_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'discordUploadUrl', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'MJ服务器信息配置表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of mj_server +-- ---------------------------- + +-- ---------------------------- +-- Table structure for payment_orders +-- ---------------------------- +DROP TABLE IF EXISTS `payment_orders`; +CREATE TABLE `payment_orders` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `order_no` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单编号', + `order_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单名称', + `amount` decimal(10, 2) NOT NULL COMMENT '金额', + `payment_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付状态', + `payment_method` char(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付方式', + `user_id` bigint NULL DEFAULT NULL COMMENT '用户ID', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1743813012090322946 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '支付订单表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of payment_orders +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sd_model_param +-- ---------------------------- +DROP TABLE IF EXISTS `sd_model_param`; +CREATE TABLE `sd_model_param` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id', + `prompt` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '描述词', + `negative_prompt` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT '负面词', + `model_name` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '模型名称', + `steps` int NULL DEFAULT NULL COMMENT '迭代步数', + `seed` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '种子', + `width` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '图片宽度', + `height` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '图片高度', + `sampler_name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '采样方法', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `version` int NULL DEFAULT NULL COMMENT '版本', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', + `update_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新IP', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户Id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '模型参数信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sd_model_param +-- ---------------------------- +INSERT INTO `sd_model_param` VALUES (1, '', '(((simple background))),monochrome ,lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, lowres, bad anatomy, bad hands, text, error, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, ugly,pregnant,vore,duplicate,morbid,mut ilated,tran nsexual, hermaphrodite,long neck,mutated hands,poorly drawn hands,poorly drawn face,mutation,deformed,blurry,bad anatomy,bad proportions,malformed limbs,extra limbs,cloned face,disfigured,gross proportions, (((missing arms))),(((missing legs))), (((extra arms))),(((extra legs))),pubic hair, plump,bad legs,error legs,username,blurry,bad feet', '国风3 GuoFeng3_v3.4.safetensors [a83e25fe5b]', 30, '-1', '768', '1024', 'DPM++ SDE Karras', NULL, NULL, '', '', NULL, NULL, NULL, '0', NULL, 0); + +-- ---------------------------- +-- Table structure for sensitive_word +-- ---------------------------- +DROP TABLE IF EXISTS `sensitive_word`; +CREATE TABLE `sensitive_word` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '敏感词内容', + `status` int NOT NULL COMMENT '状态 1 启用 2 停用', + `is_deleted` int NULL DEFAULT 0 COMMENT '是否删除 0 否 NULL 是', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '敏感词表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sensitive_word +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `config_id` bigint NOT NULL COMMENT '参数主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数名称', + `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数键名', + `config_value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数键值', + `config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '系统内置(Y是 N否)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`config_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '参数配置表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +INSERT INTO `sys_config` VALUES (1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow'); +INSERT INTO `sys_config` VALUES (2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '初始化密码 123456'); +INSERT INTO `sys_config` VALUES (3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '深色主题theme-dark,浅色主题theme-light'); +INSERT INTO `sys_config` VALUES (5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '是否开启注册用户功能(true开启,false关闭)'); +INSERT INTO `sys_config` VALUES (11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, '2023-05-14 15:19:42', NULL, NULL, 'true:开启, false:关闭'); +INSERT INTO `sys_config` VALUES (1729685495520854018, '911866', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow'); +INSERT INTO `sys_config` VALUES (1729685495520854019, '911866', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', '初始化密码 123456'); +INSERT INTO `sys_config` VALUES (1729685495520854020, '911866', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', '深色主题theme-dark,浅色主题theme-light'); +INSERT INTO `sys_config` VALUES (1729685495583768578, '911866', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', '是否开启注册用户功能(true开启,false关闭)'); +INSERT INTO `sys_config` VALUES (1729685495583768579, '911866', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', 'true:开启, false:关闭'); + +-- ---------------------------- +-- Table structure for sys_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dept`; +CREATE TABLE `sys_dept` ( + `dept_id` bigint NOT NULL COMMENT '部门id', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `parent_id` bigint NULL DEFAULT 0 COMMENT '父部门id', + `ancestors` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '祖级列表', + `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称', + `order_num` int NULL DEFAULT 0 COMMENT '显示顺序', + `leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '负责人', + `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '部门状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`dept_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dept +-- ---------------------------- +INSERT INTO `sys_dept` VALUES (100, '000000', 0, '0', '熊猫科技', 0, 'ageerle', '15888888888', 'ageerle@163.com', '0', '0', 103, 1, '2023-05-14 15:19:39', 1, '2023-12-29 11:18:24'); +INSERT INTO `sys_dept` VALUES (101, '000000', 100, '0,100', '深圳总公司', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (102, '000000', 100, '0,100', '长沙分公司', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (103, '000000', 101, '0,100,101', '研发部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (104, '000000', 101, '0,100,101', '市场部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (105, '000000', 101, '0,100,101', '测试部门', 3, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (106, '000000', 101, '0,100,101', '财务部门', 4, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (107, '000000', 101, '0,100,101', '运维部门', 5, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (108, '000000', 102, '0,100,102', '市场部门', 1, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (109, '000000', 102, '0,100,102', '财务部门', 2, '疯狂的狮子Li', '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_dept` VALUES (1729685491964084226, '911866', 0, '0', '5126', 0, 'admin', NULL, NULL, '0', '2', 103, 1, '2023-11-29 10:15:32', 1, '2023-11-29 10:15:32'); + +-- ---------------------------- +-- Table structure for sys_dict_data +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_data`; +CREATE TABLE `sys_dict_data` ( + `dict_code` bigint NOT NULL COMMENT '字典编码', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `dict_sort` int NULL DEFAULT 0 COMMENT '字典排序', + `dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典标签', + `dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典键值', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型', + `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '样式属性(其他样式扩展)', + `list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表格回显样式', + `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '是否默认(Y是 N否)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`dict_code`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典数据表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dict_data +-- ---------------------------- +INSERT INTO `sys_dict_data` VALUES (1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '性别男'); +INSERT INTO `sys_dict_data` VALUES (2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '性别女'); +INSERT INTO `sys_dict_data` VALUES (3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '性别未知'); +INSERT INTO `sys_dict_data` VALUES (4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '显示菜单'); +INSERT INTO `sys_dict_data` VALUES (5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '隐藏菜单'); +INSERT INTO `sys_dict_data` VALUES (6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '正常状态'); +INSERT INTO `sys_dict_data` VALUES (7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '停用状态'); +INSERT INTO `sys_dict_data` VALUES (12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '系统默认是'); +INSERT INTO `sys_dict_data` VALUES (13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '系统默认否'); +INSERT INTO `sys_dict_data` VALUES (14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '通知'); +INSERT INTO `sys_dict_data` VALUES (15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '公告'); +INSERT INTO `sys_dict_data` VALUES (16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '正常状态'); +INSERT INTO `sys_dict_data` VALUES (17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '关闭状态'); +INSERT INTO `sys_dict_data` VALUES (18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '新增操作'); +INSERT INTO `sys_dict_data` VALUES (19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '修改操作'); +INSERT INTO `sys_dict_data` VALUES (20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '删除操作'); +INSERT INTO `sys_dict_data` VALUES (21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '授权操作'); +INSERT INTO `sys_dict_data` VALUES (22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '导出操作'); +INSERT INTO `sys_dict_data` VALUES (23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '导入操作'); +INSERT INTO `sys_dict_data` VALUES (24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '强退操作'); +INSERT INTO `sys_dict_data` VALUES (25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '生成操作'); +INSERT INTO `sys_dict_data` VALUES (26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '清空操作'); +INSERT INTO `sys_dict_data` VALUES (27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '正常状态'); +INSERT INTO `sys_dict_data` VALUES (28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '停用状态'); +INSERT INTO `sys_dict_data` VALUES (29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '其他操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736897, '911866', 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '性别男'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736898, '911866', 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '性别女'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736899, '911866', 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '性别未知'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736900, '911866', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '显示菜单'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736901, '911866', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '隐藏菜单'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736902, '911866', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '正常状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736903, '911866', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '停用状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494870736904, '911866', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '系统默认是'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845761, '911866', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '系统默认否'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845762, '911866', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '通知'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845763, '911866', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '公告'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845764, '911866', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '正常状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845765, '911866', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '关闭状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845766, '911866', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '新增操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845767, '911866', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '修改操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845768, '911866', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '删除操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845769, '911866', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '授权操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845770, '911866', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '导出操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845771, '911866', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '导入操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845772, '911866', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '强退操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845773, '911866', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '生成操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845774, '911866', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '清空操作'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845775, '911866', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '正常状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845776, '911866', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '停用状态'); +INSERT INTO `sys_dict_data` VALUES (1729685494937845777, '911866', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '其他操作'); + +-- ---------------------------- +-- Table structure for sys_dict_type +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_type`; +CREATE TABLE `sys_dict_type` ( + `dict_id` bigint NOT NULL COMMENT '字典主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典名称', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`dict_id`) USING BTREE, + UNIQUE INDEX `tenant_id`(`tenant_id` ASC, `dict_type` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典类型表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dict_type +-- ---------------------------- +INSERT INTO `sys_dict_type` VALUES (1, '000000', '用户性别', 'sys_user_sex', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '用户性别列表'); +INSERT INTO `sys_dict_type` VALUES (2, '000000', '菜单状态', 'sys_show_hide', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '菜单状态列表'); +INSERT INTO `sys_dict_type` VALUES (3, '000000', '系统开关', 'sys_normal_disable', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '系统开关列表'); +INSERT INTO `sys_dict_type` VALUES (6, '000000', '系统是否', 'sys_yes_no', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '系统是否列表'); +INSERT INTO `sys_dict_type` VALUES (7, '000000', '通知类型', 'sys_notice_type', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '通知类型列表'); +INSERT INTO `sys_dict_type` VALUES (8, '000000', '通知状态', 'sys_notice_status', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '通知状态列表'); +INSERT INTO `sys_dict_type` VALUES (9, '000000', '操作类型', 'sys_oper_type', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '操作类型列表'); +INSERT INTO `sys_dict_type` VALUES (10, '000000', '系统状态', 'sys_common_status', '0', 103, 1, '2023-05-14 15:19:41', NULL, NULL, '登录状态列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083714, '911866', '用户性别', 'sys_user_sex', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '用户性别列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083715, '911866', '菜单状态', 'sys_show_hide', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '菜单状态列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083716, '911866', '系统开关', 'sys_normal_disable', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '系统开关列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083717, '911866', '系统是否', 'sys_yes_no', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '系统是否列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083718, '911866', '通知类型', 'sys_notice_type', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '通知类型列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083719, '911866', '通知状态', 'sys_notice_status', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '通知状态列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083720, '911866', '操作类型', 'sys_oper_type', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '操作类型列表'); +INSERT INTO `sys_dict_type` VALUES (1729685494468083721, '911866', '系统状态', 'sys_common_status', '0', 103, 1, '2023-05-14 15:19:41', 1, '2023-05-14 15:19:41', '登录状态列表'); + +-- ---------------------------- +-- Table structure for sys_file_info +-- ---------------------------- +DROP TABLE IF EXISTS `sys_file_info`; +CREATE TABLE `sys_file_info` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '文件id', + `url` varchar(512) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '文件访问地址', + `size` bigint NULL DEFAULT NULL COMMENT '文件大小,单位字节', + `filename` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '文件名称', + `original_filename` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '原始文件名', + `base_path` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '基础存储路径', + `path` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '存储路径', + `ext` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '文件扩展名', + `user_id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '文件所属用户', + `file_type` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '文件类型', + `attr` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT '附加属性', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `version` int NULL DEFAULT NULL COMMENT '版本', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', + `update_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新IP', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户Id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1688133688718106627 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '文件记录表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_file_info +-- ---------------------------- +INSERT INTO `sys_file_info` VALUES (1680497615330447362, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680497611488464896.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 16:40:28', NULL, NULL, '2023-07-16 16:40:28', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680498392497229826, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680498388747522048.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 16:43:34', NULL, NULL, '2023-07-16 16:43:34', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680498798468108290, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680498794991030272.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 16:45:10', NULL, NULL, '2023-07-16 16:45:10', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680499270151147522, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680499266577600512.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 16:47:03', NULL, NULL, '2023-07-16 16:47:03', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680501243424362498, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680501239389442048.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 16:54:53', NULL, NULL, '2023-07-16 16:54:53', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504215915024386, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504212333088768.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:06:42', NULL, NULL, '2023-07-16 17:06:42', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504359825788929, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504356621340672.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:07:16', NULL, NULL, '2023-07-16 17:07:16', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504461810290689, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504458815557632.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:07:41', NULL, NULL, '2023-07-16 17:07:41', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504563446665217, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504560128970752.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:08:05', NULL, NULL, '2023-07-16 17:08:05', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504666311970817, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504662826504192.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:08:29', NULL, NULL, '2023-07-16 17:08:29', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504771475755009, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504767772184576.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:08:54', NULL, NULL, '2023-07-16 17:08:54', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504875007954945, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504871493128192.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:09:19', NULL, NULL, '2023-07-16 17:09:19', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680504979622285314, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680504976069709824.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:09:44', NULL, NULL, '2023-07-16 17:09:44', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505086774169602, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505083313868800.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:10:10', NULL, NULL, '2023-07-16 17:10:10', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505190952292354, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505187441659904.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:10:34', NULL, NULL, '2023-07-16 17:10:34', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505293607882754, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505290164359168.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:10:59', NULL, NULL, '2023-07-16 17:10:59', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505399979626497, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505396498354176.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:11:24', NULL, NULL, '2023-07-16 17:11:24', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505503469883394, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505500106051584.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:11:49', NULL, NULL, '2023-07-16 17:11:49', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680505903468072962, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680505900011966464.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:13:24', NULL, NULL, '2023-07-16 17:13:24', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680506303004889090, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680506299167100928.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:15:00', NULL, NULL, '2023-07-16 17:15:00', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680506406075715586, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680506402477002752.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:15:24', NULL, NULL, '2023-07-16 17:15:24', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680506507569483778, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680506504318898176.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:15:48', NULL, NULL, '2023-07-16 17:15:48', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680506608983560193, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680506605502287872.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:16:13', NULL, NULL, '2023-07-16 17:16:13', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680506908322648066, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680506904853958656.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:17:24', NULL, NULL, '2023-07-16 17:17:24', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680507008256135169, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680507004808417280.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:17:48', NULL, NULL, '2023-07-16 17:17:48', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680507147406364674, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680507143727960064.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:18:21', NULL, NULL, '2023-07-16 17:18:21', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680507359759781889, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680507356144291840.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:19:12', NULL, NULL, '2023-07-16 17:19:12', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680507970840514561, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680507967422156800.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 17:21:37', NULL, NULL, '2023-07-16 17:21:37', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680526836295618562, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680526832839512064.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 18:36:35', NULL, NULL, '2023-07-16 18:36:35', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680526937252515842, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680526933968375808.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 18:36:59', NULL, NULL, '2023-07-16 18:36:59', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680527038914056193, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680527035281788928.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 18:37:23', NULL, NULL, '2023-07-16 18:37:23', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1680527140101640194, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-07-16/1680527136674893824.png', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'sd', NULL, NULL, '2023-07-16 18:37:48', NULL, NULL, '2023-07-16 18:37:48', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688105795313041409, '/StableDiffusion/2023-08-06/1688105795258515456.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:32:40', '1', '1', '2023-08-06 16:32:40', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688105901743505409, '/StableDiffusion/2023-08-06/1688105901739311104.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:33:05', '1', '1', '2023-08-06 16:33:05', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688106313796100097, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688106310495182848.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:34:43', '1', '1', '2023-08-06 16:34:43', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688106572739846146, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688106568948195328.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:35:45', '1', '1', '2023-08-06 16:35:45', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688108066235031554, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688108062321745920.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:41:41', '1', '1', '2023-08-06 16:41:41', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688108539901984770, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688108525284835328.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:43:34', '1', '1', '2023-08-06 16:43:34', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688109337788628994, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688109333917286400.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:46:44', '1', '1', '2023-08-06 16:46:44', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688109526054158337, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688109522946179072.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:47:29', '1', '1', '2023-08-06 16:47:29', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688109680681369602, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688109676877135872.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:48:06', '1', '1', '2023-08-06 16:48:06', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688109962416963586, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688109958998605824.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:49:13', '1', '1', '2023-08-06 16:49:13', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688110249684844545, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688110246283264000.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:50:22', '1', '1', '2023-08-06 16:50:22', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688110430224465922, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688110426466369536.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:51:05', '1', '1', '2023-08-06 16:51:05', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688110522054557698, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688110519072407552.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:51:27', '1', '1', '2023-08-06 16:51:27', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688110601553395714, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688110598130843648.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 16:51:46', '1', '1', '2023-08-06 16:51:46', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688133085753352193, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688133081856843776.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 18:21:06', '1', '1', '2023-08-06 18:21:06', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688133343300395010, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688133339932368896.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 18:22:08', '1', '1', '2023-08-06 18:22:08', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688133448241881090, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688133445284896768.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 18:22:33', '1', '1', '2023-08-06 18:22:33', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688133518894931970, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688133515803729920.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 18:22:50', '1', '1', '2023-08-06 18:22:50', NULL, NULL, '0', NULL, 0); +INSERT INTO `sys_file_info` VALUES (1688133688718106626, 'https://panda-1253683406.cos.ap-guangzhou.myqcloud.com/StableDiffusion/2023-08-06/1688133685488492544.png', NULL, NULL, NULL, NULL, NULL, NULL, '1', 'sd', NULL, 103, '2023-08-06 18:23:30', '1', '1', '2023-08-06 18:23:30', NULL, NULL, '0', NULL, 0); + +-- ---------------------------- +-- Table structure for sys_logininfor +-- ---------------------------- +DROP TABLE IF EXISTS `sys_logininfor`; +CREATE TABLE `sys_logininfor` ( + `info_id` bigint NOT NULL COMMENT '访问ID', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户账号', + `ipaddr` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录IP地址', + `login_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录地点', + `browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '浏览器类型', + `os` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作系统', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '登录状态(0成功 1失败)', + `msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '提示消息', + `login_time` datetime NULL DEFAULT NULL COMMENT '访问时间', + PRIMARY KEY (`info_id`) USING BTREE, + INDEX `idx_sys_logininfor_s`(`status` ASC) USING BTREE, + INDEX `idx_sys_logininfor_lt`(`login_time` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统访问记录' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_logininfor +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu`; +CREATE TABLE `sys_menu` ( + `menu_id` bigint NOT NULL COMMENT '菜单ID', + `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称', + `parent_id` bigint NULL DEFAULT 0 COMMENT '父菜单ID', + `order_num` int NULL DEFAULT 0 COMMENT '显示顺序', + `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '路由地址', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组件路径', + `query_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由参数', + `is_frame` int NULL DEFAULT 1 COMMENT '是否为外链(0是 1否)', + `is_cache` int NULL DEFAULT 0 COMMENT '是否缓存(0缓存 1不缓存)', + `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)', + `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '显示状态(0显示 1隐藏)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '菜单状态(0正常 1停用)', + `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限标识', + `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '#' COMMENT '菜单图标', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注', + PRIMARY KEY (`menu_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单权限表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_menu +-- ---------------------------- +INSERT INTO `sys_menu` VALUES (1, '系统管理', 0, 1, 'system', NULL, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '系统管理目录'); +INSERT INTO `sys_menu` VALUES (2, '系统监控', 0, 3, 'monitor', NULL, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '系统监控目录'); +INSERT INTO `sys_menu` VALUES (6, '租户管理', 0, 2, 'tenant', NULL, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '租户管理目录'); +INSERT INTO `sys_menu` VALUES (100, '用户管理', 1, 1, 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '用户管理菜单'); +INSERT INTO `sys_menu` VALUES (101, '角色管理', 1, 2, 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '角色管理菜单'); +INSERT INTO `sys_menu` VALUES (102, '菜单管理', 1, 3, 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '菜单管理菜单'); +INSERT INTO `sys_menu` VALUES (103, '部门管理', 1, 4, 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '部门管理菜单'); +INSERT INTO `sys_menu` VALUES (104, '岗位管理', 1, 5, 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '岗位管理菜单'); +INSERT INTO `sys_menu` VALUES (105, '字典管理', 1, 6, 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '字典管理菜单'); +INSERT INTO `sys_menu` VALUES (106, '参数设置', 1, 7, 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '参数设置菜单'); +INSERT INTO `sys_menu` VALUES (107, '通知公告', 1, 8, 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '通知公告菜单'); +INSERT INTO `sys_menu` VALUES (108, '日志管理', 1, 9, 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '日志管理菜单'); +INSERT INTO `sys_menu` VALUES (109, '在线用户', 2, 1, 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '在线用户菜单'); +INSERT INTO `sys_menu` VALUES (113, '缓存监控', 2, 5, 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '缓存监控菜单'); +INSERT INTO `sys_menu` VALUES (115, '代码生成', 1, 2, 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, '2023-05-14 15:19:40', 1, '2023-12-29 11:33:01', '代码生成菜单'); +INSERT INTO `sys_menu` VALUES (118, '文件管理', 1, 10, 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '文件管理菜单'); +INSERT INTO `sys_menu` VALUES (121, '租户管理', 6, 1, 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '租户管理菜单'); +INSERT INTO `sys_menu` VALUES (122, '租户套餐管理', 6, 2, 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '租户套餐管理菜单'); +INSERT INTO `sys_menu` VALUES (500, '操作日志', 108, 1, 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '操作日志菜单'); +INSERT INTO `sys_menu` VALUES (501, '登录日志', 108, 2, 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '登录日志菜单'); +INSERT INTO `sys_menu` VALUES (1001, '用户查询', 100, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1002, '用户新增', 100, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1003, '用户修改', 100, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1004, '用户删除', 100, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1005, '用户导出', 100, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1006, '用户导入', 100, 6, '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1007, '重置密码', 100, 7, '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1008, '角色查询', 101, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1009, '角色新增', 101, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1010, '角色修改', 101, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1011, '角色删除', 101, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1012, '角色导出', 101, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1013, '菜单查询', 102, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1014, '菜单新增', 102, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1015, '菜单修改', 102, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1016, '菜单删除', 102, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1017, '部门查询', 103, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1018, '部门新增', 103, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1019, '部门修改', 103, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1020, '部门删除', 103, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1021, '岗位查询', 104, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1022, '岗位新增', 104, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1023, '岗位修改', 104, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1024, '岗位删除', 104, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1025, '岗位导出', 104, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1026, '字典查询', 105, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1027, '字典新增', 105, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1028, '字典修改', 105, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1029, '字典删除', 105, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1030, '字典导出', 105, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1031, '参数查询', 106, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1032, '参数新增', 106, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1033, '参数修改', 106, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1034, '参数删除', 106, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1035, '参数导出', 106, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1036, '公告查询', 107, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1037, '公告新增', 107, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1038, '公告修改', 107, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1039, '公告删除', 107, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1040, '操作查询', 500, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1041, '操作删除', 500, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1042, '日志导出', 500, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1043, '登录查询', 501, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1044, '登录删除', 501, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1045, '日志导出', 501, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1046, '在线查询', 109, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1047, '批量强退', 109, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1048, '单条强退', 109, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1050, '账户解锁', 501, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1055, '生成查询', 115, 1, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1056, '生成修改', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1057, '生成删除', 115, 3, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1058, '导入代码', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1059, '预览代码', 115, 4, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1060, '生成代码', 115, 5, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1600, '文件查询', 118, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1601, '文件上传', 118, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1602, '文件下载', 118, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1603, '文件删除', 118, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1604, '配置添加', 118, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1605, '配置编辑', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1606, '租户查询', 121, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1607, '租户新增', 121, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1608, '租户修改', 121, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1609, '租户删除', 121, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1610, '租户导出', 121, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1611, '租户套餐查询', 122, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1612, '租户套餐新增', 122, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1613, '租户套餐修改', 122, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1614, '租户套餐删除', 122, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1615, '租户套餐导出', 122, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); + +-- ---------------------------- +-- Table structure for sys_menu_copy1 +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu_copy1`; +CREATE TABLE `sys_menu_copy1` ( + `menu_id` bigint NOT NULL COMMENT '菜单ID', + `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称', + `parent_id` bigint NULL DEFAULT 0 COMMENT '父菜单ID', + `order_num` int NULL DEFAULT 0 COMMENT '显示顺序', + `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '路由地址', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组件路径', + `query_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由参数', + `is_frame` int NULL DEFAULT 1 COMMENT '是否为外链(0是 1否)', + `is_cache` int NULL DEFAULT 0 COMMENT '是否缓存(0缓存 1不缓存)', + `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)', + `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '显示状态(0显示 1隐藏)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '菜单状态(0正常 1停用)', + `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限标识', + `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '#' COMMENT '菜单图标', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注', + PRIMARY KEY (`menu_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单权限表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_menu_copy1 +-- ---------------------------- +INSERT INTO `sys_menu_copy1` VALUES (1, '系统管理', 0, 1, 'system', NULL, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '系统管理目录'); +INSERT INTO `sys_menu_copy1` VALUES (2, '系统监控', 0, 3, 'monitor', NULL, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '系统监控目录'); +INSERT INTO `sys_menu_copy1` VALUES (3, '系统工具', 0, 4, 'tool', NULL, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '系统工具目录'); +INSERT INTO `sys_menu_copy1` VALUES (4, 'PLUS官网', 0, 5, 'https://gitee.com/dromara/RuoYi-Vue-Plus', NULL, '', 0, 0, 'M', '0', '0', '', 'guide', 103, 1, '2023-05-14 15:19:39', NULL, NULL, 'RuoYi-Vue-Plus官网地址'); +INSERT INTO `sys_menu_copy1` VALUES (6, '租户管理', 0, 2, 'tenant', NULL, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '租户管理目录'); +INSERT INTO `sys_menu_copy1` VALUES (100, '用户管理', 1, 1, 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '用户管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (101, '角色管理', 1, 2, 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '角色管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (102, '菜单管理', 1, 3, 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '菜单管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (103, '部门管理', 1, 4, 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '部门管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (104, '岗位管理', 1, 5, 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '岗位管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (105, '字典管理', 1, 6, 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '字典管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (106, '参数设置', 1, 7, 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '参数设置菜单'); +INSERT INTO `sys_menu_copy1` VALUES (107, '通知公告', 1, 8, 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '通知公告菜单'); +INSERT INTO `sys_menu_copy1` VALUES (108, '日志管理', 1, 9, 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '日志管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (109, '在线用户', 2, 1, 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '在线用户菜单'); +INSERT INTO `sys_menu_copy1` VALUES (113, '缓存监控', 2, 5, 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '缓存监控菜单'); +INSERT INTO `sys_menu_copy1` VALUES (114, '表单构建', 3, 1, 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '表单构建菜单'); +INSERT INTO `sys_menu_copy1` VALUES (115, '代码生成', 3, 2, 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '代码生成菜单'); +INSERT INTO `sys_menu_copy1` VALUES (118, '文件管理', 1, 10, 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '文件管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (121, '租户管理', 6, 1, 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '租户管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (122, '租户套餐管理', 6, 2, 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '租户套餐管理菜单'); +INSERT INTO `sys_menu_copy1` VALUES (500, '操作日志', 108, 1, 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '操作日志菜单'); +INSERT INTO `sys_menu_copy1` VALUES (501, '登录日志', 108, 2, 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, '2023-05-14 15:19:40', NULL, NULL, '登录日志菜单'); +INSERT INTO `sys_menu_copy1` VALUES (1001, '用户查询', 100, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1002, '用户新增', 100, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1003, '用户修改', 100, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1004, '用户删除', 100, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1005, '用户导出', 100, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1006, '用户导入', 100, 6, '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1007, '重置密码', 100, 7, '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1008, '角色查询', 101, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1009, '角色新增', 101, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1010, '角色修改', 101, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1011, '角色删除', 101, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1012, '角色导出', 101, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1013, '菜单查询', 102, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1014, '菜单新增', 102, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1015, '菜单修改', 102, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1016, '菜单删除', 102, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1017, '部门查询', 103, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1018, '部门新增', 103, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1019, '部门修改', 103, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1020, '部门删除', 103, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1021, '岗位查询', 104, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1022, '岗位新增', 104, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1023, '岗位修改', 104, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1024, '岗位删除', 104, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1025, '岗位导出', 104, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1026, '字典查询', 105, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1027, '字典新增', 105, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1028, '字典修改', 105, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1029, '字典删除', 105, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1030, '字典导出', 105, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1031, '参数查询', 106, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1032, '参数新增', 106, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1033, '参数修改', 106, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1034, '参数删除', 106, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1035, '参数导出', 106, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1036, '公告查询', 107, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1037, '公告新增', 107, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1038, '公告修改', 107, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1039, '公告删除', 107, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1040, '操作查询', 500, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1041, '操作删除', 500, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1042, '日志导出', 500, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1043, '登录查询', 501, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1044, '登录删除', 501, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1045, '日志导出', 501, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1046, '在线查询', 109, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1047, '批量强退', 109, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1048, '单条强退', 109, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1050, '账户解锁', 501, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1055, '生成查询', 115, 1, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1056, '生成修改', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1057, '生成删除', 115, 3, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1058, '导入代码', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1059, '预览代码', 115, 4, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1060, '生成代码', 115, 5, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1600, '文件查询', 118, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1601, '文件上传', 118, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1602, '文件下载', 118, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1603, '文件删除', 118, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1604, '配置添加', 118, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1605, '配置编辑', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1606, '租户查询', 121, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1607, '租户新增', 121, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1608, '租户修改', 121, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1609, '租户删除', 121, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1610, '租户导出', 121, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1611, '租户套餐查询', 122, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1612, '租户套餐新增', 122, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1613, '租户套餐修改', 122, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1614, '租户套餐删除', 122, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1615, '租户套餐导出', 122, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, '2023-05-14 15:19:40', NULL, NULL, ''); +INSERT INTO `sys_menu_copy1` VALUES (1689201668374556674, 'MJ服务器', 1689205943360188417, 1, 'mjserver', 'midjourney/mjserver/index', NULL, 1, 0, 'C', '0', '0', 'midjourney:mjserver:list', 'cascader', 103, 1, '2023-08-09 17:30:43', 1, '2023-08-09 17:41:50', 'MJ服务器信息配置菜单'); +INSERT INTO `sys_menu_copy1` VALUES (1689205943360188417, 'Midjourney', 0, 1, 'Midjourney', NULL, NULL, 1, 0, 'M', '0', '0', NULL, 'documentation', 103, 1, '2023-08-09 17:24:15', 1, '2023-08-09 17:24:15', ''); +INSERT INTO `sys_menu_copy1` VALUES (1689243465037561858, '创作记录', 1689205943360188417, 1, 'mjRoomMsg', 'midjourney/mjRoomMsg/index', NULL, 1, 0, 'C', '0', '0', 'midjourney:mjRoomMsg:list', 'documentation', 103, 1, '2023-08-09 20:10:20', 1, '2023-08-09 20:11:51', 'Midjourney 创作记录菜单'); +INSERT INTO `sys_menu_copy1` VALUES (1689243466220355585, 'MJ创作会话', 1689205943360188417, 1, 'mjroom', 'midjourney/mjroom/index', NULL, 1, 0, 'C', '0', '0', 'midjourney:mjroom:list', 'example', 103, 1, '2023-08-09 20:10:04', 1, '2023-08-09 20:12:41', 'MJ创作会话菜单'); + +-- ---------------------------- +-- Table structure for sys_notice +-- ---------------------------- +DROP TABLE IF EXISTS `sys_notice`; +CREATE TABLE `sys_notice` ( + `notice_id` bigint NOT NULL COMMENT '公告ID', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `notice_title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告标题', + `notice_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告类型(1通知 2公告)', + `notice_content` longblob NULL COMMENT '公告内容', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`notice_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '通知公告表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_notice +-- ---------------------------- +INSERT INTO `sys_notice` VALUES (1, '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', 0xE696B0E78988E69CACE58685E5AEB9, '0', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '管理员'); +INSERT INTO `sys_notice` VALUES (2, '000000', '维护通知:2018-07-01 系统凌晨维护', '1', 0xE7BBB4E68AA4E58685E5AEB9, '0', 103, 1, '2023-05-14 15:19:42', NULL, NULL, '管理员'); + +-- ---------------------------- +-- Table structure for sys_oper_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_oper_log`; +CREATE TABLE `sys_oper_log` ( + `oper_id` bigint NOT NULL COMMENT '日志主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '模块标题', + `business_type` int NULL DEFAULT 0 COMMENT '业务类型(0其它 1新增 2修改 3删除)', + `method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '方法名称', + `request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求方式', + `operator_type` int NULL DEFAULT 0 COMMENT '操作类别(0其它 1后台用户 2手机端用户)', + `oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作人员', + `dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称', + `oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求URL', + `oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '主机地址', + `oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作地点', + `oper_param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求参数', + `json_result` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '返回参数', + `status` int NULL DEFAULT 0 COMMENT '操作状态(0正常 1异常)', + `error_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '错误消息', + `oper_time` datetime NULL DEFAULT NULL COMMENT '操作时间', + `cost_time` bigint NULL DEFAULT 0 COMMENT '消耗时间', + PRIMARY KEY (`oper_id`) USING BTREE, + INDEX `idx_sys_oper_log_bt`(`business_type` ASC) USING BTREE, + INDEX `idx_sys_oper_log_s`(`status` ASC) USING BTREE, + INDEX `idx_sys_oper_log_ot`(`oper_time` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '操作日志记录' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_oper_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_oss +-- ---------------------------- +DROP TABLE IF EXISTS `sys_oss`; +CREATE TABLE `sys_oss` ( + `oss_id` bigint NOT NULL COMMENT '对象存储主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件名', + `original_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '原名', + `file_suffix` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件后缀名', + `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'URL地址', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `create_by` bigint NULL DEFAULT NULL COMMENT '上传人', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新人', + `service` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'minio' COMMENT '服务商', + PRIMARY KEY (`oss_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'OSS对象存储表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_oss +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_oss_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_oss_config`; +CREATE TABLE `sys_oss_config` ( + `oss_config_id` bigint NOT NULL COMMENT '主建', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `config_key` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置key', + `access_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'accessKey', + `secret_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '秘钥', + `bucket_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '桶名称', + `prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '前缀', + `endpoint` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '访问站点', + `domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '自定义域名', + `is_https` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '是否https(Y=是,N=否)', + `region` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '域', + `access_policy` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '桶权限类型(0=private 1=public 2=custom)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '是否默认(0=是,1=否)', + `ext1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '扩展字段', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`oss_config_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '对象存储配置表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_oss_config +-- ---------------------------- +INSERT INTO `sys_oss_config` VALUES (1, '000000', 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '', 'N', '', '1', '1', '', 103, 1, '2023-05-14 15:19:42', 1, '2023-07-13 23:28:18', NULL); +INSERT INTO `sys_oss_config` VALUES (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '', 'N', '', '1', '1', '', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', NULL); +INSERT INTO `sys_oss_config` VALUES (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '', 'N', '', '1', '1', '', 103, 1, '2023-05-14 15:19:42', 1, '2023-07-13 23:35:23', NULL); +INSERT INTO `sys_oss_config` VALUES (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', 'image', '127.0.0.1:9000', '', 'N', '', '1', '0', '', 103, 1, '2023-05-14 15:19:42', 1, '2023-11-13 23:58:09', ''); +INSERT INTO `sys_oss_config` VALUES (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '', 'N', '', '1', '1', '', 103, 1, '2023-05-14 15:19:42', 1, '2023-05-14 15:19:42', NULL); + +-- ---------------------------- +-- Table structure for sys_post +-- ---------------------------- +DROP TABLE IF EXISTS `sys_post`; +CREATE TABLE `sys_post` ( + `post_id` bigint NOT NULL COMMENT '岗位ID', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位编码', + `post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位名称', + `post_sort` int NOT NULL COMMENT '显示顺序', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态(0正常 1停用)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`post_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '岗位信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_post +-- ---------------------------- +INSERT INTO `sys_post` VALUES (1, '000000', 'ceo', '董事长', 1, '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, ''); +INSERT INTO `sys_post` VALUES (2, '000000', 'se', '项目经理', 2, '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, ''); +INSERT INTO `sys_post` VALUES (3, '000000', 'hr', '人力资源', 3, '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, ''); +INSERT INTO `sys_post` VALUES (4, '000000', 'user', '普通员工', 4, '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, ''); + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称', + `role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色权限字符串', + `role_sort` int NOT NULL COMMENT '显示顺序', + `data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + `menu_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '菜单树选择项是否关联显示', + `dept_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '部门树选择项是否关联显示', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`role_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +INSERT INTO `sys_role` VALUES (1, '000000', '超级管理员', 'superadmin', 1, '1', 1, 1, '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '超级管理员'); +INSERT INTO `sys_role` VALUES (2, '000000', '普通角色', 'common', 2, '2', 1, 1, '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL, '普通角色'); +INSERT INTO `sys_role` VALUES (3, '000000', '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 103, 1, '2023-05-14 15:20:00', 1, '2023-06-04 10:20:43', NULL); +INSERT INTO `sys_role` VALUES (4, '000000', '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 103, 1, '2023-05-14 15:20:00', 1, '2023-06-04 10:21:01', NULL); +INSERT INTO `sys_role` VALUES (1661661183933177857, '000000', '小程序管理员', 'xcxadmin', 1, '1', 1, 1, '0', '0', 103, 1, '2023-05-25 17:11:13', 1, '2023-05-25 17:11:13', ''); +INSERT INTO `sys_role` VALUES (1729685491108446210, '911866', '管理员', 'admin', 1, '1', 1, 1, '0', '0', 103, 1, '2023-11-29 10:15:32', 1, '2023-11-29 10:15:32', NULL); + +-- ---------------------------- +-- 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 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色和部门关联表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_role_dept +-- ---------------------------- +INSERT INTO `sys_role_dept` VALUES (2, 100); +INSERT INTO `sys_role_dept` VALUES (2, 101); +INSERT INTO `sys_role_dept` VALUES (2, 105); +INSERT INTO `sys_role_dept` VALUES (1729685491108446210, 1729685491964084226); + +-- ---------------------------- +-- 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 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色和菜单关联表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_role_menu +-- ---------------------------- +INSERT INTO `sys_role_menu` VALUES (2, 1); +INSERT INTO `sys_role_menu` VALUES (2, 2); +INSERT INTO `sys_role_menu` VALUES (2, 3); +INSERT INTO `sys_role_menu` VALUES (2, 4); +INSERT INTO `sys_role_menu` VALUES (2, 100); +INSERT INTO `sys_role_menu` VALUES (2, 101); +INSERT INTO `sys_role_menu` VALUES (2, 102); +INSERT INTO `sys_role_menu` VALUES (2, 103); +INSERT INTO `sys_role_menu` VALUES (2, 104); +INSERT INTO `sys_role_menu` VALUES (2, 105); +INSERT INTO `sys_role_menu` VALUES (2, 106); +INSERT INTO `sys_role_menu` VALUES (2, 107); +INSERT INTO `sys_role_menu` VALUES (2, 108); +INSERT INTO `sys_role_menu` VALUES (2, 109); +INSERT INTO `sys_role_menu` VALUES (2, 110); +INSERT INTO `sys_role_menu` VALUES (2, 111); +INSERT INTO `sys_role_menu` VALUES (2, 112); +INSERT INTO `sys_role_menu` VALUES (2, 113); +INSERT INTO `sys_role_menu` VALUES (2, 114); +INSERT INTO `sys_role_menu` VALUES (2, 115); +INSERT INTO `sys_role_menu` VALUES (2, 116); +INSERT INTO `sys_role_menu` VALUES (2, 500); +INSERT INTO `sys_role_menu` VALUES (2, 501); +INSERT INTO `sys_role_menu` VALUES (2, 1000); +INSERT INTO `sys_role_menu` VALUES (2, 1001); +INSERT INTO `sys_role_menu` VALUES (2, 1002); +INSERT INTO `sys_role_menu` VALUES (2, 1003); +INSERT INTO `sys_role_menu` VALUES (2, 1004); +INSERT INTO `sys_role_menu` VALUES (2, 1005); +INSERT INTO `sys_role_menu` VALUES (2, 1006); +INSERT INTO `sys_role_menu` VALUES (2, 1007); +INSERT INTO `sys_role_menu` VALUES (2, 1008); +INSERT INTO `sys_role_menu` VALUES (2, 1009); +INSERT INTO `sys_role_menu` VALUES (2, 1010); +INSERT INTO `sys_role_menu` VALUES (2, 1011); +INSERT INTO `sys_role_menu` VALUES (2, 1012); +INSERT INTO `sys_role_menu` VALUES (2, 1013); +INSERT INTO `sys_role_menu` VALUES (2, 1014); +INSERT INTO `sys_role_menu` VALUES (2, 1015); +INSERT INTO `sys_role_menu` VALUES (2, 1016); +INSERT INTO `sys_role_menu` VALUES (2, 1017); +INSERT INTO `sys_role_menu` VALUES (2, 1018); +INSERT INTO `sys_role_menu` VALUES (2, 1019); +INSERT INTO `sys_role_menu` VALUES (2, 1020); +INSERT INTO `sys_role_menu` VALUES (2, 1021); +INSERT INTO `sys_role_menu` VALUES (2, 1022); +INSERT INTO `sys_role_menu` VALUES (2, 1023); +INSERT INTO `sys_role_menu` VALUES (2, 1024); +INSERT INTO `sys_role_menu` VALUES (2, 1025); +INSERT INTO `sys_role_menu` VALUES (2, 1026); +INSERT INTO `sys_role_menu` VALUES (2, 1027); +INSERT INTO `sys_role_menu` VALUES (2, 1028); +INSERT INTO `sys_role_menu` VALUES (2, 1029); +INSERT INTO `sys_role_menu` VALUES (2, 1030); +INSERT INTO `sys_role_menu` VALUES (2, 1031); +INSERT INTO `sys_role_menu` VALUES (2, 1032); +INSERT INTO `sys_role_menu` VALUES (2, 1033); +INSERT INTO `sys_role_menu` VALUES (2, 1034); +INSERT INTO `sys_role_menu` VALUES (2, 1035); +INSERT INTO `sys_role_menu` VALUES (2, 1036); +INSERT INTO `sys_role_menu` VALUES (2, 1037); +INSERT INTO `sys_role_menu` VALUES (2, 1038); +INSERT INTO `sys_role_menu` VALUES (2, 1039); +INSERT INTO `sys_role_menu` VALUES (2, 1040); +INSERT INTO `sys_role_menu` VALUES (2, 1041); +INSERT INTO `sys_role_menu` VALUES (2, 1042); +INSERT INTO `sys_role_menu` VALUES (2, 1043); +INSERT INTO `sys_role_menu` VALUES (2, 1044); +INSERT INTO `sys_role_menu` VALUES (2, 1045); +INSERT INTO `sys_role_menu` VALUES (2, 1046); +INSERT INTO `sys_role_menu` VALUES (2, 1047); +INSERT INTO `sys_role_menu` VALUES (2, 1048); +INSERT INTO `sys_role_menu` VALUES (2, 1050); +INSERT INTO `sys_role_menu` VALUES (2, 1055); +INSERT INTO `sys_role_menu` VALUES (2, 1056); +INSERT INTO `sys_role_menu` VALUES (2, 1057); +INSERT INTO `sys_role_menu` VALUES (2, 1058); +INSERT INTO `sys_role_menu` VALUES (2, 1059); +INSERT INTO `sys_role_menu` VALUES (2, 1060); +INSERT INTO `sys_role_menu` VALUES (3, 1); +INSERT INTO `sys_role_menu` VALUES (3, 100); +INSERT INTO `sys_role_menu` VALUES (3, 101); +INSERT INTO `sys_role_menu` VALUES (3, 102); +INSERT INTO `sys_role_menu` VALUES (3, 103); +INSERT INTO `sys_role_menu` VALUES (3, 104); +INSERT INTO `sys_role_menu` VALUES (3, 105); +INSERT INTO `sys_role_menu` VALUES (3, 106); +INSERT INTO `sys_role_menu` VALUES (3, 107); +INSERT INTO `sys_role_menu` VALUES (3, 108); +INSERT INTO `sys_role_menu` VALUES (3, 500); +INSERT INTO `sys_role_menu` VALUES (3, 501); +INSERT INTO `sys_role_menu` VALUES (3, 1001); +INSERT INTO `sys_role_menu` VALUES (3, 1002); +INSERT INTO `sys_role_menu` VALUES (3, 1003); +INSERT INTO `sys_role_menu` VALUES (3, 1004); +INSERT INTO `sys_role_menu` VALUES (3, 1005); +INSERT INTO `sys_role_menu` VALUES (3, 1006); +INSERT INTO `sys_role_menu` VALUES (3, 1007); +INSERT INTO `sys_role_menu` VALUES (3, 1008); +INSERT INTO `sys_role_menu` VALUES (3, 1009); +INSERT INTO `sys_role_menu` VALUES (3, 1010); +INSERT INTO `sys_role_menu` VALUES (3, 1011); +INSERT INTO `sys_role_menu` VALUES (3, 1012); +INSERT INTO `sys_role_menu` VALUES (3, 1013); +INSERT INTO `sys_role_menu` VALUES (3, 1014); +INSERT INTO `sys_role_menu` VALUES (3, 1015); +INSERT INTO `sys_role_menu` VALUES (3, 1016); +INSERT INTO `sys_role_menu` VALUES (3, 1017); +INSERT INTO `sys_role_menu` VALUES (3, 1018); +INSERT INTO `sys_role_menu` VALUES (3, 1019); +INSERT INTO `sys_role_menu` VALUES (3, 1020); +INSERT INTO `sys_role_menu` VALUES (3, 1021); +INSERT INTO `sys_role_menu` VALUES (3, 1022); +INSERT INTO `sys_role_menu` VALUES (3, 1023); +INSERT INTO `sys_role_menu` VALUES (3, 1024); +INSERT INTO `sys_role_menu` VALUES (3, 1025); +INSERT INTO `sys_role_menu` VALUES (3, 1026); +INSERT INTO `sys_role_menu` VALUES (3, 1027); +INSERT INTO `sys_role_menu` VALUES (3, 1028); +INSERT INTO `sys_role_menu` VALUES (3, 1029); +INSERT INTO `sys_role_menu` VALUES (3, 1030); +INSERT INTO `sys_role_menu` VALUES (3, 1031); +INSERT INTO `sys_role_menu` VALUES (3, 1032); +INSERT INTO `sys_role_menu` VALUES (3, 1033); +INSERT INTO `sys_role_menu` VALUES (3, 1034); +INSERT INTO `sys_role_menu` VALUES (3, 1035); +INSERT INTO `sys_role_menu` VALUES (3, 1036); +INSERT INTO `sys_role_menu` VALUES (3, 1037); +INSERT INTO `sys_role_menu` VALUES (3, 1038); +INSERT INTO `sys_role_menu` VALUES (3, 1039); +INSERT INTO `sys_role_menu` VALUES (3, 1040); +INSERT INTO `sys_role_menu` VALUES (3, 1041); +INSERT INTO `sys_role_menu` VALUES (3, 1042); +INSERT INTO `sys_role_menu` VALUES (3, 1043); +INSERT INTO `sys_role_menu` VALUES (3, 1044); +INSERT INTO `sys_role_menu` VALUES (3, 1045); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 100); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 107); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1001); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1002); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1003); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1004); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1005); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1006); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1007); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1036); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1037); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1038); +INSERT INTO `sys_role_menu` VALUES (1661661183933177857, 1039); +INSERT INTO `sys_role_menu` VALUES (1729685491108446210, 1689201668374556674); +INSERT INTO `sys_role_menu` VALUES (1729685491108446210, 1689205943360188417); +INSERT INTO `sys_role_menu` VALUES (1729685491108446210, 1689243465037561858); +INSERT INTO `sys_role_menu` VALUES (1729685491108446210, 1689243466220355585); + +-- ---------------------------- +-- Table structure for sys_tenant +-- ---------------------------- +DROP TABLE IF EXISTS `sys_tenant`; +CREATE TABLE `sys_tenant` ( + `id` bigint NOT NULL COMMENT 'id', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '租户编号', + `contact_user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系人', + `contact_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话', + `company_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '企业名称', + `license_number` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '统一社会信用代码', + `address` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '地址', + `intro` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '企业简介', + `domain` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '域名', + `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `package_id` bigint NULL DEFAULT NULL COMMENT '租户套餐编号', + `expire_time` datetime NULL DEFAULT NULL COMMENT '过期时间', + `account_count` int NULL DEFAULT -1 COMMENT '用户数量(-1不限制)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '租户状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '租户表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_tenant +-- ---------------------------- +INSERT INTO `sys_tenant` VALUES (1, '000000', '管理组', '15888888888', 'XXX有限公司', NULL, NULL, '多租户通用后台管理管理系统', NULL, NULL, NULL, NULL, -1, '0', '0', 103, 1, '2023-05-14 15:19:39', NULL, NULL); +INSERT INTO `sys_tenant` VALUES (1729685490647072769, '911866', '陈', '11111111111', '5126', '', '', '', '', '', 1729685389795033090, NULL, 1, '0', '2', 103, 1, '2023-11-29 10:15:32', 1, '2023-11-29 10:15:32'); + +-- ---------------------------- +-- Table structure for sys_tenant_package +-- ---------------------------- +DROP TABLE IF EXISTS `sys_tenant_package`; +CREATE TABLE `sys_tenant_package` ( + `package_id` bigint NOT NULL COMMENT '租户套餐id', + `package_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '套餐名称', + `menu_ids` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '关联菜单id', + `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `menu_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '菜单树选择项是否关联显示', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`package_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '租户套餐表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_tenant_package +-- ---------------------------- +INSERT INTO `sys_tenant_package` VALUES (1729685389795033090, '绘画', '1689205943360188417, 1689243466220355585, 1689201668374556674, 1689243465037561858', '', 1, '0', '2', 103, 1, '2023-11-29 10:15:08', 1, '2023-11-29 10:15:08'); + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `open_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信用户标识', + `user_grade` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '用户等级', + `user_balance` double(20, 2) NULL DEFAULT 0.00 COMMENT '账户余额', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID', + `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户账号', + `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户昵称', + `user_type` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'sys_user' COMMENT '用户类型(sys_user系统用户)', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户邮箱', + `phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号码', + `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像地址', + `wx_avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信头像地址', + `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '密码', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '最后登录IP', + `login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`user_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +INSERT INTO `sys_user` VALUES (1, NULL, '0', 4.00, '00000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', NULL, NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '0:0:0:0:0:0:0:1', '2024-01-05 17:11:34', 103, 1, '2023-05-14 15:19:39', 1, '2024-01-05 17:11:34', '管理员'); + +-- ---------------------------- +-- 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 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户与岗位关联表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_user_post +-- ---------------------------- +INSERT INTO `sys_user_post` VALUES (1, 1); +INSERT INTO `sys_user_post` VALUES (2, 2); +INSERT INTO `sys_user_post` VALUES (1661660085084250114, 2); +INSERT INTO `sys_user_post` VALUES (1661660804847788034, 1); + +-- ---------------------------- +-- 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 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户和角色关联表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_user_role +-- ---------------------------- +INSERT INTO `sys_user_role` VALUES (1, 1); +INSERT INTO `sys_user_role` VALUES (2, 2); +INSERT INTO `sys_user_role` VALUES (3, 3); +INSERT INTO `sys_user_role` VALUES (4, 4); +INSERT INTO `sys_user_role` VALUES (1661646824293031937, 1661661183933177857); +INSERT INTO `sys_user_role` VALUES (1661660085084250114, 1661661183933177857); +INSERT INTO `sys_user_role` VALUES (1661660804847788034, 2); +INSERT INTO `sys_user_role` VALUES (1713427806956404738, 1); +INSERT INTO `sys_user_role` VALUES (1713439839684689921, 1); +INSERT INTO `sys_user_role` VALUES (1713440206715650049, 1); +INSERT INTO `sys_user_role` VALUES (1713724795803299841, 1); +INSERT INTO `sys_user_role` VALUES (1714176194496339970, 1); +INSERT INTO `sys_user_role` VALUES (1714267685998907393, 1); +INSERT INTO `sys_user_role` VALUES (1714269581270667265, 1); +INSERT INTO `sys_user_role` VALUES (1714270420659949569, 1); +INSERT INTO `sys_user_role` VALUES (1714455864827723777, 1); +INSERT INTO `sys_user_role` VALUES (1714536425072115714, 1); +INSERT INTO `sys_user_role` VALUES (1714819715117105153, 1); +INSERT INTO `sys_user_role` VALUES (1714820415783976961, 1); +INSERT INTO `sys_user_role` VALUES (1714820611611836417, 1); +INSERT INTO `sys_user_role` VALUES (1714820755698761729, 1); +INSERT INTO `sys_user_role` VALUES (1714823588305190914, 1); +INSERT INTO `sys_user_role` VALUES (1714829502936530945, 1); +INSERT INTO `sys_user_role` VALUES (1714835527643185154, 1); +INSERT INTO `sys_user_role` VALUES (1714835835278606337, 1); +INSERT INTO `sys_user_role` VALUES (1714898663033290754, 1); +INSERT INTO `sys_user_role` VALUES (1714942733206175746, 1); +INSERT INTO `sys_user_role` VALUES (1714943378361434113, 1); +INSERT INTO `sys_user_role` VALUES (1714943388671033346, 1); +INSERT INTO `sys_user_role` VALUES (1714945928464711682, 1); +INSERT INTO `sys_user_role` VALUES (1714946100850606082, 1); +INSERT INTO `sys_user_role` VALUES (1714952355237347329, 1); +INSERT INTO `sys_user_role` VALUES (1714954192279584770, 1); +INSERT INTO `sys_user_role` VALUES (1714960721598758913, 1); +INSERT INTO `sys_user_role` VALUES (1714961357132283906, 1); +INSERT INTO `sys_user_role` VALUES (1714963426656403458, 1); +INSERT INTO `sys_user_role` VALUES (1714980339130318850, 1); +INSERT INTO `sys_user_role` VALUES (1714985002550444034, 1); +INSERT INTO `sys_user_role` VALUES (1714996959085084674, 1); +INSERT INTO `sys_user_role` VALUES (1715000784541990913, 1); +INSERT INTO `sys_user_role` VALUES (1715160830886297602, 1); +INSERT INTO `sys_user_role` VALUES (1715174792021426177, 1); +INSERT INTO `sys_user_role` VALUES (1715176760861278209, 1); +INSERT INTO `sys_user_role` VALUES (1715187418688405506, 1); +INSERT INTO `sys_user_role` VALUES (1715263570077564930, 1); +INSERT INTO `sys_user_role` VALUES (1715273299113820162, 1); +INSERT INTO `sys_user_role` VALUES (1715289765028577281, 1); +INSERT INTO `sys_user_role` VALUES (1715642509052624897, 1); +INSERT INTO `sys_user_role` VALUES (1715645217792868353, 1); +INSERT INTO `sys_user_role` VALUES (1715655140035543041, 1); +INSERT INTO `sys_user_role` VALUES (1715688813166346242, 1); +INSERT INTO `sys_user_role` VALUES (1715695623109623810, 1); +INSERT INTO `sys_user_role` VALUES (1716076523383177217, 1); +INSERT INTO `sys_user_role` VALUES (1716077329079615490, 1); +INSERT INTO `sys_user_role` VALUES (1716316658037178370, 1); +INSERT INTO `sys_user_role` VALUES (1716375479287824386, 1); +INSERT INTO `sys_user_role` VALUES (1716376929359380482, 1); +INSERT INTO `sys_user_role` VALUES (1716449431389487106, 1); +INSERT INTO `sys_user_role` VALUES (1716626232627707906, 1); +INSERT INTO `sys_user_role` VALUES (1716668774639484929, 1); +INSERT INTO `sys_user_role` VALUES (1716723582348050434, 1); +INSERT INTO `sys_user_role` VALUES (1717010625036828674, 1); +INSERT INTO `sys_user_role` VALUES (1717112818712723458, 1); +INSERT INTO `sys_user_role` VALUES (1717171039955599361, 1); +INSERT INTO `sys_user_role` VALUES (1717382776042569730, 1); +INSERT INTO `sys_user_role` VALUES (1717383874597896194, 1); +INSERT INTO `sys_user_role` VALUES (1717463477270102018, 1); +INSERT INTO `sys_user_role` VALUES (1717550755342467074, 1); +INSERT INTO `sys_user_role` VALUES (1718643906618605569, 1); +INSERT INTO `sys_user_role` VALUES (1719357065528623105, 1); +INSERT INTO `sys_user_role` VALUES (1719629669720145921, 1); +INSERT INTO `sys_user_role` VALUES (1719631746265530370, 1); +INSERT INTO `sys_user_role` VALUES (1719969371128086529, 1); +INSERT INTO `sys_user_role` VALUES (1719994192431955970, 1); +INSERT INTO `sys_user_role` VALUES (1720001597920264194, 1); +INSERT INTO `sys_user_role` VALUES (1720054174099718145, 1); +INSERT INTO `sys_user_role` VALUES (1720373256426635265, 1); +INSERT INTO `sys_user_role` VALUES (1720615324298264578, 1); +INSERT INTO `sys_user_role` VALUES (1720966085100191746, 1); +INSERT INTO `sys_user_role` VALUES (1721433118342397954, 1); +INSERT INTO `sys_user_role` VALUES (1721798759096270850, 1); +INSERT INTO `sys_user_role` VALUES (1721869407395332097, 1); +INSERT INTO `sys_user_role` VALUES (1721869952080232450, 1); +INSERT INTO `sys_user_role` VALUES (1722083875718737921, 1); +INSERT INTO `sys_user_role` VALUES (1722126825769185282, 1); +INSERT INTO `sys_user_role` VALUES (1722453238653169665, 1); +INSERT INTO `sys_user_role` VALUES (1722501722198552577, 1); +INSERT INTO `sys_user_role` VALUES (1722546398997819394, 1); +INSERT INTO `sys_user_role` VALUES (1722635856464097281, 1); +INSERT INTO `sys_user_role` VALUES (1722652602847768578, 1); +INSERT INTO `sys_user_role` VALUES (1722787874222682114, 1); +INSERT INTO `sys_user_role` VALUES (1722799180870889473, 1); +INSERT INTO `sys_user_role` VALUES (1722872660475817986, 1); +INSERT INTO `sys_user_role` VALUES (1722874592401600514, 1); +INSERT INTO `sys_user_role` VALUES (1722883137289367554, 1); +INSERT INTO `sys_user_role` VALUES (1722918534182645762, 1); +INSERT INTO `sys_user_role` VALUES (1723173295586848769, 1); +INSERT INTO `sys_user_role` VALUES (1723222687891107841, 1); +INSERT INTO `sys_user_role` VALUES (1723224404040921089, 1); +INSERT INTO `sys_user_role` VALUES (1723225015520112641, 1); +INSERT INTO `sys_user_role` VALUES (1723278284531478529, 1); +INSERT INTO `sys_user_role` VALUES (1723330835209564161, 1); +INSERT INTO `sys_user_role` VALUES (1723708198137147393, 1); +INSERT INTO `sys_user_role` VALUES (1723754683843260417, 1); +INSERT INTO `sys_user_role` VALUES (1723878185250369537, 1); +INSERT INTO `sys_user_role` VALUES (1723940614634254337, 1); +INSERT INTO `sys_user_role` VALUES (1723975861757325314, 1); +INSERT INTO `sys_user_role` VALUES (1724306907803725826, 1); +INSERT INTO `sys_user_role` VALUES (1724308252862492673, 1); +INSERT INTO `sys_user_role` VALUES (1724382895124295681, 1); +INSERT INTO `sys_user_role` VALUES (1724727778758406145, 1); +INSERT INTO `sys_user_role` VALUES (1724815478295425026, 1); +INSERT INTO `sys_user_role` VALUES (1725026071145107458, 1); +INSERT INTO `sys_user_role` VALUES (1725026978817658881, 1); +INSERT INTO `sys_user_role` VALUES (1725043562961457154, 1); +INSERT INTO `sys_user_role` VALUES (1725058936893362178, 1); +INSERT INTO `sys_user_role` VALUES (1725363117009162242, 1); +INSERT INTO `sys_user_role` VALUES (1725538633251049474, 1); +INSERT INTO `sys_user_role` VALUES (1725564937467875329, 1); +INSERT INTO `sys_user_role` VALUES (1725891713243021314, 1); +INSERT INTO `sys_user_role` VALUES (1725905000621932546, 1); +INSERT INTO `sys_user_role` VALUES (1726440708294049793, 1); +INSERT INTO `sys_user_role` VALUES (1726443526979584002, 1); +INSERT INTO `sys_user_role` VALUES (1726445663797116929, 1); +INSERT INTO `sys_user_role` VALUES (1726452867329687553, 1); +INSERT INTO `sys_user_role` VALUES (1726472827451998209, 1); +INSERT INTO `sys_user_role` VALUES (1726479651370696705, 1); +INSERT INTO `sys_user_role` VALUES (1726487492674195458, 1); +INSERT INTO `sys_user_role` VALUES (1726496513055784961, 1); +INSERT INTO `sys_user_role` VALUES (1726498781398302722, 1); +INSERT INTO `sys_user_role` VALUES (1726506873632587778, 1); +INSERT INTO `sys_user_role` VALUES (1726529248394739714, 1); +INSERT INTO `sys_user_role` VALUES (1726578079102664705, 1); +INSERT INTO `sys_user_role` VALUES (1726582181383634946, 1); +INSERT INTO `sys_user_role` VALUES (1726583555672506369, 1); +INSERT INTO `sys_user_role` VALUES (1726596448690372609, 1); +INSERT INTO `sys_user_role` VALUES (1726599361261207553, 1); +INSERT INTO `sys_user_role` VALUES (1726604511749079041, 1); +INSERT INTO `sys_user_role` VALUES (1726606973822304258, 1); +INSERT INTO `sys_user_role` VALUES (1726609379524083713, 1); +INSERT INTO `sys_user_role` VALUES (1726616151265640450, 1); +INSERT INTO `sys_user_role` VALUES (1726775811478126594, 1); +INSERT INTO `sys_user_role` VALUES (1726795490141667329, 1); +INSERT INTO `sys_user_role` VALUES (1726798403169681410, 1); +INSERT INTO `sys_user_role` VALUES (1726830794655399937, 1); +INSERT INTO `sys_user_role` VALUES (1726862038013313026, 1); +INSERT INTO `sys_user_role` VALUES (1726919220696186882, 1); +INSERT INTO `sys_user_role` VALUES (1727140184050630658, 1); +INSERT INTO `sys_user_role` VALUES (1727506163368722433, 1); +INSERT INTO `sys_user_role` VALUES (1727518983086931969, 1); +INSERT INTO `sys_user_role` VALUES (1727580969606840321, 1); +INSERT INTO `sys_user_role` VALUES (1727590505323429890, 1); +INSERT INTO `sys_user_role` VALUES (1727918393172164609, 1); +INSERT INTO `sys_user_role` VALUES (1728249002000121857, 1); +INSERT INTO `sys_user_role` VALUES (1728680561446486017, 1); +INSERT INTO `sys_user_role` VALUES (1728964404182577153, 1); +INSERT INTO `sys_user_role` VALUES (1729020459675611137, 1); +INSERT INTO `sys_user_role` VALUES (1729051002043691009, 1); +INSERT INTO `sys_user_role` VALUES (1729423744832172033, 1); +INSERT INTO `sys_user_role` VALUES (1729429590291050497, 1); +INSERT INTO `sys_user_role` VALUES (1729685493222375426, 1729685491108446210); +INSERT INTO `sys_user_role` VALUES (1730050324466036738, 1); +INSERT INTO `sys_user_role` VALUES (1730102403335254018, 1); +INSERT INTO `sys_user_role` VALUES (1730129923250122754, 1); +INSERT INTO `sys_user_role` VALUES (1730155108925763586, 1); +INSERT INTO `sys_user_role` VALUES (1730273428207366145, 1); +INSERT INTO `sys_user_role` VALUES (1730498722784669697, 1); +INSERT INTO `sys_user_role` VALUES (1730815105229713410, 1); +INSERT INTO `sys_user_role` VALUES (1730858886951923714, 1); +INSERT INTO `sys_user_role` VALUES (1731357405659824130, 1); +INSERT INTO `sys_user_role` VALUES (1731475532557090818, 1); +INSERT INTO `sys_user_role` VALUES (1731480953627901953, 1); +INSERT INTO `sys_user_role` VALUES (1731502381106495490, 1); +INSERT INTO `sys_user_role` VALUES (1731524458442162177, 1); +INSERT INTO `sys_user_role` VALUES (1731524630094053377, 1); +INSERT INTO `sys_user_role` VALUES (1731524650293821441, 1); +INSERT INTO `sys_user_role` VALUES (1731529253710233601, 1); +INSERT INTO `sys_user_role` VALUES (1731559936046432258, 1); +INSERT INTO `sys_user_role` VALUES (1731564032228884482, 1); +INSERT INTO `sys_user_role` VALUES (1731565926737281026, 1); +INSERT INTO `sys_user_role` VALUES (1731566918589513729, 1); +INSERT INTO `sys_user_role` VALUES (1731567740094283778, 1); +INSERT INTO `sys_user_role` VALUES (1731575439263563777, 1); +INSERT INTO `sys_user_role` VALUES (1731583864055824385, 1); +INSERT INTO `sys_user_role` VALUES (1731588155382464513, 1); +INSERT INTO `sys_user_role` VALUES (1731589827840212993, 1); +INSERT INTO `sys_user_role` VALUES (1731635461435719682, 1); +INSERT INTO `sys_user_role` VALUES (1731668049902731266, 1); +INSERT INTO `sys_user_role` VALUES (1731922694168412162, 1); +INSERT INTO `sys_user_role` VALUES (1731944975456305153, 1); +INSERT INTO `sys_user_role` VALUES (1731949019394506753, 1); +INSERT INTO `sys_user_role` VALUES (1731951425054343170, 1); +INSERT INTO `sys_user_role` VALUES (1732000242621513729, 1); +INSERT INTO `sys_user_role` VALUES (1732027163380056066, 1); +INSERT INTO `sys_user_role` VALUES (1732289382269353985, 1); +INSERT INTO `sys_user_role` VALUES (1732289439282528258, 1); +INSERT INTO `sys_user_role` VALUES (1732289699585228801, 1); +INSERT INTO `sys_user_role` VALUES (1732290827173527553, 1); +INSERT INTO `sys_user_role` VALUES (1732291549344595969, 1); +INSERT INTO `sys_user_role` VALUES (1732293265184030721, 1); +INSERT INTO `sys_user_role` VALUES (1732329664117506049, 1); +INSERT INTO `sys_user_role` VALUES (1732334104450990081, 1); +INSERT INTO `sys_user_role` VALUES (1732578671045672962, 1); +INSERT INTO `sys_user_role` VALUES (1732584047426174978, 1); +INSERT INTO `sys_user_role` VALUES (1732608690321129474, 1); +INSERT INTO `sys_user_role` VALUES (1732678147815014401, 1); +INSERT INTO `sys_user_role` VALUES (1732731410102910977, 1); +INSERT INTO `sys_user_role` VALUES (1733005266763939841, 1); +INSERT INTO `sys_user_role` VALUES (1733016149837774850, 1); +INSERT INTO `sys_user_role` VALUES (1733053523871432705, 1); +INSERT INTO `sys_user_role` VALUES (1733061400367497218, 1); +INSERT INTO `sys_user_role` VALUES (1733167090469732353, 1); +INSERT INTO `sys_user_role` VALUES (1733298702729641986, 1); +INSERT INTO `sys_user_role` VALUES (1733488544511983617, 1); +INSERT INTO `sys_user_role` VALUES (1733720554119659521, 1); +INSERT INTO `sys_user_role` VALUES (1733846657777827842, 1); +INSERT INTO `sys_user_role` VALUES (1733859832720031745, 1); +INSERT INTO `sys_user_role` VALUES (1734137817339559938, 1); +INSERT INTO `sys_user_role` VALUES (1734227535762849793, 1); +INSERT INTO `sys_user_role` VALUES (1734492373726560257, 1); +INSERT INTO `sys_user_role` VALUES (1734508040978726914, 1); +INSERT INTO `sys_user_role` VALUES (1734513545461661697, 1); +INSERT INTO `sys_user_role` VALUES (1734581580998451202, 1); +INSERT INTO `sys_user_role` VALUES (1734751884580298754, 1); +INSERT INTO `sys_user_role` VALUES (1734781716483612674, 1); +INSERT INTO `sys_user_role` VALUES (1734833221987278849, 1); +INSERT INTO `sys_user_role` VALUES (1734834063154946050, 1); +INSERT INTO `sys_user_role` VALUES (1734880697666576386, 1); +INSERT INTO `sys_user_role` VALUES (1734891995888427009, 1); +INSERT INTO `sys_user_role` VALUES (1735132534701367297, 1); +INSERT INTO `sys_user_role` VALUES (1735242647239991298, 1); +INSERT INTO `sys_user_role` VALUES (1735486862444273666, 1); +INSERT INTO `sys_user_role` VALUES (1735487912727355394, 1); +INSERT INTO `sys_user_role` VALUES (1735542352767426561, 1); +INSERT INTO `sys_user_role` VALUES (1735551915889598466, 1); +INSERT INTO `sys_user_role` VALUES (1735616653411557377, 1); +INSERT INTO `sys_user_role` VALUES (1735835864146714626, 1); +INSERT INTO `sys_user_role` VALUES (1735953007769100289, 1); +INSERT INTO `sys_user_role` VALUES (1735960189784891393, 1); +INSERT INTO `sys_user_role` VALUES (1736265950381547522, 1); +INSERT INTO `sys_user_role` VALUES (1736577606684844034, 1); +INSERT INTO `sys_user_role` VALUES (1736638822375563266, 1); +INSERT INTO `sys_user_role` VALUES (1736779069306511361, 1); +INSERT INTO `sys_user_role` VALUES (1737028378602053634, 1); +INSERT INTO `sys_user_role` VALUES (1737271234797314050, 1); +INSERT INTO `sys_user_role` VALUES (1737315322405920770, 1); +INSERT INTO `sys_user_role` VALUES (1737445221154234370, 1); +INSERT INTO `sys_user_role` VALUES (1737452907568635906, 1); +INSERT INTO `sys_user_role` VALUES (1737453186955419649, 1); +INSERT INTO `sys_user_role` VALUES (1737717777685880833, 1); +INSERT INTO `sys_user_role` VALUES (1737768515594166274, 1); +INSERT INTO `sys_user_role` VALUES (1738108912170246145, 1); +INSERT INTO `sys_user_role` VALUES (1738118086488825858, 1); +INSERT INTO `sys_user_role` VALUES (1738520430804279297, 1); +INSERT INTO `sys_user_role` VALUES (1738802060248817666, 1); +INSERT INTO `sys_user_role` VALUES (1738812447119712257, 1); +INSERT INTO `sys_user_role` VALUES (1738941480197234689, 1); +INSERT INTO `sys_user_role` VALUES (1738963430776840194, 1); +INSERT INTO `sys_user_role` VALUES (1739121784341995522, 1); +INSERT INTO `sys_user_role` VALUES (1739166931951886338, 1); +INSERT INTO `sys_user_role` VALUES (1739272055240073217, 1); +INSERT INTO `sys_user_role` VALUES (1739451838930427905, 1); +INSERT INTO `sys_user_role` VALUES (1739452037375533057, 1); +INSERT INTO `sys_user_role` VALUES (1739452376946384898, 1); +INSERT INTO `sys_user_role` VALUES (1739484503888961537, 1); +INSERT INTO `sys_user_role` VALUES (1739485282335006722, 1); +INSERT INTO `sys_user_role` VALUES (1739577551431999490, 1); +INSERT INTO `sys_user_role` VALUES (1739825609910591489, 1); +INSERT INTO `sys_user_role` VALUES (1739916453439152130, 1); +INSERT INTO `sys_user_role` VALUES (1740188388454629378, 1); +INSERT INTO `sys_user_role` VALUES (1741339991320580097, 1); +INSERT INTO `sys_user_role` VALUES (1741803737633542145, 1); +INSERT INTO `sys_user_role` VALUES (1741823858229923841, 1); +INSERT INTO `sys_user_role` VALUES (1741845883943227393, 1); +INSERT INTO `sys_user_role` VALUES (1742179775941201921, 1); +INSERT INTO `sys_user_role` VALUES (1742437553771458562, 1); +INSERT INTO `sys_user_role` VALUES (1742451201315254273, 1); +INSERT INTO `sys_user_role` VALUES (1742469913120419841, 1); +INSERT INTO `sys_user_role` VALUES (1742798283280568321, 1); +INSERT INTO `sys_user_role` VALUES (1742798987701342210, 1); +INSERT INTO `sys_user_role` VALUES (1742799476950126594, 1); +INSERT INTO `sys_user_role` VALUES (1742799839619010562, 1); +INSERT INTO `sys_user_role` VALUES (1742801019527057410, 1); +INSERT INTO `sys_user_role` VALUES (1742804073915699202, 1); +INSERT INTO `sys_user_role` VALUES (1742821280687149058, 1); +INSERT INTO `sys_user_role` VALUES (1742821467476283394, 1); +INSERT INTO `sys_user_role` VALUES (1742822775600009217, 1); +INSERT INTO `sys_user_role` VALUES (1742823890928357377, 1); +INSERT INTO `sys_user_role` VALUES (1742838225297821697, 1); +INSERT INTO `sys_user_role` VALUES (1742902317295423490, 1); +INSERT INTO `sys_user_role` VALUES (1742910854243373058, 1); +INSERT INTO `sys_user_role` VALUES (1742961994725150721, 1); +INSERT INTO `sys_user_role` VALUES (1742969861079388161, 1); +INSERT INTO `sys_user_role` VALUES (1743068363130228737, 1); +INSERT INTO `sys_user_role` VALUES (1743075924621479938, 1); +INSERT INTO `sys_user_role` VALUES (1743079200725225474, 1); +INSERT INTO `sys_user_role` VALUES (1743085878682144769, 1); +INSERT INTO `sys_user_role` VALUES (1743110774967586818, 1); +INSERT INTO `sys_user_role` VALUES (1743162481042870274, 1); +INSERT INTO `sys_user_role` VALUES (1743166491284033537, 1); +INSERT INTO `sys_user_role` VALUES (1743251016219447297, 1); +INSERT INTO `sys_user_role` VALUES (1743469820367142914, 1); +INSERT INTO `sys_user_role` VALUES (1743514389280522242, 1); +INSERT INTO `sys_user_role` VALUES (1743519646916083714, 1); +INSERT INTO `sys_user_role` VALUES (1743670356026654722, 1); + +-- ---------------------------- +-- Table structure for test_demo +-- ---------------------------- +DROP TABLE IF EXISTS `test_demo`; +CREATE TABLE `test_demo` ( + `id` bigint NOT NULL COMMENT '主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `dept_id` bigint NULL DEFAULT NULL COMMENT '部门id', + `user_id` bigint NULL DEFAULT NULL COMMENT '用户id', + `order_num` int NULL DEFAULT 0 COMMENT '排序号', + `test_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'key键', + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '值', + `version` int NULL DEFAULT 0 COMMENT '版本', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建人', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新人', + `del_flag` int NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '测试单表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of test_demo +-- ---------------------------- +INSERT INTO `test_demo` VALUES (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_demo` VALUES (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); + +-- ---------------------------- +-- Table structure for test_tree +-- ---------------------------- +DROP TABLE IF EXISTS `test_tree`; +CREATE TABLE `test_tree` ( + `id` bigint NOT NULL COMMENT '主键', + `tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号', + `parent_id` bigint NULL DEFAULT 0 COMMENT '父id', + `dept_id` bigint NULL DEFAULT NULL COMMENT '部门id', + `user_id` bigint NULL DEFAULT NULL COMMENT '用户id', + `tree_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '值', + `version` int NULL DEFAULT 0 COMMENT '版本', + `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `create_by` bigint NULL DEFAULT NULL COMMENT '创建人', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `update_by` bigint NULL DEFAULT NULL COMMENT '更新人', + `del_flag` int NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '测试树表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of test_tree +-- ---------------------------- +INSERT INTO `test_tree` VALUES (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (2, '000000', 1, 102, 3, '子节点1', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (3, '000000', 2, 102, 3, '子节点2', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (4, '000000', 0, 108, 4, '测试树1', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (5, '000000', 4, 108, 3, '子节点11', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (6, '000000', 4, 108, 3, '子节点22', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (7, '000000', 4, 108, 3, '子节点33', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (8, '000000', 5, 108, 3, '子节点44', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (9, '000000', 6, 108, 3, '子节点55', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (10, '000000', 7, 108, 3, '子节点66', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (11, '000000', 7, 108, 3, '子节点77', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (12, '000000', 10, 108, 3, '子节点88', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); +INSERT INTO `test_tree` VALUES (13, '000000', 10, 108, 3, '子节点99', 0, 103, '2023-05-14 15:20:01', 1, NULL, NULL, 0); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql new file mode 100644 index 00000000..f72dfffc --- /dev/null +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -0,0 +1,2814 @@ +CREATE TABLE sys_tenant +( + id bigint NOT NULL, + tenant_id nvarchar(20) NOT NULL, + contact_user_name nvarchar(20) NULL, + contact_phone nvarchar(20) NULL, + company_name nvarchar(50) NULL, + license_number nvarchar(30) NULL, + address nvarchar(200) NULL, + intro nvarchar(200) NULL, + domain nvarchar(200) NULL, + remark nvarchar(200) NULL, + package_id bigint NULL, + expire_time datetime2(7) NULL, + account_count int DEFAULT ((-1)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_tenant__B21E8F2427725F8A PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系人' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'contact_user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系电话' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'contact_phone' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'企业名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'company_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'统一社会信用代码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'license_number' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'address' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'企业简介' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'intro' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'域名' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'domain' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'package_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'过期时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'expire_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户数量(-1不限制)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'account_count' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant' +GO + +INSERT sys_tenant VALUES (1, N'000000', N'管理组', N'15888888888', N'XXX有限公司', NULL, NULL, N'多租户通用后台管理管理系统', NULL, NULL, NULL, NULL, -1, N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO + + +CREATE TABLE sys_tenant_package +( + package_id bigint NOT NULL, + package_name nvarchar(20) NOT NULL, + menu_ids nvarchar(20) NULL, + remark nvarchar(200) NULL, + menu_check_strictly tinyint DEFAULT ((1)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_tenant_package__B21E8F2427725F8A PRIMARY KEY CLUSTERED (package_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'package_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'套餐名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'package_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'关联菜单id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'menu_ids' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package' +GO + + +CREATE TABLE gen_table +( + table_id bigint NOT NULL, + table_name nvarchar(200) DEFAULT '' NULL, + table_comment nvarchar(500) DEFAULT '' NULL, + sub_table_name nvarchar(64) NULL, + sub_table_fk_name nvarchar(64) NULL, + class_name nvarchar(100) DEFAULT '' NULL, + tpl_category nvarchar(200) DEFAULT ('crud') NULL, + package_name nvarchar(100) NULL, + module_name nvarchar(30) NULL, + business_name nvarchar(30) NULL, + function_name nvarchar(50) NULL, + function_author nvarchar(50) NULL, + gen_type nchar(1) DEFAULT ('0') NULL, + gen_path nvarchar(200) DEFAULT ('/') NULL, + options nvarchar(1000) NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__gen_tabl__B21E8F2427725F8A PRIMARY KEY CLUSTERED (table_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'关联子表的表名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'子表关联的外键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_fk_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'实体类名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'class_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'使用的模板(crud单表操作 tree树表操作)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'tpl_category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成包路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'package_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成模块名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'module_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成业务名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'business_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能作者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_author' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成代码方式(0zip压缩包 1自定义路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成路径(不填默认项目路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'其它生成选项' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'options' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table' +GO + +CREATE TABLE gen_table_column +( + column_id bigint NOT NULL, + table_id bigint NULL, + column_name nvarchar(200) NULL, + column_comment nvarchar(500) NULL, + column_type nvarchar(100) NULL, + java_type nvarchar(500) NULL, + java_field nvarchar(200) NULL, + is_pk nchar(1) NULL, + is_increment nchar(1) NULL, + is_required nchar(1) NULL, + is_insert nchar(1) NULL, + is_edit nchar(1) NULL, + is_list nchar(1) NULL, + is_query nchar(1) NULL, + query_type nvarchar(200) DEFAULT ('EQ') NULL, + html_type nvarchar(200) NULL, + dict_type nvarchar(200) DEFAULT '' NULL, + sort int NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__gen_tabl__E301851F2E68B4E8 PRIMARY KEY CLUSTERED (column_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'归属表编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA字段名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_field' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否主键(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_pk' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否自增(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_increment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否必填(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_required' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为插入字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_insert' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否编辑字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_edit' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否列表字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_list' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否查询字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_query' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'查询方式(等于、不等于、大于、小于、范围)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'query_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'html_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表字段' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column' +GO + +CREATE TABLE sys_config +( + config_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT '000000' NULL, + config_name nvarchar(100) DEFAULT '' NULL, + config_key nvarchar(100) DEFAULT '' NULL, + config_value nvarchar(500) DEFAULT '' NULL, + config_type nchar(1) DEFAULT ('N') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_conf__4AD1BFF182643682 PRIMARY KEY CLUSTERED (config_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统内置(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数配置表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config' +GO + +INSERT sys_config VALUES (1, N'000000', N'主框架页-默认皮肤样式名称', N'sys.index.skinName', N'skin-blue', N'Y', 103, 1, getdate(), NULL, NULL, N'蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow') +GO +INSERT sys_config VALUES (2, N'000000', N'用户管理-账号初始密码', N'sys.user.initPassword', N'123456', N'Y', 103, 1, getdate(), NULL, NULL, N'初始化密码 123456') +GO +INSERT sys_config VALUES (3, N'000000', N'主框架页-侧边栏主题', N'sys.index.sideTheme', N'theme-dark', N'Y', 103, 1, getdate(), NULL, NULL, N'深色主题theme-dark,浅色主题theme-light') +GO +INSERT sys_config VALUES (5, N'000000', N'账号自助-是否开启用户注册功能', N'sys.account.registerUser', N'false', N'Y', 103, 1, getdate(), NULL, NULL, N'是否开启注册用户功能(true开启,false关闭)') +GO +INSERT sys_config VALUES (11, N'000000', N'OSS预览列表资源开关', N'sys.oss.previewListResource', N'true', N'Y', 103, 1, getdate(), NULL, NULL, N'true:开启, false:关闭'); +GO + +CREATE TABLE sys_dept +( + dept_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + parent_id bigint DEFAULT ((0)) NULL, + ancestors nvarchar(500)DEFAULT '' NULL, + dept_name nvarchar(30) DEFAULT '' NULL, + order_num int DEFAULT ((0)) NULL, + leader nvarchar(20) NULL, + phone nvarchar(11) NULL, + email nvarchar(50) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_dept__DCA659747DE13804 PRIMARY KEY CLUSTERED (dept_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'祖级列表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'ancestors' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'负责人' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'leader' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系电话' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'phone' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept' +GO + +INSERT sys_dept VALUES (100, N'000000', 0, N'0', N'XXX科技', 0, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (101, N'000000', 100, N'0,100', N'深圳总公司', 1, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (102, N'000000', 100, N'0,100', N'长沙分公司', 2, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (103, N'000000', 101, N'0,100,101', N'研发部门', 1, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (104, N'000000', 101, N'0,100,101', N'市场部门', 2, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (105, N'000000', 101, N'0,100,101', N'测试部门', 3, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (106, N'000000', 101, N'0,100,101', N'财务部门', 4, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (107, N'000000', 101, N'0,100,101', N'运维部门', 5, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (108, N'000000', 102, N'0,100,102', N'市场部门', 1, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (109, N'000000', 102, N'0,100,102', N'财务部门', 2, N'疯狂的狮子Li', N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO + +CREATE TABLE sys_dict_data +( + dict_code bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dict_sort int DEFAULT ((0)) NULL, + dict_label nvarchar(100) DEFAULT '' NULL, + dict_value nvarchar(100) DEFAULT '' NULL, + dict_type nvarchar(100) DEFAULT '' NULL, + css_class nvarchar(100) NULL, + list_class nvarchar(100) NULL, + is_default nchar(1) DEFAULT ('N') NULL, + status nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_dict__19CBC34B661AF3B3 PRIMARY KEY CLUSTERED (dict_code) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典标签' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_label' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'样式属性(其他样式扩展)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'css_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表格回显样式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'list_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否默认(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'is_default' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典数据表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data' +GO + +INSERT sys_dict_data VALUES (1, N'000000', 1, N'男', N'0', N'sys_user_sex', N'', N'', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'性别男') +GO +INSERT sys_dict_data VALUES (2, N'000000', 2, N'女', N'1', N'sys_user_sex', N'', N'', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'性别女') +GO +INSERT sys_dict_data VALUES (3, N'000000', 3, N'未知', N'2', N'sys_user_sex', N'', N'', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'性别未知') +GO +INSERT sys_dict_data VALUES (4, N'000000', 1, N'显示', N'0', N'sys_show_hide', N'', N'primary', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'显示菜单') +GO +INSERT sys_dict_data VALUES (5, N'000000', 2, N'隐藏', N'1', N'sys_show_hide', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'隐藏菜单') +GO +INSERT sys_dict_data VALUES (6, N'000000', 1, N'正常', N'0', N'sys_normal_disable', N'', N'primary', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (7, N'000000', 2, N'停用', N'1', N'sys_normal_disable', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO +INSERT sys_dict_data VALUES (8, N'000000', 1, N'正常', N'0', N'sys_job_status', N'', N'primary', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (9, N'000000', 2, N'暂停', N'1', N'sys_job_status', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO +INSERT sys_dict_data VALUES (10, N'000000', 1, N'默认', N'DEFAULT', N'sys_job_group', N'', N'', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'默认分组') +GO +INSERT sys_dict_data VALUES (11, N'000000', 2, N'系统', N'SYSTEM', N'sys_job_group', N'', N'', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'系统分组') +GO +INSERT sys_dict_data VALUES (12, N'000000', 1, N'是', N'Y', N'sys_yes_no', N'', N'primary', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'系统默认是') +GO +INSERT sys_dict_data VALUES (13, N'000000', 2, N'否', N'N', N'sys_yes_no', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'系统默认否') +GO +INSERT sys_dict_data VALUES (14, N'000000', 1, N'通知', N'1', N'sys_notice_type', N'', N'warning', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'通知') +GO +INSERT sys_dict_data VALUES (15, N'000000', 2, N'公告', N'2', N'sys_notice_type', N'', N'success', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'公告') +GO +INSERT sys_dict_data VALUES (16, N'000000', 1, N'正常', N'0', N'sys_notice_status', N'', N'primary', N'Y', N'0', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (17, N'000000', 2, N'关闭', N'1', N'sys_notice_status', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'关闭状态') +GO +INSERT sys_dict_data VALUES (29, N'000000', 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'其他操作'); +GO +INSERT sys_dict_data VALUES (18, N'000000', 1, N'新增', N'1', N'sys_oper_type', N'', N'info', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'新增操作') +GO +INSERT sys_dict_data VALUES (19, N'000000', 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'修改操作') +GO +INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'删除操作') +GO +INSERT sys_dict_data VALUES (21, N'000000', 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'授权操作') +GO +INSERT sys_dict_data VALUES (22, N'000000', 5, N'导出', N'5', N'sys_oper_type', N'', N'warning', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'导出操作') +GO +INSERT sys_dict_data VALUES (23, N'000000', 6, N'导入', N'6', N'sys_oper_type', N'', N'warning', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'导入操作') +GO +INSERT sys_dict_data VALUES (24, N'000000', 7, N'强退', N'7', N'sys_oper_type', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'强退操作') +GO +INSERT sys_dict_data VALUES (25, N'000000', 8, N'生成代码', N'8', N'sys_oper_type', N'', N'warning', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'生成操作') +GO +INSERT sys_dict_data VALUES (26, N'000000', 9, N'清空数据', N'9', N'sys_oper_type', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'清空操作') +GO +INSERT sys_dict_data VALUES (27, N'000000', 1, N'成功', N'0', N'sys_common_status', N'', N'primary', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (28, N'000000', 2, N'失败', N'1', N'sys_common_status', N'', N'danger', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO + +CREATE TABLE sys_dict_type +( + dict_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dict_name nvarchar(100) DEFAULT '' NULL, + dict_type nvarchar(100) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_dict__3BD4186C409C5391 PRIMARY KEY CLUSTERED (dict_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX sys_dict_type_index1 ON sys_dict_type (tenant_id, dict_type) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type' +GO + +INSERT sys_dict_type VALUES (1, N'000000', N'用户性别', N'sys_user_sex', N'0', 103, 1, getdate(), NULL, NULL, N'用户性别列表') +GO +INSERT sys_dict_type VALUES (2, N'000000', N'菜单状态', N'sys_show_hide', N'0', 103, 1, getdate(), NULL, NULL, N'菜单状态列表') +GO +INSERT sys_dict_type VALUES (3, N'000000', N'系统开关', N'sys_normal_disable', N'0', 103, 1, getdate(), NULL, NULL, N'系统开关列表') +GO +INSERT sys_dict_type VALUES (4, N'000000', N'任务状态', N'sys_job_status', N'0', 103, 1, getdate(), NULL, NULL, N'任务状态列表') +GO +INSERT sys_dict_type VALUES (5, N'000000', N'任务分组', N'sys_job_group', N'0', 103, 1, getdate(), NULL, NULL, N'任务分组列表') +GO +INSERT sys_dict_type VALUES (6, N'000000', N'系统是否', N'sys_yes_no', N'0', 103, 1, getdate(), NULL, NULL, N'系统是否列表') +GO +INSERT sys_dict_type VALUES (7, N'000000', N'通知类型', N'sys_notice_type', N'0', 103, 1, getdate(), NULL, NULL, N'通知类型列表') +GO +INSERT sys_dict_type VALUES (8, N'000000', N'通知状态', N'sys_notice_status', N'0', 103, 1, getdate(), NULL, NULL, N'通知状态列表') +GO +INSERT sys_dict_type VALUES (9, N'000000', N'操作类型', N'sys_oper_type', N'0', 103, 1, getdate(), NULL, NULL, N'操作类型列表') +GO +INSERT sys_dict_type VALUES (10, N'000000', N'系统状态', N'sys_common_status', N'0', 103, 1, getdate(), NULL, NULL, N'登录状态列表') +GO + +CREATE TABLE sys_logininfor +( + info_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + user_name nvarchar(50) DEFAULT '' NULL, + ipaddr nvarchar(128) DEFAULT '' NULL, + login_location nvarchar(255) DEFAULT '' NULL, + browser nvarchar(50) DEFAULT '' NULL, + os nvarchar(50) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + msg nvarchar(255) DEFAULT '' NULL, + login_time datetime2(7) NULL, + CONSTRAINT PK__sys_logi__3D8A9C1A1854AE10 PRIMARY KEY CLUSTERED (info_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX idx_sys_logininfor_s ON sys_logininfor (status) +GO +CREATE NONCLUSTERED INDEX idx_sys_logininfor_lt ON sys_logininfor (login_time) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'info_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录IP地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'ipaddr' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'浏览器类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'browser' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作系统' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'os' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录状态(0成功 1失败)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'提示消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统访问记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor' +GO + +CREATE TABLE sys_menu +( + menu_id bigint NOT NULL, + menu_name nvarchar(50) NOT NULL, + parent_id bigint DEFAULT ((0)) NULL, + order_num int DEFAULT ((0)) NULL, + path nvarchar(200) DEFAULT '' NULL, + component nvarchar(255) NULL, + query_param nvarchar(255) NULL, + is_frame int DEFAULT ((1)) NULL, + is_cache int DEFAULT ((0)) NULL, + menu_type nchar(1) DEFAULT '' NULL, + visible nchar(1) DEFAULT ((0)) NULL, + status nchar(1) DEFAULT ((0)) NULL, + perms nvarchar(100) NULL, + icon nvarchar(100) DEFAULT ('#') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) DEFAULT '' NULL, + CONSTRAINT PK__sys_menu__4CA0FADCF8545C58 PRIMARY KEY CLUSTERED (menu_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'组件路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'component' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'query_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为外链(0是 1否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_frame' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否缓存(0缓存 1不缓存)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_cache' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单类型(M目录 C菜单 F按钮)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示状态(0显示 1隐藏)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'visible' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'权限标识' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'perms' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单图标' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'icon' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单权限表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu' +GO + +INSERT sys_menu VALUES (1, N'系统管理', 0, 1, N'system', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'system', 103, 1, getdate(), NULL, NULL, N'系统管理目录') +GO +INSERT sys_menu VALUES (6, N'租户管理', 0, 2, N'tenant', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'chart', 103, 1, getdate(), NULL, NULL, N'租户管理目录') +GO +INSERT sys_menu VALUES (2, N'系统监控', 0, 3, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, getdate(), NULL, NULL, N'系统监控目录') +GO +INSERT sys_menu VALUES (3, N'系统工具', 0, 4, N'tool', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'tool', 103, 1, getdate(), NULL, NULL, N'系统工具目录') +GO +INSERT sys_menu VALUES (4, N'PLUS官网', 0, 5, N'https://gitee.com/dromara/RuoYi-Vue-Plus', null, N'', 0, 0, N'M', N'0', N'0', N'', N'guide', 103, 1, getdate(), null, null, N'RuoYi-Vue-Plus官网地址'); +GO +INSERT sys_menu VALUES (100, N'用户管理', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', 103, 1, getdate(), NULL, NULL, N'用户管理菜单') +GO +INSERT sys_menu VALUES (101, N'角色管理', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', 103, 1, getdate(), NULL, NULL, N'角色管理菜单') +GO +INSERT sys_menu VALUES (102, N'菜单管理', 1, 3, N'menu', N'system/menu/index', N'', 1, 0, N'C', N'0', N'0', N'system:menu:list', N'tree-table', 103, 1, getdate(), NULL, NULL, N'菜单管理菜单') +GO +INSERT sys_menu VALUES (103, N'部门管理', 1, 4, N'dept', N'system/dept/index', N'', 1, 0, N'C', N'0', N'0', N'system:dept:list', N'tree', 103, 1, getdate(), NULL, NULL, N'部门管理菜单') +GO +INSERT sys_menu VALUES (104, N'岗位管理', 1, 5, N'post', N'system/post/index', N'', 1, 0, N'C', N'0', N'0', N'system:post:list', N'post', 103, 1, getdate(), NULL, NULL, N'岗位管理菜单') +GO +INSERT sys_menu VALUES (105, N'字典管理', 1, 6, N'dict', N'system/dict/index', N'', 1, 0, N'C', N'0', N'0', N'system:dict:list', N'dict', 103, 1, getdate(), NULL, NULL, N'字典管理菜单') +GO +INSERT sys_menu VALUES (106, N'参数设置', 1, 7, N'config', N'system/config/index', N'', 1, 0, N'C', N'0', N'0', N'system:config:list', N'edit', 103, 1, getdate(), NULL, NULL, N'参数设置菜单') +GO +INSERT sys_menu VALUES (107, N'通知公告', 1, 8, N'notice', N'system/notice/index', N'', 1, 0, N'C', N'0', N'0', N'system:notice:list', N'message', 103, 1, getdate(), NULL, NULL, N'通知公告菜单') +GO +INSERT sys_menu VALUES (108, N'日志管理', 1, 9, N'log', N'', N'', 1, 0, N'M', N'0', N'0', N'', N'log', 103, 1, getdate(), NULL, NULL, N'日志管理菜单') +GO +INSERT sys_menu VALUES (109, N'在线用户', 2, 1, N'online', N'monitor/online/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:online:list', N'online', 103, 1, getdate(), NULL, NULL, N'在线用户菜单') +GO +INSERT sys_menu VALUES (113, N'缓存监控', 2, 5, N'cache', N'monitor/cache/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis', 103, 1, getdate(), NULL, NULL, N'缓存监控菜单') +GO +INSERT sys_menu VALUES (114, N'表单构建', 3, 1, N'build', N'tool/build/index', N'', 1, 0, N'C', N'0', N'0', N'tool:build:list', N'build', 103, 1, getdate(), NULL, NULL, N'表单构建菜单') +GO +INSERT sys_menu VALUES (115, N'代码生成', 3, 2, N'gen', N'tool/gen/index', N'', 1, 0, N'C', N'0', N'0', N'tool:gen:list', N'code', 103, 1, getdate(), NULL, NULL, N'代码生成菜单') +GO +INSERT sys_menu VALUES (121, N'租户管理', 6, 1, N'tenant', N'system/tenant/index', N'', 1, 0, N'C', N'0', N'0', N'system:tenant:list', N'code', 103, 1, getdate(), NULL, NULL, N'租户管理菜单') +GO +INSERT sys_menu VALUES (122, N'租户套餐管理', 6, 2, N'tenantPackage', N'system/tenantPackage/index', N'', 1, 0, N'C', N'0', N'0', N'system:tenantPackage:list', N'code', 103, 1, getdate(), NULL, NULL, N'租户套餐管理菜单') +GO +INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', 103, 1, getdate(), NULL, NULL, N'Admin监控菜单'); +GO +INSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', 103, 1, getdate(), NULL, NULL, N'文件管理菜单'); +GO +INSERT sys_menu VALUES (120, N'任务调度中心', 2, 5, N'XxlJob', N'monitor/xxljob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:xxljob:list', N'job', 103, 1, getdate(), NULL, NULL, N'Xxl-Job控制台菜单'); +GO +INSERT sys_menu VALUES (500, N'操作日志', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', 103, 1, getdate(), NULL, NULL, N'操作日志菜单') +GO +INSERT sys_menu VALUES (501, N'登录日志', 108, 2, N'logininfor', N'monitor/logininfor/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:logininfor:list', N'logininfor', 103, 1, getdate(), NULL, NULL, N'登录日志菜单') +GO +INSERT sys_menu VALUES (1001, N'用户查询', 100, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1002, N'用户新增', 100, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1003, N'用户修改', 100, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1004, N'用户删除', 100, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1005, N'用户导出', 100, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1006, N'用户导入', 100, 6, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:import', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1007, N'重置密码', 100, 7, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:resetPwd', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1008, N'角色查询', 101, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1009, N'角色新增', 101, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1010, N'角色修改', 101, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1011, N'角色删除', 101, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1012, N'角色导出', 101, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1013, N'菜单查询', 102, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1014, N'菜单新增', 102, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1015, N'菜单修改', 102, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1016, N'菜单删除', 102, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1017, N'部门查询', 103, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1018, N'部门新增', 103, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1019, N'部门修改', 103, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1020, N'部门删除', 103, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1021, N'岗位查询', 104, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1022, N'岗位新增', 104, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1023, N'岗位修改', 104, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1024, N'岗位删除', 104, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1025, N'岗位导出', 104, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1026, N'字典查询', 105, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1027, N'字典新增', 105, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1028, N'字典修改', 105, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1029, N'字典删除', 105, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1030, N'字典导出', 105, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1031, N'参数查询', 106, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1032, N'参数新增', 106, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1033, N'参数修改', 106, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1034, N'参数删除', 106, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1035, N'参数导出', 106, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1036, N'公告查询', 107, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1037, N'公告新增', 107, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1038, N'公告修改', 107, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1039, N'公告删除', 107, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1040, N'操作查询', 500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1041, N'操作删除', 500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1042, N'日志导出', 500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1043, N'登录查询', 501, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1047, N'批量强退', 109, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:batchLogout', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1048, N'单条强退', 109, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:forceLogout', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1055, N'生成查询', 115, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1056, N'生成修改', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1057, N'生成删除', 115, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1058, N'导入代码', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:import', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1059, N'预览代码', 115, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:preview', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1060, N'生成代码', 115, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:code', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +-- oss相关按钮 +INSERT sys_menu VALUES (1600, N'文件查询', 118, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1601, N'文件上传', 118, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:upload', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1602, N'文件下载', 118, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:download', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1603, N'文件删除', 118, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1604, N'配置添加', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1605, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 租户管理相关按钮 +INSERT sys_menu VALUES (1606, N'租户查询', 121, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1607, N'租户新增', 121, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1608, N'租户修改', 121, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1609, N'租户删除', 121, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1610, N'租户导出', 121, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 租户套餐管理相关按钮 +INSERT sys_menu VALUES (1611, N'租户套餐查询', 122, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1612, N'租户套餐新增', 122, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1613, N'租户套餐修改', 122, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1614, N'租户套餐删除', 122, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1615, N'租户套餐导出', 122, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + +CREATE TABLE sys_notice +( + notice_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + notice_title nvarchar(50) NOT NULL, + notice_type nchar(1) NOT NULL, + notice_content nvarchar(max) NULL, + status nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(255) NULL, + CONSTRAINT PK__sys_noti__3E82A5DB0EC94801 PRIMARY KEY CLUSTERED (notice_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +TEXTIMAGE_ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告类型(1通知 2公告)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告内容' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_content' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告状态(0正常 1关闭)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'通知公告表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice' +GO + +INSERT sys_notice VALUES (1, N'000000', N'温馨提醒:2018-07-01 若依新版本发布啦', N'2', N'新版本内容', N'0', 103, 1, getdate(), NULL, NULL, N'管理员') +GO +INSERT sys_notice VALUES (2, N'000000', N'维护通知:2018-07-01 若依系统凌晨维护', N'1', N'维护内容', N'0', 103, 1, getdate(), NULL, NULL, N'管理员') +GO + +CREATE TABLE sys_oper_log +( + oper_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + title nvarchar(50) DEFAULT '' NULL, + business_type int DEFAULT ((0)) NULL, + method nvarchar(100) DEFAULT '' NULL, + request_method nvarchar(10) DEFAULT '' NULL, + operator_type int DEFAULT ((0)) NULL, + oper_name nvarchar(50) DEFAULT '' NULL, + dept_name nvarchar(50) DEFAULT '' NULL, + oper_url nvarchar(255) DEFAULT '' NULL, + oper_ip nvarchar(128) DEFAULT '' NULL, + oper_location nvarchar(255) DEFAULT '' NULL, + oper_param nvarchar(2000) DEFAULT '' NULL, + json_result nvarchar(2000) DEFAULT '' NULL, + status int DEFAULT ((0)) NULL, + error_msg nvarchar(2000) DEFAULT '' NULL, + oper_time datetime2(7) NULL, + cost_time bigint DEFAULT ((0)) NULL, + CONSTRAINT PK__sys_oper__34723BF9BD954573 PRIMARY KEY CLUSTERED (oper_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX idx_sys_oper_log_bt ON sys_oper_log (business_type) +GO +CREATE NONCLUSTERED INDEX idx_sys_oper_log_s ON sys_oper_log (status) +GO +CREATE NONCLUSTERED INDEX idx_sys_oper_log_ot ON sys_oper_log (oper_time) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'日志主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'模块标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'业务类型(0其它 1新增 2修改 3删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'business_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'方法名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求方式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'request_method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作类别(0其它 1后台用户 2手机端用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'operator_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作人员' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求URL' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_url' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'主机地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'返回参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'json_result' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作状态(0正常 1异常)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'错误消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'error_msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消耗时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'cost_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作日志记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log' +GO + +CREATE TABLE sys_post +( + post_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + post_code nvarchar(64) NOT NULL, + post_name nvarchar(50) NOT NULL, + post_sort int NOT NULL, + status nchar(1) NOT NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_post__3ED7876668E2D081 PRIMARY KEY CLUSTERED (post_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post' +GO + +INSERT sys_post VALUES (1, N'000000', N'ceo', N'董事长', 1, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (2, N'000000', N'se', N'项目经理', 2, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (3, N'000000', N'hr', N'人力资源', 3, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (4, N'000000', N'user', N'普通员工', 4, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO + +CREATE TABLE sys_role +( + role_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + role_name nvarchar(30) NOT NULL, + role_key nvarchar(100) NOT NULL, + role_sort int NOT NULL, + data_scope nchar(1) DEFAULT ('1') NULL, + menu_check_strictly tinyint DEFAULT ((1)) NULL, + dept_check_strictly tinyint DEFAULT ((1)) NULL, + status nchar(1) NOT NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_role__760965CCF9383145 PRIMARY KEY CLUSTERED (role_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色权限字符串' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'data_scope' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'menu_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'dept_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role' +GO + +INSERT sys_role VALUES (1, N'000000', N'超级管理员', N'superadmin', 1, N'1', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'超级管理员') +GO +INSERT sys_role VALUES (2, N'000000', N'普通角色', N'common', 2, N'2', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'普通角色') +GO + +CREATE TABLE sys_role_dept +( + role_id bigint NOT NULL, + dept_id bigint NOT NULL, + CONSTRAINT PK__sys_role__2BC3005BABBCA08A PRIMARY KEY CLUSTERED (role_id, dept_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和部门关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept' +GO + +INSERT sys_role_dept VALUES (2, 100) +GO +INSERT sys_role_dept VALUES (2, 101) +GO +INSERT sys_role_dept VALUES (2, 105) +GO + +CREATE TABLE sys_role_menu +( + role_id bigint NOT NULL, + menu_id bigint NOT NULL, + CONSTRAINT PK__sys_role__A2C36A6187BA4B17 PRIMARY KEY CLUSTERED (role_id, menu_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和菜单关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu' +GO + +INSERT sys_role_menu VALUES (2, 1) +GO +INSERT sys_role_menu VALUES (2, 2) +GO +INSERT sys_role_menu VALUES (2, 3) +GO +INSERT sys_role_menu VALUES (2, 100) +GO +INSERT sys_role_menu VALUES (2, 101) +GO +INSERT sys_role_menu VALUES (2, 102) +GO +INSERT sys_role_menu VALUES (2, 103) +GO +INSERT sys_role_menu VALUES (2, 104) +GO +INSERT sys_role_menu VALUES (2, 105) +GO +INSERT sys_role_menu VALUES (2, 106) +GO +INSERT sys_role_menu VALUES (2, 107) +GO +INSERT sys_role_menu VALUES (2, 108) +GO +INSERT sys_role_menu VALUES (2, 109) +GO +INSERT sys_role_menu VALUES (2, 110) +GO +INSERT sys_role_menu VALUES (2, 111) +GO +INSERT sys_role_menu VALUES (2, 112) +GO +INSERT sys_role_menu VALUES (2, 113) +GO +INSERT sys_role_menu VALUES (2, 114) +GO +INSERT sys_role_menu VALUES (2, 115) +GO +INSERT sys_role_menu VALUES (2, 116) +GO +INSERT sys_role_menu VALUES (2, 500) +GO +INSERT sys_role_menu VALUES (2, 501) +GO +INSERT sys_role_menu VALUES (2, 1001) +GO +INSERT sys_role_menu VALUES (2, 1002) +GO +INSERT sys_role_menu VALUES (2, 1003) +GO +INSERT sys_role_menu VALUES (2, 1004) +GO +INSERT sys_role_menu VALUES (2, 1005) +GO +INSERT sys_role_menu VALUES (2, 1006) +GO +INSERT sys_role_menu VALUES (2, 1007) +GO +INSERT sys_role_menu VALUES (2, 1008) +GO +INSERT sys_role_menu VALUES (2, 1009) +GO +INSERT sys_role_menu VALUES (2, 1010) +GO +INSERT sys_role_menu VALUES (2, 1011) +GO +INSERT sys_role_menu VALUES (2, 1012) +GO +INSERT sys_role_menu VALUES (2, 1013) +GO +INSERT sys_role_menu VALUES (2, 1014) +GO +INSERT sys_role_menu VALUES (2, 1015) +GO +INSERT sys_role_menu VALUES (2, 1016) +GO +INSERT sys_role_menu VALUES (2, 1017) +GO +INSERT sys_role_menu VALUES (2, 1018) +GO +INSERT sys_role_menu VALUES (2, 1019) +GO +INSERT sys_role_menu VALUES (2, 1020) +GO +INSERT sys_role_menu VALUES (2, 1021) +GO +INSERT sys_role_menu VALUES (2, 1022) +GO +INSERT sys_role_menu VALUES (2, 1023) +GO +INSERT sys_role_menu VALUES (2, 1024) +GO +INSERT sys_role_menu VALUES (2, 1025) +GO +INSERT sys_role_menu VALUES (2, 1026) +GO +INSERT sys_role_menu VALUES (2, 1027) +GO +INSERT sys_role_menu VALUES (2, 1028) +GO +INSERT sys_role_menu VALUES (2, 1029) +GO +INSERT sys_role_menu VALUES (2, 1030) +GO +INSERT sys_role_menu VALUES (2, 1031) +GO +INSERT sys_role_menu VALUES (2, 1032) +GO +INSERT sys_role_menu VALUES (2, 1033) +GO +INSERT sys_role_menu VALUES (2, 1034) +GO +INSERT sys_role_menu VALUES (2, 1035) +GO +INSERT sys_role_menu VALUES (2, 1036) +GO +INSERT sys_role_menu VALUES (2, 1037) +GO +INSERT sys_role_menu VALUES (2, 1038) +GO +INSERT sys_role_menu VALUES (2, 1039) +GO +INSERT sys_role_menu VALUES (2, 1040) +GO +INSERT sys_role_menu VALUES (2, 1041) +GO +INSERT sys_role_menu VALUES (2, 1042) +GO +INSERT sys_role_menu VALUES (2, 1043) +GO +INSERT sys_role_menu VALUES (2, 1044) +GO +INSERT sys_role_menu VALUES (2, 1045) +GO +INSERT sys_role_menu VALUES (2, 1046) +GO +INSERT sys_role_menu VALUES (2, 1047) +GO +INSERT sys_role_menu VALUES (2, 1048) +GO +INSERT sys_role_menu VALUES (2, 1049) +GO +INSERT sys_role_menu VALUES (2, 1050) +GO +INSERT sys_role_menu VALUES (2, 1051) +GO +INSERT sys_role_menu VALUES (2, 1052) +GO +INSERT sys_role_menu VALUES (2, 1053) +GO +INSERT sys_role_menu VALUES (2, 1054) +GO +INSERT sys_role_menu VALUES (2, 1055) +GO +INSERT sys_role_menu VALUES (2, 1056) +GO +INSERT sys_role_menu VALUES (2, 1057) +GO +INSERT sys_role_menu VALUES (2, 1058) +GO +INSERT sys_role_menu VALUES (2, 1059) +GO +INSERT sys_role_menu VALUES (2, 1060) +GO + +CREATE TABLE sys_user +( + user_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dept_id bigint NULL, + user_name nvarchar(30) NOT NULL, + nick_name nvarchar(30) NOT NULL, + user_type nvarchar(10) DEFAULT ('sys_user') NULL, + email nvarchar(50) DEFAULT '' NULL, + phonenumber nvarchar(11) DEFAULT '' NULL, + sex nchar(1) DEFAULT ('0') NULL, + avatar bigint NULL, + password nvarchar(100) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + login_ip nvarchar(128) DEFAULT '' NULL, + login_date datetime2(7) NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_user__B9BE370F79170B6A PRIMARY KEY CLUSTERED (user_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户昵称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'nick_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户类型(sys_user系统用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'手机号码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'phonenumber' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户性别(0男 1女 2未知)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'sex' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'头像地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'avatar' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'密码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'password' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'帐号状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录IP' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_date' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user' +GO + +INSERT sys_user VALUES (1, 103, N'000000', N'admin', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', NULL, N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), NULL, NULL, N'管理员') +GO +INSERT sys_user VALUES (2, 105, N'000000', N'lionli', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@qq.com', N'15666666666', N'1', NULL, N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), NULL, NULL, N'测试员') +GO + +CREATE TABLE sys_user_post +( + user_id bigint NOT NULL, + post_id bigint NOT NULL, + CONSTRAINT PK__sys_user__CA534F799C04589B PRIMARY KEY CLUSTERED (user_id, post_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户与岗位关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post' +GO + +INSERT sys_user_post VALUES (1, 1) +GO +INSERT sys_user_post VALUES (2, 2) +GO + +CREATE TABLE sys_user_role +( + user_id bigint NOT NULL, + role_id bigint NOT NULL, + CONSTRAINT PK__sys_user__6EDEA153FB34D8F0 PRIMARY KEY CLUSTERED (user_id, role_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户和角色关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role' +GO + +INSERT sys_user_role VALUES (1, 1) +GO +INSERT sys_user_role VALUES (2, 2) +GO + +CREATE TABLE sys_oss +( + oss_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + file_name nvarchar(255) DEFAULT '' NOT NULL, + original_name nvarchar(255) DEFAULT '' NOT NULL, + file_suffix nvarchar(10) DEFAULT '' NOT NULL, + url nvarchar(500) NOT NULL, + create_dept bigint NULL, + create_time datetime2(7) NULL, + create_by bigint NULL, + update_time datetime2(7) NULL, + update_by bigint NULL, + service nvarchar(20) DEFAULT ('minio') NOT NULL, + CONSTRAINT PK__sys_oss__91241EA442389F0D PRIMARY KEY CLUSTERED (oss_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'oss_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'原名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'original_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件后缀名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_suffix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'URL地址', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'url' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'上传人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'服务商', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'service' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'OSS对象存储表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss' +GO + +CREATE TABLE sys_oss_config +( + oss_config_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + config_key nvarchar(20) DEFAULT '' NOT NULL, + access_key nvarchar(255) DEFAULT '' NULL, + secret_key nvarchar(255) DEFAULT '' NULL, + bucket_name nvarchar(255) DEFAULT '' NULL, + prefix nvarchar(255) DEFAULT '' NULL, + endpoint nvarchar(255) DEFAULT '' NULL, + domain nvarchar(255) DEFAULT '' NULL, + is_https nchar(1) DEFAULT ('N') NULL, + region nvarchar(255) DEFAULT '' NULL, + access_policy nchar(1) DEFAULT ('1') NOT NULL, + status nchar(1) DEFAULT ('1') NULL, + ext1 nvarchar(255) DEFAULT '' NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_oss___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (oss_config_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主建', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'oss_config_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'配置key', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'config_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'accessKey', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'秘钥', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'secret_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'bucket_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'前缀', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'prefix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'访问站点', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'endpoint' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'自定义域名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'domain' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'是否https(Y=是,N=否)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'is_https' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'域', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'region' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_policy' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'是否默认(0=是,1=否)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'status' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'ext1' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'备注', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'remark' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储配置表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config' +GO + +INSERT INTO sys_oss_config VALUES (N'1', N'000000', N'minio', N'ruoyi', N'ruoyi123', N'ruoyi', N'', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'0', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'2', N'000000', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'3', N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'4', N'000000', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'5', N'000000', N'image', N'ruoyi', N'ruoyi123', N'ruoyi', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO diff --git a/script/sql/sqlserver/sqlserver_test.sql b/script/sql/sqlserver/sqlserver_test.sql new file mode 100644 index 00000000..87628bd6 --- /dev/null +++ b/script/sql/sqlserver/sqlserver_test.sql @@ -0,0 +1,510 @@ +CREATE TABLE test_demo +( + id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dept_id bigint NULL, + user_id bigint NULL, + order_num int DEFAULT ((0)) NULL, + test_key nvarchar(255) NULL, + value nvarchar(255) NULL, + version int DEFAULT ((0)) NULL, + create_dept bigint NULL, + create_time datetime2(0) NULL, + create_by bigint NULL, + update_time datetime2(0) NULL, + update_by bigint NULL, + del_flag int DEFAULT ((0)) NULL, + CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'租户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'排序号', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'order_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'key键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'test_key' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'value' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'version' +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试单表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo' +GO + +CREATE TABLE test_tree +( + id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + parent_id bigint DEFAULT ((0)) NULL, + dept_id bigint NULL, + user_id bigint NULL, + tree_name nvarchar(255) NULL, + version int DEFAULT ((0)) NULL, + create_dept bigint NULL, + create_time datetime2(0) NULL, + create_by bigint NULL, + update_time datetime2(0) NULL, + update_by bigint NULL, + del_flag int DEFAULT ((0)) NULL, + CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'租户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'tree_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'version' +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试树表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree' +GO + +INSERT sys_user VALUES (3, N'000000', 108, N'test', N'本部门及以下 密码666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 3, getdate(), NULL); +GO +INSERT sys_user VALUES (4, N'000000', 102, N'test1', N'仅本人 密码666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 4, getdate(), NULL); +GO + +INSERT sys_menu VALUES (5, N'测试菜单', 0, 5, N'demo', NULL, 1, 0, N'M', N'0', N'0', NULL, N'star', 103, 1, getdate(), NULL, NULL, N''); +GO + +INSERT sys_menu VALUES (1500, N'测试单表', 5, 1, N'demo', N'demo/demo/index', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', 103, 1, getdate(), NULL, NULL, N'测试单表菜单'); +GO +INSERT sys_menu VALUES (1501, N'测试单表查询', 1500, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1502, N'测试单表新增', 1500, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1503, N'测试单表修改', 1500, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1504, N'测试单表删除', 1500, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1505, N'测试单表导出', 1500, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + +INSERT sys_menu VALUES (1506, N'测试树表', 5, 1, N'tree', N'demo/tree/index', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', 103, 1, getdate(), NULL, NULL, N'测试树表菜单'); +GO +INSERT sys_menu VALUES (1507, N'测试树表查询', 1506, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1508, N'测试树表新增', 1506, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1509, N'测试树表修改', 1506, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1510, N'测试树表删除', 1506, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1511, N'测试树表导出', 1506, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + +INSERT sys_role VALUES (3, N'000000', N'本部门及以下', N'test1', 3, N'4', 1, 1, N'0', N'0', 103, 1, getdate(), 1, NULL, NULL); +GO +INSERT sys_role VALUES (4, N'000000', N'仅本人', N'test2', 4, N'5', 1, 1, N'0', N'0', 103, 1, getdate(), 1, NULL, NULL); +GO + +INSERT sys_role_menu VALUES (3, 1); +GO +INSERT sys_role_menu VALUES (3, 5); +GO +INSERT sys_role_menu VALUES (3, 100); +GO +INSERT sys_role_menu VALUES (3, 101); +GO +INSERT sys_role_menu VALUES (3, 102); +GO +INSERT sys_role_menu VALUES (3, 103); +GO +INSERT sys_role_menu VALUES (3, 104); +GO +INSERT sys_role_menu VALUES (3, 105); +GO +INSERT sys_role_menu VALUES (3, 106); +GO +INSERT sys_role_menu VALUES (3, 107); +GO +INSERT sys_role_menu VALUES (3, 108); +GO +INSERT sys_role_menu VALUES (3, 500); +GO +INSERT sys_role_menu VALUES (3, 501); +GO +INSERT sys_role_menu VALUES (3, 1001); +GO +INSERT sys_role_menu VALUES (3, 1002); +GO +INSERT sys_role_menu VALUES (3, 1003); +GO +INSERT sys_role_menu VALUES (3, 1004); +GO +INSERT sys_role_menu VALUES (3, 1005); +GO +INSERT sys_role_menu VALUES (3, 1006); +GO +INSERT sys_role_menu VALUES (3, 1007); +GO +INSERT sys_role_menu VALUES (3, 1008); +GO +INSERT sys_role_menu VALUES (3, 1009); +GO +INSERT sys_role_menu VALUES (3, 1010); +GO +INSERT sys_role_menu VALUES (3, 1011); +GO +INSERT sys_role_menu VALUES (3, 1012); +GO +INSERT sys_role_menu VALUES (3, 1013); +GO +INSERT sys_role_menu VALUES (3, 1014); +GO +INSERT sys_role_menu VALUES (3, 1015); +GO +INSERT sys_role_menu VALUES (3, 1016); +GO +INSERT sys_role_menu VALUES (3, 1017); +GO +INSERT sys_role_menu VALUES (3, 1018); +GO +INSERT sys_role_menu VALUES (3, 1019); +GO +INSERT sys_role_menu VALUES (3, 1020); +GO +INSERT sys_role_menu VALUES (3, 1021); +GO +INSERT sys_role_menu VALUES (3, 1022); +GO +INSERT sys_role_menu VALUES (3, 1023); +GO +INSERT sys_role_menu VALUES (3, 1024); +GO +INSERT sys_role_menu VALUES (3, 1025); +GO +INSERT sys_role_menu VALUES (3, 1026); +GO +INSERT sys_role_menu VALUES (3, 1027); +GO +INSERT sys_role_menu VALUES (3, 1028); +GO +INSERT sys_role_menu VALUES (3, 1029); +GO +INSERT sys_role_menu VALUES (3, 1030); +GO +INSERT sys_role_menu VALUES (3, 1031); +GO +INSERT sys_role_menu VALUES (3, 1032); +GO +INSERT sys_role_menu VALUES (3, 1033); +GO +INSERT sys_role_menu VALUES (3, 1034); +GO +INSERT sys_role_menu VALUES (3, 1035); +GO +INSERT sys_role_menu VALUES (3, 1036); +GO +INSERT sys_role_menu VALUES (3, 1037); +GO +INSERT sys_role_menu VALUES (3, 1038); +GO +INSERT sys_role_menu VALUES (3, 1039); +GO +INSERT sys_role_menu VALUES (3, 1040); +GO +INSERT sys_role_menu VALUES (3, 1041); +GO +INSERT sys_role_menu VALUES (3, 1042); +GO +INSERT sys_role_menu VALUES (3, 1043); +GO +INSERT sys_role_menu VALUES (3, 1044); +GO +INSERT sys_role_menu VALUES (3, 1045); +GO +INSERT sys_role_menu VALUES (3, 1500); +GO +INSERT sys_role_menu VALUES (3, 1501); +GO +INSERT sys_role_menu VALUES (3, 1502); +GO +INSERT sys_role_menu VALUES (3, 1503); +GO +INSERT sys_role_menu VALUES (3, 1504); +GO +INSERT sys_role_menu VALUES (3, 1505); +GO +INSERT sys_role_menu VALUES (3, 1506); +GO +INSERT sys_role_menu VALUES (3, 1507); +GO +INSERT sys_role_menu VALUES (3, 1508); +GO +INSERT sys_role_menu VALUES (3, 1509); +GO +INSERT sys_role_menu VALUES (3, 1510); +GO +INSERT sys_role_menu VALUES (3, 1511); +GO +INSERT sys_role_menu VALUES (4, 5); +GO +INSERT sys_role_menu VALUES (4, 1500); +GO +INSERT sys_role_menu VALUES (4, 1501); +GO +INSERT sys_role_menu VALUES (4, 1502); +GO +INSERT sys_role_menu VALUES (4, 1503); +GO +INSERT sys_role_menu VALUES (4, 1504); +GO +INSERT sys_role_menu VALUES (4, 1505); +GO +INSERT sys_role_menu VALUES (4, 1506); +GO +INSERT sys_role_menu VALUES (4, 1507); +GO +INSERT sys_role_menu VALUES (4, 1508); +GO +INSERT sys_role_menu VALUES (4, 1509); +GO +INSERT sys_role_menu VALUES (4, 1510); +GO +INSERT sys_role_menu VALUES (4, 1511); +GO + +INSERT sys_user_role VALUES (3, 3); +GO +INSERT sys_user_role VALUES (4, 4); +GO + +INSERT test_demo VALUES (1, N'000000', 102, 4, 1, N'测试数据权限', N'测试', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (2, N'000000', 102, 3, 2, N'子节点1', N'111', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (3, N'000000', 102, 3, 3, N'子节点2', N'222', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (4, N'000000', 108, 4, 4, N'测试数据', N'demo', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (5, N'000000', 108, 3, 13, N'子节点11', N'1111', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (6, N'000000', 108, 3, 12, N'子节点22', N'2222', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (7, N'000000', 108, 3, 11, N'子节点33', N'3333', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (8, N'000000', 108, 3, 10, N'子节点44', N'4444', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (9, N'000000', 108, 3, 9, N'子节点55', N'5555', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (10, N'000000', 108, 3, 8, N'子节点66', N'6666', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (11, N'000000', 108, 3, 7, N'子节点77', N'7777', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (12, N'000000', 108, 3, 6, N'子节点88', N'8888', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (13, N'000000', 108, 3, 5, N'子节点99', N'9999', 0, 103, getdate(), 1, NULL, NULL, 0); +GO + +INSERT test_tree VALUES (1, N'000000', 0, 102, 4, N'测试数据权限', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (2, N'000000', 1, 102, 3, N'子节点1', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (3, N'000000', 2, 102, 3, N'子节点2', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (4, N'000000', 0, 108, 4, N'测试树1', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (5, N'000000', 4, 108, 3, N'子节点11', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (6, N'000000', 4, 108, 3, N'子节点22', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (7, N'000000', 4, 108, 3, N'子节点33', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (8, N'000000', 5, 108, 3, N'子节点44', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (9, N'000000', 6, 108, 3, N'子节点55', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (10, N'000000', 7, 108, 3, N'子节点66', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (11, N'000000', 7, 108, 3, N'子节点77', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (12, N'000000', 10, 108, 3, N'子节点88', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (13, N'000000', 10, 108, 3, N'子节点99', 0, 103, getdate(), 1, NULL, NULL, 0); +GO